) {
+ // Called when active participants change
+ }
-## Participant View Modes
+ override fun onParticipantPinned(participant: RtkRemoteParticipant) {
+ // Called when a participant is pinned
+ }
-The view mode indicates whether participants are populated in `ACTIVE_GRID` mode or `PAGINATED` mode.
+ override fun onParticipantUnpinned(participant: RtkRemoteParticipant) {
+ // Called when a participant is unpinned
+ }
-- **`ACTIVE_GRID` mode** - Participants are automatically replaced in `meeting.participants.active` based on who is speaking or who has their video turned on
-- **`PAGINATED` mode** - Participants in `meeting.participants.active` are fixed. Use `setPage()` to change the active participants
+ // ... other methods
+})
+```
-
-
+
-### Set View Mode
+
-```js
-// Set the view mode to paginated
-await meeting.participants.setViewMode("PAGINATED");
+Use event listeners to monitor participant changes:
-// Set the view mode to active grid
-await meeting.participants.setViewMode("ACTIVE_GRID");
-```
+```swift
+extension MeetingViewModel: RtkParticipantsEventListener {
+ func onParticipantJoin(participant: RtkRemoteParticipant) {
+ // Called when a participant joins the meeting
+ }
-### Set Page in Paginated Mode
+ func onParticipantLeave(participant: RtkRemoteParticipant) {
+ // Called when a participant leaves the meeting
+ }
-```js
-// Switch to second page
-await meeting.participants.setPage(2);
-```
+ func onActiveParticipantsChanged(active: [RtkRemoteParticipant]) {
+ // Called when active participants change
+ }
-
-
+ func onParticipantPinned(participant: RtkRemoteParticipant) {
+ // Called when a participant is pinned
+ }
-### Set View Mode
+ func onParticipantUnpinned(participant: RtkRemoteParticipant) {
+ // Called when a participant is unpinned
+ }
-```jsx
-// Set the view mode to paginated
-await meeting.participants.setViewMode("PAGINATED");
+ // ... other methods
+}
-// Set the view mode to active grid
-await meeting.participants.setViewMode("ACTIVE_GRID");
+meeting.addParticipantsEventListener(self)
```
-### Set Page in Paginated Mode
-
-```jsx
-// Switch to second page
-await meeting.participants.setPage(2);
-```
+
-### Monitor View Mode
+
-```jsx
-const viewMode = useRealtimeKitSelector((m) => m.participants.viewMode);
-const currentPage = useRealtimeKitSelector((m) => m.participants.currentPage);
-```
+Use event listeners to monitor participant changes:
-
-
+```dart
+class ParticipantsNotifier extends RtkParticipantsEventListener {
+ @override
+ void onParticipantJoin(RtkRemoteParticipant participant) {
+ // Called when a participant joins the meeting
+ }
-## Waiting Room Methods
+ @override
+ void onParticipantLeave(RtkRemoteParticipant participant) {
+ // Called when a participant leaves the meeting
+ }
-
-
+ @override
+ void onActiveParticipantsChanged(List active) {
+ // Called when active participants change
+ }
-### Accept Waiting Room Request
+ @override
+ void onParticipantPinned(RtkRemoteParticipant participant) {
+ // Called when a participant is pinned
+ }
-```js
-await meeting.participants.acceptWaitingRoomRequest(participantId);
-```
+ @override
+ void onParticipantUnpinned(RtkRemoteParticipant participant) {
+ // Called when a participant is unpinned
+ }
-### Reject Waiting Room Request
+ // ... other methods
+}
-```js
-await meeting.participants.rejectWaitingRoomRequest(participantId);
+meeting.addParticipantsEventListener(ParticipantsNotifier());
```
-
-
+
-### Accept Waiting Room Request
+
-```jsx
-await meeting.participants.acceptWaitingRoomRequest(participantId);
-```
+You can also use event listeners for specific actions:
-### Reject Waiting Room Request
+```tsx
+import { useRealtimeKitClient } from "@cloudflare/realtimekit-react-native";
+import { useEffect } from "react";
-```jsx
-await meeting.participants.rejectWaitingRoomRequest(participantId);
-```
+function ParticipantListener() {
+ const [meeting] = useRealtimeKitClient();
-
-
+ useEffect(() => {
+ if (!meeting) return;
-## Participant Object
+ const handleParticipantPinned = (participant) => {
+ console.log(`Participant ${participant.name} got pinned`);
+ };
-The participant object contains all information related to a particular participant, including their video/audio/screenshare streams, name, and state variables.
+ meeting.participants.pinned.on(
+ "participantJoined",
+ handleParticipantPinned,
+ );
-### Participant Properties
+ return () => {
+ meeting.participants.pinned.off(
+ "participantJoined",
+ handleParticipantPinned,
+ );
+ };
+ }, [meeting]);
+}
+```
-**Media Properties:**
+
-- `videoEnabled` - Set to `true` if the participant's camera is on
-- `audioEnabled` - Set to `true` if the participant is unmuted
-- `screenShareEnabled` - Set to `true` if the participant is sharing their screen
-- `videoTrack` - The video track of the participant
-- `audioTrack` - The audio track of the participant
-- `screenShareTracks` - The video and audio tracks of the participant's screen share
+### Participant Map Properties
+
+
+
+```js
+// Number of participants joined in the meeting
+console.log(meeting.participants.count);
+
+// Number of pages available in paginated mode
+console.log(meeting.participants.pageCount);
+
+// Maximum number of participants in active state
+console.log(meeting.participants.maxActiveParticipantsCount);
+
+// ParticipantId of the last participant who spoke
+console.log(meeting.participants.lastActiveSpeaker);
+```
+
+
+
+
+
+```jsx
+// Number of participants joined in the meeting
+const participantCount = useRealtimeKitSelector((m) => m.participants.count);
+
+// Number of pages available in paginated mode
+const pageCount = useRealtimeKitSelector((m) => m.participants.pageCount);
+
+// Maximum number of participants in active state
+const maxActiveCount = useRealtimeKitSelector(
+ (m) => m.participants.maxActiveParticipantsCount,
+);
+
+// ParticipantId of the last participant who spoke
+const lastActiveSpeaker = useRealtimeKitSelector(
+ (m) => m.participants.lastActiveSpeaker,
+);
+```
+
+
+
+
+
+```kotlin
+// Number of participants joined in the meeting
+val participantCount = meeting.participants.joined.size
+
+// Access pagination properties
+val maxNumberOnScreen = meeting.participants.maxNumberOnScreen
+val currentPageNumber = meeting.participants.currentPageNumber
+val pageCount = meeting.participants.pageCount
+val canGoNextPage = meeting.participants.canGoNextPage
+val canGoPreviousPage = meeting.participants.canGoPreviousPage
+```
+
+
+
+
+
+```swift
+// Number of participants joined in the meeting
+let participantCount = meeting.participants.joined.count
+
+// Access pagination properties
+let maxNumberOnScreen = meeting.participants.maxNumberOnScreen
+let currentPageNumber = meeting.participants.currentPageNumber
+let pageCount = meeting.participants.pageCount
+let canGoNextPage = meeting.participants.canGoNextPage
+let canGoPreviousPage = meeting.participants.canGoPreviousPage
+```
+
+
+
+
+
+```dart
+// Number of participants joined in the meeting
+final participantCount = meeting.participants.joined.length;
+
+// Access pagination properties
+final currentPageNumber = meeting.participants.currentPageNumber;
+final pageCount = meeting.participants.pageCount;
+final canGoNextPage = meeting.participants.isNextPagePossible;
+final canGoPreviousPage = meeting.participants.isPreviousPagePossible;
+```
+
+
+
+
+
+```tsx
+// Number of participants joined in the meeting
+const participantCount = useRealtimeKitSelector((m) => m.participants.count);
+
+// Number of pages available in paginated mode
+const pageCount = useRealtimeKitSelector((m) => m.participants.pageCount);
+
+// Maximum number of participants in active state
+const maxActiveCount = useRealtimeKitSelector(
+ (m) => m.participants.maxActiveParticipantsCount,
+);
+
+// ParticipantId of the last participant who spoke
+const lastActiveSpeaker = useRealtimeKitSelector(
+ (m) => m.participants.lastActiveSpeaker,
+);
+```
+
+
+
+## Participant View Modes
+
+The view mode indicates whether participants are populated in `ACTIVE_GRID` mode or `PAGINATED` mode.
+
+- **`ACTIVE_GRID` mode** - Participants are automatically replaced in `meeting.participants.active` based on who is speaking or who has their video turned on
+- **`PAGINATED` mode** - Participants in `meeting.participants.active` are fixed. Use `setPage()` to change the active participants
+
+### Set View Mode
+
+
+
+```js
+// Set the view mode to paginated
+await meeting.participants.setViewMode("PAGINATED");
+
+// Set the view mode to active grid
+await meeting.participants.setViewMode("ACTIVE_GRID");
+```
+
+
+
+
+
+```jsx
+// Set the view mode to paginated
+await meeting.participants.setViewMode("PAGINATED");
+
+// Set the view mode to active grid
+await meeting.participants.setViewMode("ACTIVE_GRID");
+```
+
+
+
+
+
+Android SDK uses paginated mode by default.
+
+
+
+
+
+iOS SDK uses paginated mode by default.
+
+
+
+
+
+Flutter SDK uses paginated mode by default.
+
+
+
+
+
+```tsx
+// Set the view mode to paginated
+await meeting.participants.setViewMode("PAGINATED");
+
+// Set the view mode to active grid
+await meeting.participants.setViewMode("ACTIVE_GRID");
+```
+
+
+
+### Set Page in Paginated Mode
+
+
+
+```js
+// Switch to second page
+await meeting.participants.setPage(2);
+```
+
+
+
+
+
+```jsx
+// Switch to second page
+await meeting.participants.setPage(2);
+```
+
+
+
+
+
+```kotlin
+// Switch to first page
+meeting.participants.setPage(1)
+```
+
+
+
+
+
+```swift
+// Switch to first page
+meeting.participants.setPage(1)
+```
+
+
+
+
+
+Flutter SDK automatically manages participant pagination.
+
+
+
+
+
+```tsx
+// Switch to second page
+await meeting.participants.setPage(2);
+```
+
+
+
+### Monitor View Mode
+
+
+
+```jsx
+const viewMode = useRealtimeKitSelector((m) => m.participants.viewMode);
+const currentPage = useRealtimeKitSelector((m) => m.participants.currentPage);
+```
+
+
+
+
+
+```tsx
+const viewMode = useRealtimeKitSelector((m) => m.participants.viewMode);
+const currentPage = useRealtimeKitSelector((m) => m.participants.currentPage);
+```
+
+
+
+## Waiting Room Methods
+
+Use the following methods to manage participants in the waiting room.
+
+### Accept Waiting Room Request
+
+
+
+```js
+await meeting.participants.acceptWaitingRoomRequest(participantId);
+```
+
+
+
+
+
+```jsx
+await meeting.participants.acceptWaitingRoomRequest(participantId);
+```
+
+
+
+
+
+```kotlin
+meeting.participants.acceptWaitingRoomRequest(participantId)
+```
+
+
+
+
+
+```swift
+meeting.participants.acceptWaitingRoomRequest(id: participantId)
+```
+
+
+
+
+
+```dart
+final participant = meeting.participants.waitlisted[0];
+meeting.participants.acceptWaitlistedParticipant(participant);
+```
+
+
+
+
+
+```tsx
+await meeting.participants.acceptWaitingRoomRequest(participantId);
+```
+
+
+
+### Reject Waiting Room Request
+
+
+
+```js
+await meeting.participants.rejectWaitingRoomRequest(participantId);
+```
+
+
+
+
+
+```jsx
+await meeting.participants.rejectWaitingRoomRequest(participantId);
+```
+
+
+
+
+
+```kotlin
+meeting.participants.rejectWaitingRoomRequest(participantId)
+```
+
+
+
+
+
+```swift
+meeting.participants.rejectWaitingRoomRequest(participantId)
+```
+
+
+
+
+
+```dart
+final participant = meeting.participants.waitlisted[0];
+meeting.participants.rejectWaitlistedParticipant(participant);
+```
+
+
+
+
+
+```tsx
+await meeting.participants.rejectWaitingRoomRequest(participantId);
+```
+
+
+
+## Participant Object
+
+The participant object contains all information related to a particular participant, including their video/audio/screenshare streams, name, and state variables.
+
+### Participant Properties
+
+
+
+**Media Properties:**
+
+- `videoEnabled` - Whether the participant's camera is currently enabled
+- `audioEnabled` - Whether the participant's microphone is currently unmuted
+- `screenShareEnabled` - Whether the participant is currently sharing their screen
+- `videoTrack` - Video track object of the participant's camera stream
+- `audioTrack` - Audio track object of the participant's microphone stream
+- `screenShareTracks` - Video and audio track objects of the participant's screen share stream
+
+**Metadata Properties:**
+
+- `id` - Session-specific identifier generated when the participant joins meeting session (also known as `peerId`)
+- `userId` - Permanent identifier of the participant generated when adding the participant to a meeting
+- `name` - Display name of the participant
+- `picture` - String URL to the participant's display picture (if any)
+- `customParticipantId` - Custom identifier that can be set while adding participant to a meeting by customer
+- `isPinned` - Whether this participant is currently pinned in the meeting
+- `presetName` - Name of the preset applied to this participant while adding to meeting
+
+
+
+
+
+**Media Properties:**
+
+- `videoEnabled` - Whether the participant's camera is currently enabled
+- `audioEnabled` - Whether the participant's microphone is currently unmuted
+- `screenShareEnabled` - Whether the participant is currently sharing their screen
+- `videoTrack` - Video track object of the participant's camera stream
+- `audioTrack` - Audio track object of the participant's microphone stream
+- `screenShareTracks` - Video and audio track objects of the participant's screen share stream
**Metadata Properties:**
-- `id` - The `participantId` of the participant (aka `peerId`)
-- `userId` - The `userId` of the participant
-- `name` - The participant's name
-- `picture` - The participant's picture (if any)
-- `customParticipantId` - An arbitrary ID that can be set to identify the participant
-- `isPinned` - Set to `true` if the participant is pinned
-- `presetName` - Name of the preset associated with the participant
+- `id` - Session-specific identifier generated when the participant joins meeting session (also known as `peerId`)
+- `userId` - Permanent identifier of the participant generated when adding the participant to a meeting
+- `name` - Display name of the participant
+- `picture` - String URL to the participant's display picture (if any)
+- `customParticipantId` - Custom identifier that can be set while adding participant to a meeting by customer
+- `isPinned` - Whether this participant is currently pinned in the meeting
+- `presetName` - Name of the preset applied to this participant while adding to meeting
+
+
+
+
+
+**Media Properties:**
+
+- `videoEnabled` - Whether the participant's camera is currently enabled
+- `audioEnabled` - Whether the participant's microphone is currently unmuted
+- `screenshareEnabled` - Whether the participant is currently sharing their screen
+
+**Metadata Properties:**
+
+- `id` - Session-specific identifier generated when the participant joins meeting session (also known as `peerId`)
+- `userId` - Permanent identifier of the participant generated when adding the participant to a meeting
+- `name` - Display name of the participant
+- `picture` - String URL to the participant's display picture (if any)
+- `customParticipantId` - Custom identifier that can be set while adding participant to a meeting by customer
+- `isHost` - Boolean value whether this participant has host privileges
+- `isPinned` - Whether this participant is currently pinned in the meeting
+- `presetName` - Name of the preset applied to this participant while adding to meeting
+- `stageStatus` - Indicates the participant's current stage status (applicable only in stage-enabled meetings)
+
+
+
+
+
+**Media Properties:**
+
+- `videoEnabled` - Whether the participant's camera is currently enabled
+- `audioEnabled` - Whether the participant's microphone is currently unmuted
+- `screenshareEnabled` - Whether the participant is currently sharing their screen
+
+**Metadata Properties:**
+
+- `id` - Session-specific identifier generated when the participant joins meeting session (also known as `peerId`)
+- `userId` - Permanent identifier of the participant generated when adding the participant to a meeting
+- `name` - Display name of the participant
+- `picture` - String URL to the participant's display picture (if any)
+- `customParticipantId` - Custom identifier that can be set while adding participant to a meeting by customer
+- `isHost` - Boolean value whether this participant has host privileges
+- `isPinned` - Whether this participant is currently pinned in the meeting
+- `presetName` - Name of the preset applied to this participant while adding to meeting
+- `stageStatus` - Indicates the participant's current stage status (applicable only in stage-enabled meetings)
+
+
+
+
+
+**Media Properties:**
+
+- `videoEnabled` - Whether the participant's camera is currently enabled
+- `audioEnabled` - Whether the participant's microphone is currently unmuted
+- `screenshareEnabled` - Whether the participant is currently sharing their screen
+
+**Metadata Properties:**
+
+- `id` - Session-specific identifier generated when the participant joins meeting session (also known as `peerId`)
+- `userId` - Permanent identifier of the participant generated when adding the participant to a meeting
+- `name` - Display name of the participant
+- `picture` - String URL to the participant's display picture (if any)
+- `isHost` - Boolean value whether this participant has host privileges
+- `customParticipantId` - Custom identifier that can be set while adding participant to a meeting by customer
+- `stageStatus` - Indicates the participant's current stage status (applicable only in stage-enabled meetings)
+- `isPinned` - Whether this participant is currently pinned in the meeting
+- `presetName` - Name of the preset applied to this participant while adding to meeting
+
+
+
+
+
+**Media Properties:**
+
+- `videoEnabled` - Whether the participant's camera is currently enabled
+- `audioEnabled` - Whether the participant's microphone is currently unmuted
+- `screenShareEnabled` - Whether the participant is currently sharing their screen
+- `videoTrack` - Video track object of the participant's camera stream
+- `audioTrack` - Audio track object of the participant's microphone stream
+- `screenShareTracks` - Video and audio track objects of the participant's screen share stream
+
+**Metadata Properties:**
+
+- `id` - Session-specific identifier generated when the participant joins meeting session (also known as `peerId`)
+- `userId` - Permanent identifier of the participant generated when adding the participant to a meeting
+- `name` - Display name of the participant
+- `picture` - String URL to the participant's display picture (if any)
+- `clientSpecificId` - Custom identifier that can be set while adding participant to a meeting by customer
+- `isPinned` - Whether this participant is currently pinned in the meeting
+- `presetName` - Name of the preset applied to this participant while adding to meeting
+
+
+
+### Access Participant Object
+
+
+
+```js
+const participant = meeting.participants.joined.get(participantId);
+
+// Access participant properties
+console.log(participant.name);
+console.log(participant.videoEnabled);
+console.log(participant.audioEnabled);
+```
+
+
+
+
+
+```jsx
+// Get a specific participant
+const participant = useRealtimeKitSelector((m) =>
+ m.participants.joined.get(participantId),
+);
+
+// Access participant properties
+const participantName = participant?.name;
+const isVideoEnabled = participant?.videoEnabled;
+const isAudioEnabled = participant?.audioEnabled;
+```
+
+
+
+
+
+```kotlin
+// Find a participant by peer ID
+val participant = meeting.participants.joined.firstOrNull { it.id == participantId }
+
+// Access participant properties
+participant?.let {
+ println("Participant: ${it.name}")
+ println("Video: ${it.videoEnabled}")
+ println("Audio: ${it.audioEnabled}")
+}
+```
+
+
+
+
+
+```swift
+// Find a participant by peer ID
+if let participant = meeting.participants.joined.first(where: { $0.id == participantId }) {
+ // Access participant properties
+ print("Participant: \(participant.name)")
+ print("Video: \(participant.videoEnabled)")
+ print("Audio: \(participant.audioEnabled)")
+}
+```
+
+
+
+
+
+```dart
+// Find a participant by peer ID
+final participant = meeting.participants.joined
+ .where((p) => p.id == "")
+ .firstOrNull;
+
+// Access participant properties
+if (participant != null) {
+ print('Participant: ${participant.name} (ID: ${participant.id})');
+ print('Audio: ${participant.audioEnabled ? "On" : "Off"}');
+ print('Video: ${participant.videoEnabled ? "On" : "Off"}');
+}
+```
+
+
+
+
+
+```tsx
+// Get a specific participant
+const participant = useRealtimeKitSelector((m) =>
+ m.participants.joined.get(participantId),
+);
+
+// Access participant properties
+const participantName = participant?.name;
+const isVideoEnabled = participant?.videoEnabled;
+const isAudioEnabled = participant?.audioEnabled;
+```
+
+
+
+### Listen to Participant Events
+
+
+
+Each participant object is an event emitter:
+
+```js
+meeting.participants.joined
+ .get(participantId)
+ .on("audioUpdate", ({ audioEnabled, audioTrack }) => {
+ console.log(
+ "The participant with id",
+ participantId,
+ "has toggled their mic to",
+ audioEnabled,
+ );
+ });
+```
+
+Alternatively, listen on the participant map for all participants:
+
+```js
+meeting.participants.joined.on(
+ "audioUpdate",
+ (participant, { audioEnabled, audioTrack }) => {
+ console.log(
+ "The participant with id",
+ participant.id,
+ "has toggled their mic to",
+ audioEnabled,
+ );
+ },
+);
+```
+
+
+
+
+
+```jsx
+import { useRealtimeKitClient } from "@cloudflare/realtimekit-react";
+import { useEffect } from "react";
+
+function ParticipantAudioListener({ participantId }) {
+ const [meeting] = useRealtimeKitClient();
+
+ useEffect(() => {
+ if (!meeting) return;
+
+ const handleAudioUpdate = ({ audioEnabled, audioTrack }) => {
+ console.log(
+ "The participant with id",
+ participantId,
+ "has toggled their mic to",
+ audioEnabled,
+ );
+ };
+
+ const participant = meeting.participants.joined.get(participantId);
+ participant.on("audioUpdate", handleAudioUpdate);
+
+ return () => {
+ participant.off("audioUpdate", handleAudioUpdate);
+ };
+ }, [meeting, participantId]);
+}
+```
+
+Or use the selector for specific properties:
+
+```jsx
+const audioEnabled = useRealtimeKitSelector(
+ (m) => m.participants.joined.get(participantId)?.audioEnabled,
+);
+```
+
+
+
+
+
+Implement the `RtkParticipantEventListener` interface to receive participant event updates:
+
+```kotlin
+meeting.addParticipantEventListener(object : RtkParticipantEventListener {
+ override fun onVideoUpdate(participant: RtkRemoteParticipant, isEnabled: Boolean) {
+ // Called when participant's video state changes
+ }
+
+ override fun onAudioUpdate(participant: RtkRemoteParticipant, isEnabled: Boolean) {
+ // Called when participant's audio state changes
+ }
+
+ override fun onScreenShareUpdate(participant: RtkRemoteParticipant, isEnabled: Boolean) {
+ // Called when participant's screen share state changes
+ }
+})
+```
+
+
+
+
+
+Implement the `RtkParticipantEventListener` protocol to receive participant event updates:
+
+```swift
+extension MeetingViewModel: RtkParticipantEventListener {
+ func onVideoUpdate(participant: RtkRemoteParticipant, isEnabled: Bool) {
+ // Called when participant's video state changes
+ }
+
+ func onAudioUpdate(participant: RtkRemoteParticipant, isEnabled: Bool) {
+ // Called when participant's audio state changes
+ }
+
+ func onScreenShareUpdate(participant: RtkRemoteParticipant, isEnabled: Bool) {
+ // Called when participant's screen share state changes
+ }
+}
+
+meeting.addParticipantEventListener(self)
+```
+
+
+
+
+
+Implement the `RtkParticipantUpdateListener` interface and add the listener on a participant:
+
+```dart
+class ParticipantUpdateHandler extends RtkParticipantUpdateListener {
+ @override
+ void onVideoUpdate(RtkRemoteParticipant participant, bool isEnabled) {
+ print("${participant.name}'s video is now ${isEnabled ? 'on' : 'off'}");
+ }
+
+ @override
+ void onAudioUpdate(RtkRemoteParticipant participant, bool isEnabled) {
+ print("${participant.name}'s audio is now ${isEnabled ? 'on' : 'off'}");
+ }
+
+ @override
+ void onPinned(RtkRemoteParticipant participant) {
+ print("${participant.name} was pinned");
+ }
+
+ @override
+ void onUnpinned(RtkRemoteParticipant participant) {
+ print("${participant.name} was unpinned");
+ }
+
+ @override
+ void onScreenShareUpdate(RtkRemoteParticipant participant, bool isEnabled) {
+ print("${participant.name}'s screen-share is now ${isEnabled ? 'on' : 'off'}");
+ }
+
+ @override
+ void onUpdate(RtkRemoteParticipant participant) {
+ print("${participant.name} was updated");
+ }
+}
+
+// Register the listener with a specific participant
+final listener = ParticipantUpdateHandler();
+participant.addParticipantUpdateListener(listener);
+
+// When done listening, remove the listener
+participant.removeParticipantUpdateListener(listener);
+```
+
+
+
+
+
+```tsx
+import { useRealtimeKitClient } from "@cloudflare/realtimekit-react-native";
+import { useEffect } from "react";
+
+function ParticipantAudioListener({ participantId }) {
+ const [meeting] = useRealtimeKitClient();
+
+ useEffect(() => {
+ if (!meeting) return;
+
+ const handleAudioUpdate = ({ audioEnabled, audioTrack }) => {
+ console.log(
+ "The participant with id",
+ participantId,
+ "has toggled their mic to",
+ audioEnabled,
+ );
+ };
+
+ const participant = meeting.participants.joined.get(participantId);
+ participant.on("audioUpdate", handleAudioUpdate);
+
+ return () => {
+ participant.off("audioUpdate", handleAudioUpdate);
+ };
+ }, [meeting, participantId]);
+}
+```
+
+Or use the selector for specific properties:
+
+```tsx
+const audioEnabled = useRealtimeKitSelector(
+ (m) => m.participants.joined.get(participantId)?.audioEnabled,
+);
+```
+
+
+
+### Host Control Methods
+
+If you have the relevant permissions, you can control participant media.
+
+
+
+```js
+const participant = meeting.participants.joined.get(participantId);
+
+// Disable a participant's video stream
+participant.disableVideo();
+
+// Disable a participant's audio stream
+participant.disableAudio();
+
+// Kick a participant from the meeting
+participant.kick();
+```
+
+
+
+
+
+```jsx
+const participant = meeting.participants.joined.get(participantId);
+
+// Disable a participant's video stream
+participant.disableVideo();
+
+// Disable a participant's audio stream
+participant.disableAudio();
+
+// Kick a participant from the meeting
+participant.kick();
+```
+
+
+
+
+
+```kotlin
+val participant = meeting.participants.joined.firstOrNull { it.id == participantId }
+
+participant?.let { pcpt ->
+ // Disable a participant's video stream
+ val videoError = pcpt.disableVideo()
+
+ // Disable a participant's audio stream
+ val audioError = pcpt.disableAudio()
+
+ // Kick a participant from the meeting
+ val kickError = pcpt.kick()
+}
+```
+
+
+
+
+
+```swift
+if let participant = meeting.participants.joined.first(where: { $0.id == participantId }) {
+ // Disable a participant's video stream
+ let videoError: HostError? = participant.disableVideo()
+
+ // Disable a participant's audio stream
+ let audioError: HostError? = participant.disableAudio()
+
+ // Kick a participant from the meeting
+ let kickError: HostError? = participant.kick()
+}
+```
+
+
+
+
+
+```dart
+// Disable a remote participant's video
+participant.disableVideo(onResult: (e) {
+ // handle error if any
+});
+
+// Disable a remote participant's audio
+participant.disableAudio(onResult: (e) {
+ // handle error if any
+});
+
+// Remove the participant from the meeting
+participant.kick();
+```
+
+**Required Permission**: `permissions.host.canDisableVideo`, `permissions.host.canDisableAudio` must be `true`
+
+
+
+
+
+```tsx
+const participant = meeting.participants.joined.get(participantId);
+
+// Disable a participant's video stream
+participant.disableVideo();
+
+// Disable a participant's audio stream
+participant.disableAudio();
+
+// Kick a participant from the meeting
+participant.kick();
+```
+
+
+
+### Pin and Unpin Participants
+
+
+
+```js
+const participant = meeting.participants.joined.get(participantId);
+
+// Pin a participant
+await participant.pin();
+
+// Unpin a participant
+await participant.unpin();
+```
+
+
+
+
+
+```jsx
+const participant = meeting.participants.joined.get(participantId);
+
+// Pin a participant
+await participant.pin();
+
+// Unpin a participant
+await participant.unpin();
+```
+
+
+
+
+
+```kotlin
+val participant = meeting.participants.joined.firstOrNull { it.id == participantId }
+
+participant?.let { pcpt ->
+ // Pin a participant
+ val pinError = pcpt.pin()
+
+ // Unpin a participant
+ val unpinError = pcpt.unpin()
+}
+```
+
+
+
+
+
+```swift
+if let participant = meeting.participants.joined.first(where: { $0.id == participantId }) {
+ // Pin a participant
+ let pinError: HostError? = participant.pin()
+
+ // Unpin a participant
+ let unpinError: HostError? = participant.unpin()
+}
+```
+
+
+
+
+
+```dart
+// Pin a remote participant
+participant.pin();
+
+// Unpin a previously pinned participant
+participant.unpin();
+```
+
+**Required Permission**: `permissions.host.canPinParticipant` must be `true`
+
+
+
+
+
+```tsx
+const participant = meeting.participants.joined.get(participantId);
+
+// Pin a participant
+await participant.pin();
+
+// Unpin a participant
+await participant.unpin();
+```
+
+
+
+## Display Participant Videos
+
+
+
+To play a participant's video track on a `
+
+
+
+Call `participant.getVideoView()` which returns a `View` that renders the participant's video stream:
+
+```kotlin
+// Get video view of a given participant
+val videoView = participant.getVideoView()
+
+// Get screen share video view
+val screenShareView = participant.getScreenShareVideoView()
+```
+
+
+
+
+
+Call `participant.getVideoView()` which returns a `UIView` that renders the participant's video stream:
+
+```swift
+// Get video view of a given participant
+let videoView = participant.getVideoView()
+
+// Get screen share video view
+let screenShareView = participant.getScreenShareVideoView()
+```
+
+
+
+
+
+Use the video view methods which return a `Widget` that you can place directly in your UI hierarchy:
+
+```dart
+// Create a widget to display the participant's camera video
+final cameraView = VideoView(meetingParticipant: participant);
+
+// Create a widget to display the participant's screen share
+final screenShareView = ScreenshareView(meetingParticipant: participant);
+```
+
+
+
+
+
+For custom video rendering, use the `RtkParticipantTile` component:
+
+```jsx
+import { RtkParticipantTile } from "@cloudflare/realtimekit-react-ui";
+import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react";
+
+function CustomVideoGrid() {
+ const participants = useRealtimeKitSelector((m) =>
+ m.participants.active.toArray(),
+ );
+
+ return (
+
+ {participants.map((participant) => (
+
+ ))}
+
+ );
+}
+```
+
+
+
+
+
+For custom video rendering, use the `RtkParticipantTile` component:
+
+```tsx
+import { RtkParticipantTile } from "@cloudflare/realtimekit-react-native-ui";
+import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react-native";
+import { View, StyleSheet } from "react-native";
+
+function CustomVideoGrid() {
+ const participants = useRealtimeKitSelector((m) =>
+ m.participants.active.toArray(),
+ );
+
+ return (
+
+ {participants.map((participant) => (
+
+ ))}
+
+ );
+}
+
+const styles = StyleSheet.create({
+ grid: {
+ flexDirection: "row",
+ flexWrap: "wrap",
+ },
+});
+```
+
+
+
+## Participant events
+
+### View Mode Change
+
+
+
+Triggered when the view mode changes between `ACTIVE_GRID` and `PAGINATED`.
+
+```js
+meeting.participants.on(
+ "viewModeChanged",
+ ({ viewMode, currentPage, pageCount }) => {
+ console.log("view mode changed", viewMode);
+ },
+);
+```
+
+
+
+
+
+Triggered when the view mode changes between `ACTIVE_GRID` and `PAGINATED`.
+
+```jsx
+const viewMode = useRealtimeKitSelector((m) => m.participants.viewMode);
+```
+
+Or use event listener:
+
+```jsx
+meeting.participants.on(
+ "viewModeChanged",
+ ({ viewMode, currentPage, pageCount }) => {
+ console.log("view mode changed", viewMode);
+ },
+);
+```
+
+
+
+
+
+This event is not available on Android.
+
+
+
+
+
+This event is not available on iOS.
+
+
+
+
+
+This event is not available on Flutter.
+
+
+
+
+
+Triggered when the view mode changes between `ACTIVE_GRID` and `PAGINATED`.
+
+```tsx
+const viewMode = useRealtimeKitSelector((m) => m.participants.viewMode);
+```
+
+Or use event listener:
+
+```tsx
+meeting.participants.on(
+ "viewModeChanged",
+ ({ viewMode, currentPage, pageCount }) => {
+ console.log("view mode changed", viewMode);
+ },
+);
+```
+
+
+
+### Page Change
+
+
+
+Triggered when the page changes in paginated mode.
+
+```js
+meeting.participants.on(
+ "pageChanged",
+ ({ viewMode, currentPage, pageCount }) => {
+ console.log("page changed", currentPage);
+ },
+);
+```
+
+
+
+
+
+Triggered when the page changes in paginated mode.
+
+```jsx
+const currentPage = useRealtimeKitSelector((m) => m.participants.currentPage);
+const pageCount = useRealtimeKitSelector((m) => m.participants.pageCount);
+```
+
+
+
+
+
+This event is not available on Android.
+
+
+
+
+
+This event is not available on iOS.
+
+
+
+
+
+This event is not available on Flutter.
+
+
+
+
+
+Triggered when the page changes in paginated mode.
+
+```tsx
+const currentPage = useRealtimeKitSelector((m) => m.participants.currentPage);
+const pageCount = useRealtimeKitSelector((m) => m.participants.pageCount);
+```
+
+
+
+### Active Speaker
-
-
+Triggered when a participant starts speaking.
-### Access Participant Object
+
```js
-const participant = meeting.participants.joined.get(participantId);
-
-// Access participant properties
-console.log(participant.name);
-console.log(participant.videoEnabled);
-console.log(participant.audioEnabled);
+meeting.participants.on("activeSpeaker", (participant) => {
+ console.log(`${participant.id} is currently speaking`);
+});
```
-### Listen to Participant Events
+
-Each participant object is an event emitter:
+
-```js
-meeting.participants.joined
- .get(participantId)
- .on("audioUpdate", ({ audioEnabled, audioTrack }) => {
- console.log(
- "The participant with id",
- participantId,
- "has toggled their mic to",
- audioEnabled,
- );
- });
+```jsx
+const activeSpeaker = useRealtimeKitSelector(
+ (m) => m.participants.lastActiveSpeaker,
+);
```
-Alternatively, listen on the participant map for all participants:
+Or use event listener:
-```js
-meeting.participants.joined.on(
- "audioUpdate",
- (participant, { audioEnabled, audioTrack }) => {
- console.log(
- "The participant with id",
- participant.id,
- "has toggled their mic to",
- audioEnabled,
- );
- },
-);
+```jsx
+meeting.participants.on("activeSpeaker", (participant) => {
+ console.log(`${participant.id} is currently speaking`);
+});
```
-### Host Control Methods
+
-If you have the relevant permissions, you can control participant media:
+
-```js
-const participant = meeting.participants.joined.get(participantId);
+```kotlin
+meeting.addParticipantsEventListener(object : RtkParticipantsEventListener {
+ override fun onActiveSpeakerChanged(participant: RtkRemoteParticipant?) {
+ participant?.let {
+ println("${it.id} is currently speaking")
+ }
+ }
+})
+```
-// Disable a participant's video stream
-participant.disableVideo();
+
-// Disable a participant's audio stream
-participant.disableAudio();
+
-// Kick a participant from the meeting
-participant.kick();
+```swift
+extension MeetingViewModel: RtkParticipantsEventListener {
+ func onActiveSpeakerChanged(participant: RtkRemoteParticipant?) {
+ if let participant = participant {
+ print("\(participant.id) is currently speaking")
+ }
+ }
+}
+
+meeting.addParticipantsEventListener(self)
```
-### Pin and Unpin Participants
+
-```js
-const participant = meeting.participants.joined.get(participantId);
+
-// Pin a participant
-await participant.pin();
+```dart
+class ParticipantsNotifier extends RtkParticipantsEventListener {
+ @override
+ void onActiveSpeakerChanged(RtkRemoteParticipant? participant) {
+ if (participant != null) {
+ print('${participant.id} is currently speaking');
+ }
+ }
+}
-// Unpin a participant
-await participant.unpin();
+meeting.addParticipantsEventListener(ParticipantsNotifier());
```
-
-
+
-### Access Participant Object
+
-```jsx
-// Get a specific participant
-const participant = useRealtimeKitSelector((m) =>
- m.participants.joined.get(participantId),
+```tsx
+const activeSpeaker = useRealtimeKitSelector(
+ (m) => m.participants.lastActiveSpeaker,
);
-
-// Access participant properties
-const participantName = participant?.name;
-const isVideoEnabled = participant?.videoEnabled;
-const isAudioEnabled = participant?.audioEnabled;
```
-### Listen to Participant Events
+Or use event listener:
-```jsx
-import { useRealtimeKitClient } from "@cloudflare/realtimekit-react";
+```tsx
+meeting.participants.on("activeSpeaker", (participant) => {
+ console.log(`${participant.id} is currently speaking`);
+});
+```
-function ParticipantAudioListener({ participantId }) {
- const [meeting] = useRealtimeKitClient();
+
- useEffect(() => {
- if (!meeting) return;
+### Participant Joined
- const handleAudioUpdate = ({ audioEnabled, audioTrack }) => {
- console.log(
- "The participant with id",
- participantId,
- "has toggled their mic to",
- audioEnabled,
- );
- };
+Triggered when any participant joins the meeting.
- const participant = meeting.participants.joined.get(participantId);
- participant.on("audioUpdate", handleAudioUpdate);
+
- return () => {
- participant.off("audioUpdate", handleAudioUpdate);
- };
- }, [meeting, participantId]);
-}
+```js
+meeting.participants.joined.on("participantJoined", (participant) => {
+ console.log(`A participant with id "${participant.id}" has joined`);
+});
```
-Or use the selector for specific properties:
+
+
+
```jsx
-const audioEnabled = useRealtimeKitSelector(
- (m) => m.participants.joined.get(participantId)?.audioEnabled,
-);
+const joinedParticipants = useRealtimeKitSelector((m) => m.participants.joined);
```
-### Host Control Methods
-
-If you have the relevant permissions, you can control participant media:
+Or use event listener:
```jsx
-const participant = meeting.participants.joined.get(participantId);
+meeting.participants.joined.on("participantJoined", (participant) => {
+ console.log(`A participant with id "${participant.id}" has joined`);
+});
+```
-// Disable a participant's video stream
-participant.disableVideo();
+
-// Disable a participant's audio stream
-participant.disableAudio();
+
-// Kick a participant from the meeting
-participant.kick();
+```kotlin
+meeting.addParticipantsEventListener(object : RtkParticipantsEventListener {
+ override fun onParticipantJoin(participant: RtkRemoteParticipant) {
+ println("A participant with id ${participant.id} has joined")
+ }
+})
```
-### Pin and Unpin Participants
+
-```jsx
-const participant = meeting.participants.joined.get(participantId);
+
-// Pin a participant
-await participant.pin();
+```swift
+extension MeetingViewModel: RtkParticipantsEventListener {
+ func onParticipantJoin(participant: RtkRemoteParticipant) {
+ print("A participant with id \(participant.id) has joined")
+ }
+}
-// Unpin a participant
-await participant.unpin();
+meeting.addParticipantsEventListener(self)
```
-
-
+
-## Display Participant Videos
+
-
-
+```dart
+class ParticipantsNotifier extends RtkParticipantsEventListener {
+ @override
+ void onParticipantJoin(RtkRemoteParticipant participant) {
+ print('A participant with id ${participant.id} has joined');
+ }
+}
-### Register Video Element
+meeting.addParticipantsEventListener(ParticipantsNotifier());
+```
-To play a participant's video track on a `` element:
+
-```html
-
+
+
+```tsx
+const joinedParticipants = useRealtimeKitSelector((m) => m.participants.joined);
+```
+
+Or use event listener:
+
+```tsx
+meeting.participants.joined.on("participantJoined", (participant) => {
+ console.log(`A participant with id "${participant.id}" has joined`);
+});
```
+
+
+### Participant Left
+
+Triggered when any participant leaves the meeting.
+
+
+
```js
-// Get the video element
-const videoElement = document.getElementById("participant-video");
+meeting.participants.joined.on("participantLeft", (participant) => {
+ console.log(`A participant with id "${participant.id}" has left the meeting`);
+});
+```
-// Get the participant
-const participant = meeting.participants.joined.get(participantId);
+
-// Register the video element
-participant.registerVideoElement(videoElement);
+
+
+```jsx
+const joinedParticipants = useRealtimeKitSelector((m) => m.participants.joined);
```
-For local user preview (video not sent to other users):
+Or use event listener:
-```js
-meeting.self.registerVideoElement(videoElement, true);
+```jsx
+meeting.participants.joined.on("participantLeft", (participant) => {
+ console.log(`A participant with id "${participant.id}" has left the meeting`);
+});
```
-### Deregister Video Element
+
-Clean up when the video element is no longer needed:
+
-```js
-participant.deregisterVideoElement(videoElement);
+```kotlin
+meeting.addParticipantsEventListener(object : RtkParticipantsEventListener {
+ override fun onParticipantLeave(participant: RtkRemoteParticipant) {
+ println("A participant with id ${participant.id} has left the meeting")
+ }
+})
```
-
-
+
-### Using UI Kit Grid Components
+
-RealtimeKit provides ready-to-use grid components for displaying participant videos:
+```swift
+extension MeetingViewModel: RtkParticipantsEventListener {
+ func onParticipantLeave(participant: RtkRemoteParticipant) {
+ print("A participant with id \(participant.id) has left the meeting")
+ }
+}
-```jsx
-import { RtkGrid } from "@cloudflare/realtimekit-react-ui";
+meeting.addParticipantsEventListener(self)
+```
-function MeetingGrid() {
- return ;
+
+
+
+
+```dart
+class ParticipantsNotifier extends RtkParticipantsEventListener {
+ @override
+ void onParticipantLeave(RtkRemoteParticipant participant) {
+ print('A participant with id ${participant.id} has left the meeting');
+ }
}
+
+meeting.addParticipantsEventListener(ParticipantsNotifier());
```
-The `RtkGrid` component handles all grid-related events and data automatically. It supports:
+
-- **`RtkSimpleGrid`** - Renders participant tiles in a simple grid
-- **`RtkSpotlightGrid`** - Renders pinned participants in a spotlight area with others in a smaller grid
-- **`RtkMixedGrid`** - Renders screenshares and plugins in the main view with a configurable smaller grid
+
-### Manual Video Rendering
+```tsx
+const joinedParticipants = useRealtimeKitSelector((m) => m.participants.joined);
+```
-For custom video rendering, use the `RtkParticipantTile` component:
+Or use event listener:
+
+```tsx
+meeting.participants.joined.on("participantLeft", (participant) => {
+ console.log(`A participant with id "${participant.id}" has left the meeting`);
+});
+```
+
+
+
+### Participant Pinned
+
+Triggered when a participant is pinned.
+
+
+
+```js
+meeting.participants.joined.on("pinned", (participant) => {
+ console.log(`Participant with id "${participant.id}" was pinned`);
+});
+```
+
+
+
+
```jsx
-import { RtkParticipantTile } from "@cloudflare/realtimekit-react-ui";
-import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react";
+const pinnedParticipants = useRealtimeKitSelector((m) => m.participants.pinned);
+```
-function CustomVideoGrid() {
- const participants = useRealtimeKitSelector((m) =>
- m.participants.active.toArray(),
- );
+
- return (
-
- {participants.map((participant) => (
-
- ))}
-
- );
+
+
+```kotlin
+meeting.addParticipantsEventListener(object : RtkParticipantsEventListener {
+ override fun onParticipantPinned(participant: RtkRemoteParticipant) {
+ println("Participant with id ${participant.id} was pinned")
+ }
+})
+```
+
+
+
+
+
+```swift
+extension MeetingViewModel: RtkParticipantsEventListener {
+ func onParticipantPinned(participant: RtkRemoteParticipant) {
+ print("Participant with id \(participant.id) was pinned")
+ }
}
+
+meeting.addParticipantsEventListener(self)
```
-
-
+
+
+
+
+```dart
+class ParticipantsNotifier extends RtkParticipantsEventListener {
+ @override
+ void onParticipantPinned(RtkRemoteParticipant participant) {
+ print('Participant with id ${participant.id} was pinned');
+ }
+}
+
+meeting.addParticipantsEventListener(ParticipantsNotifier());
+```
-## Participant Events
+
-
-
+
-### View Mode Change
+```tsx
+const pinnedParticipants = useRealtimeKitSelector((m) => m.participants.pinned);
+```
-Triggered when the view mode changes:
+
-```js
-meeting.participants.on(
- "viewModeChanged",
- ({ viewMode, currentPage, pageCount }) => {
- console.log("view mode changed", viewMode);
- },
-);
-```
+### Participant Unpinned
-### Page Change
+Triggered when a participant is unpinned.
-Triggered when the page changes in paginated mode:
+
```js
-meeting.participants.on(
- "pageChanged",
- ({ viewMode, currentPage, pageCount }) => {
- console.log("page changed", currentPage);
- },
-);
+meeting.participants.joined.on("unpinned", (participant) => {
+ console.log(`Participant with id "${participant.id}" was unpinned`);
+});
```
-### Active Speaker
+
-Triggered when a participant starts speaking:
+
-```js
-meeting.participants.on("activeSpeaker", (participant) => {
- console.log(`${participant.id} is currently speaking`);
-});
-```
+This event is not available on React.
-### Participant Joined
+
-Triggered when any participant joins the meeting:
+
-```js
-meeting.participants.joined.on("participantJoined", (participant) => {
- console.log(`A participant with id "${participant.id}" has joined`);
-});
+```kotlin
+meeting.addParticipantsEventListener(object : RtkParticipantsEventListener {
+ override fun onParticipantUnpinned(participant: RtkRemoteParticipant) {
+ println("Participant with id ${participant.id} was unpinned")
+ }
+})
```
-### Participant Left
+
-Triggered when any participant leaves the meeting:
+
-```js
-meeting.participants.joined.on("participantLeft", (participant) => {
- console.log(`A participant with id "${participant.id}" has left the meeting`);
-});
+```swift
+extension MeetingViewModel: RtkParticipantsEventListener {
+ func onParticipantUnpinned(participant: RtkRemoteParticipant) {
+ print("Participant with id \(participant.id) was unpinned")
+ }
+}
+
+meeting.addParticipantsEventListener(self)
```
-### Participant Pinned
+
-Triggered when a participant is pinned:
+
-```js
-meeting.participants.joined.on("pinned", (participant) => {
- console.log(`Participant with id "${participant.id}" was pinned`);
-});
+```dart
+class ParticipantsNotifier extends RtkParticipantsEventListener {
+ @override
+ void onParticipantUnpinned(RtkRemoteParticipant participant) {
+ print('Participant with id ${participant.id} was unpinned');
+ }
+}
+
+meeting.addParticipantsEventListener(ParticipantsNotifier());
```
-### Participant Unpinned
+
-Triggered when a participant is unpinned:
+
-```js
-meeting.participants.joined.on("unpinned", (participant) => {
- console.log(`Participant with id "${participant.id}" was unpinned`);
-});
-```
+This event is not available on React Native.
+
+
### Video Update
-Triggered when any participant starts/stops video:
+Triggered when any participant starts/stops video.
+
+
```js
meeting.participants.joined.on("videoUpdate", (participant) => {
@@ -642,9 +2138,86 @@ meeting.participants.joined.on("videoUpdate", (participant) => {
});
```
+
+
+
+
+```jsx
+// Check for one participant
+const videoEnabled = useRealtimeKitSelector(
+ (m) => m.participants.joined.get(participantId)?.videoEnabled,
+);
+
+// All video enabled participants
+const videoEnabledParticipants = useRealtimeKitSelector((m) =>
+ m.participants.joined.toArray().filter((p) => p.videoEnabled),
+);
+```
+
+
+
+
+
+```kotlin
+meeting.addParticipantEventListener(object : RtkParticipantEventListener {
+ override fun onVideoUpdate(participant: RtkRemoteParticipant, isEnabled: Boolean) {
+ println("Participant ${participant.id} video is now ${if (isEnabled) "enabled" else "disabled"}")
+ }
+})
+```
+
+
+
+
+
+```swift
+extension MeetingViewModel: RtkParticipantEventListener {
+ func onVideoUpdate(participant: RtkRemoteParticipant, isEnabled: Bool) {
+ print("Participant \(participant.id) video is now \(isEnabled ? "enabled" : "disabled")")
+ }
+}
+
+meeting.addParticipantEventListener(self)
+```
+
+
+
+
+
+```dart
+class ParticipantUpdateHandler extends RtkParticipantUpdateListener {
+ @override
+ void onVideoUpdate(RtkRemoteParticipant participant, bool isEnabled) {
+ print('Participant ${participant.id} video is now ${isEnabled ? "enabled" : "disabled"}');
+ }
+}
+
+participant.addParticipantUpdateListener(ParticipantUpdateHandler());
+```
+
+
+
+
+
+```tsx
+// Check for one participant
+const videoEnabled = useRealtimeKitSelector(
+ (m) => m.participants.joined.get(participantId)?.videoEnabled,
+);
+
+// All video enabled participants
+const videoEnabledParticipants = useRealtimeKitSelector((m) =>
+ m.participants.joined.toArray().filter((p) => p.videoEnabled),
+);
+```
+
+
+
### Audio Update
-Triggered when any participant starts/stops audio:
+Triggered when any participant starts/stops audio.
+
+
```js
meeting.participants.joined.on("audioUpdate", (participant) => {
@@ -660,9 +2233,86 @@ meeting.participants.joined.on("audioUpdate", (participant) => {
});
```
+
+
+
+
+```jsx
+// Check for one participant
+const audioEnabled = useRealtimeKitSelector(
+ (m) => m.participants.joined.get(participantId)?.audioEnabled,
+);
+
+// All audio enabled participants
+const audioEnabledParticipants = useRealtimeKitSelector((m) =>
+ m.participants.joined.toArray().filter((p) => p.audioEnabled),
+);
+```
+
+
+
+
+
+```kotlin
+meeting.addParticipantEventListener(object : RtkParticipantEventListener {
+ override fun onAudioUpdate(participant: RtkRemoteParticipant, isEnabled: Boolean) {
+ println("Participant ${participant.id} audio is now ${if (isEnabled) "enabled" else "disabled"}")
+ }
+})
+```
+
+
+
+
+
+```swift
+extension MeetingViewModel: RtkParticipantEventListener {
+ func onAudioUpdate(participant: RtkRemoteParticipant, isEnabled: Bool) {
+ print("Participant \(participant.id) audio is now \(isEnabled ? "enabled" : "disabled")")
+ }
+}
+
+meeting.addParticipantEventListener(self)
+```
+
+
+
+
+
+```dart
+class ParticipantUpdateHandler extends RtkParticipantUpdateListener {
+ @override
+ void onAudioUpdate(RtkRemoteParticipant participant, bool isEnabled) {
+ print('Participant ${participant.id} audio is now ${isEnabled ? "enabled" : "disabled"}');
+ }
+}
+
+participant.addParticipantUpdateListener(ParticipantUpdateHandler());
+```
+
+
+
+
+
+```tsx
+// Check for one participant
+const audioEnabled = useRealtimeKitSelector(
+ (m) => m.participants.joined.get(participantId)?.audioEnabled,
+);
+
+// All audio enabled participants
+const audioEnabledParticipants = useRealtimeKitSelector((m) =>
+ m.participants.joined.toArray().filter((p) => p.audioEnabled),
+);
+```
+
+
+
### Screen Share Update
-Triggered when any participant starts/stops screen share:
+Triggered when any participant starts/stops screen share.
+
+
```js
meeting.participants.joined.on("screenShareUpdate", (participant) => {
@@ -678,9 +2328,86 @@ meeting.participants.joined.on("screenShareUpdate", (participant) => {
});
```
+
+
+
+
+```jsx
+// Check for one participant
+const screensharingParticipant = useRealtimeKitSelector((m) =>
+ m.participants.joined.toArray().find((p) => p.screenShareEnabled),
+);
+
+// All screen sharing participants
+const screenSharingParticipants = useRealtimeKitSelector((m) =>
+ m.participants.joined.toArray().filter((p) => p.screenShareEnabled),
+);
+```
+
+
+
+
+
+```kotlin
+meeting.addParticipantEventListener(object : RtkParticipantEventListener {
+ override fun onScreenShareUpdate(participant: RtkRemoteParticipant, isEnabled: Boolean) {
+ println("Participant ${participant.id} screen share is now ${if (isEnabled) "enabled" else "disabled"}")
+ }
+})
+```
+
+
+
+
+
+```swift
+extension MeetingViewModel: RtkParticipantEventListener {
+ func onScreenShareUpdate(participant: RtkRemoteParticipant, isEnabled: Bool) {
+ print("Participant \(participant.id) screen share is now \(isEnabled ? "enabled" : "disabled")")
+ }
+}
+
+meeting.addParticipantEventListener(self)
+```
+
+
+
+
+
+```dart
+class ParticipantUpdateHandler extends RtkParticipantUpdateListener {
+ @override
+ void onScreenShareUpdate(RtkRemoteParticipant participant, bool isEnabled) {
+ print('Participant ${participant.id} screen share is now ${isEnabled ? "enabled" : "disabled"}');
+ }
+}
+
+participant.addParticipantUpdateListener(ParticipantUpdateHandler());
+```
+
+
+
+
+
+```tsx
+// Check for one participant
+const screensharingParticipant = useRealtimeKitSelector((m) =>
+ m.participants.joined.toArray().find((p) => p.screenShareEnabled),
+);
+
+// All screen sharing participants
+const screenSharingParticipants = useRealtimeKitSelector((m) =>
+ m.participants.joined.toArray().filter((p) => p.screenShareEnabled),
+);
+```
+
+
+
### Network Quality Score
-Monitor participant network quality:
+
+
+Monitor participant network quality using the `mediaScoreUpdate` event.
```js
meeting.participants.joined.on(
@@ -707,128 +2434,73 @@ meeting.participants.joined.on(
);
```
-
-
-
-### View Mode Change
-
-```jsx
-const viewMode = useRealtimeKitSelector((m) => m.participants.viewMode);
-```
-
-Or use event listener:
-
-```jsx
-meeting.participants.on(
- "viewModeChanged",
- ({ viewMode, currentPage, pageCount }) => {
- console.log("view mode changed", viewMode);
- },
-);
-```
-
-### Page Change
-
-```jsx
-const currentPage = useRealtimeKitSelector((m) => m.participants.currentPage);
-const pageCount = useRealtimeKitSelector((m) => m.participants.pageCount);
-```
-
-### Active Speaker
-
-```jsx
-const activeSpeaker = useRealtimeKitSelector(
- (m) => m.participants.lastActiveSpeaker,
-);
-```
-
-Or use event listener:
-
-```jsx
-meeting.participants.on("activeSpeaker", (participant) => {
- console.log(`${participant.id} is currently speaking`);
-});
-```
-
-### Participant Joined
-
-```jsx
-const joinedParticipants = useRealtimeKitSelector((m) => m.participants.joined);
-```
-
-Or use event listener:
+
-```jsx
-meeting.participants.joined.on("participantJoined", (participant) => {
- console.log(`A participant with id "${participant.id}" has joined`);
-});
-```
+
-### Participant Left
+Monitor participant network quality using the `mediaScoreUpdate` event.
```jsx
-const joinedParticipants = useRealtimeKitSelector((m) => m.participants.joined);
-```
+import { useEffect } from "react";
-Or use event listener:
+// Use event listener for media score updates
+useEffect(() => {
+ if (!meeting) return;
-```jsx
-meeting.participants.joined.on("participantLeft", (participant) => {
- console.log(`A participant with id "${participant.id}" has left the meeting`);
-});
-```
+ const handleMediaScoreUpdate = ({
+ participantId,
+ kind,
+ isScreenshare,
+ score,
+ scoreStats,
+ }) => {
+ if (kind === "video") {
+ console.log(
+ `Participant ${participantId}'s ${isScreenshare ? "screenshare" : "video"} quality score is`,
+ score,
+ );
+ }
-### Participant Pinned/Unpinned
+ if (score < 5) {
+ console.log(`Participant ${participantId}'s media quality is poor`);
+ }
+ };
-```jsx
-const pinnedParticipants = useRealtimeKitSelector((m) => m.participants.pinned);
+ meeting.participants.joined.on("mediaScoreUpdate", handleMediaScoreUpdate);
+
+ return () => {
+ meeting.participants.joined.off("mediaScoreUpdate", handleMediaScoreUpdate);
+ };
+}, [meeting]);
```
-### Video Update
+
-```jsx
-// Check for one participant
-const videoEnabled = useRealtimeKitSelector(
- (m) => m.participants.joined.get(participantId)?.videoEnabled,
-);
+
-// All video enabled participants
-const videoEnabledParticipants = useRealtimeKitSelector((m) =>
- m.participants.joined.toArray().filter((p) => p.videoEnabled),
-);
-```
+This event is not available on Android.
-### Audio Update
+
-```jsx
-// Check for one participant
-const audioEnabled = useRealtimeKitSelector(
- (m) => m.participants.joined.get(participantId)?.audioEnabled,
-);
+
-// All audio enabled participants
-const audioEnabledParticipants = useRealtimeKitSelector((m) =>
- m.participants.joined.toArray().filter((p) => p.audioEnabled),
-);
-```
+This event is not available on iOS.
-### Screen Share Update
+
-```jsx
-// Check for one participant
-const screensharingParticipant = useRealtimeKitSelector((m) =>
- m.participants.joined.toArray().find((p) => p.screenShareEnabled),
-);
+
-// All screen sharing participants
-const screenSharingParticipants = useRealtimeKitSelector((m) =>
- m.participants.joined.toArray().filter((p) => p.screenShareEnabled),
-);
-```
+This event is not available on Flutter.
-### Network Quality Score
+
+
+
+
+Monitor participant network quality using the `mediaScoreUpdate` event.
+
+```tsx
+import { useEffect } from "react";
-```jsx
// Use event listener for media score updates
useEffect(() => {
if (!meeting) return;
@@ -860,8 +2532,7 @@ useEffect(() => {
}, [meeting]);
```
-
-
+
## Picture-in-Picture
@@ -871,60 +2542,150 @@ Picture-in-Picture API allows you to render `meeting.participants.active` partic
Supported in Chrome/Edge/Chromium-based browsers only.
:::
-
-
-
### Check if Supported
+
+
```js
const isSupported = meeting.participants.pip.isSupported();
```
+
+
+
+
+```jsx
+const isSupported = meeting.participants.pip.isSupported();
+```
+
+
+
+
+
+Picture-in-Picture is not available on Android.
+
+
+
+
+
+Picture-in-Picture is not available on iOS.
+
+
+
+
+
+Picture-in-Picture is not available on Flutter.
+
+
+
+
+
+```tsx
+const isSupported = meeting.participants.pip.isSupported();
+```
+
+
+
### Enable Picture-in-Picture
+
+
```js
await meeting.participants.pip.enable();
```
+
+
+
+
+```jsx
+await meeting.participants.pip.enable();
+```
+
+
+
+
+
+Picture-in-Picture is not available on Android.
+
+
+
+
+
+Picture-in-Picture is not available on iOS.
+
+
+
+
+
+Picture-in-Picture is not available on Flutter.
+
+
+
+
+
+```tsx
+await meeting.participants.pip.enable();
+```
+
+
+
### Disable Picture-in-Picture
+
+
```js
await meeting.participants.pip.disable();
```
-
-
+
-### Check if Supported
+
```jsx
-const isSupported = meeting.participants.pip.isSupported();
+await meeting.participants.pip.disable();
```
-### Enable Picture-in-Picture
+
-```jsx
-await meeting.participants.pip.enable();
-```
+
-### Disable Picture-in-Picture
+Picture-in-Picture is not available on Android.
-```jsx
+
+
+
+
+Picture-in-Picture is not available on iOS.
+
+
+
+
+
+Picture-in-Picture is not available on Flutter.
+
+
+
+
+
+```tsx
await meeting.participants.pip.disable();
```
-
-
+
## Update Participant Permissions
Permissions for a participant are defined by the preset, but can be updated during a meeting by calling `updatePermissions` for remote participants.
-
-
+:::note[Web Platforms Only]
+This feature is currently available for web-based platforms (Web Components, React, and React Native).
+:::
### Find Target Participants
+
+
```js
const participantIds = meeting.participants.joined
.toArray()
@@ -932,8 +2693,52 @@ const participantIds = meeting.participants.joined
.map((p) => p.id);
```
+
+
+
+
+```jsx
+const participantIds = meeting.participants.joined
+ .toArray()
+ .filter((e) => e.name.startsWith("John"))
+ .map((p) => p.id);
+```
+
+
+
+
+
+Updating participant permissions is not available on Android.
+
+
+
+
+
+Updating participant permissions is not available on iOS.
+
+
+
+
+
+Updating participant permissions is not available on Flutter.
+
+
+
+
+
+```tsx
+const participantIds = meeting.participants.joined
+ .toArray()
+ .filter((e) => e.name.startsWith("John"))
+ .map((p) => p.id);
+```
+
+
+
### Update Permissions
+
+
```js
// Allow file upload permissions in public chat
const newPermissions = {
@@ -947,8 +2752,64 @@ const newPermissions = {
meeting.participants.updatePermissions(participantIds, newPermissions);
```
+
+
+
+
+```jsx
+// Allow file upload permissions in public chat
+const newPermissions = {
+ chat: {
+ public: {
+ files: true,
+ },
+ },
+};
+
+meeting.participants.updatePermissions(participantIds, newPermissions);
+```
+
+
+
+
+
+Updating participant permissions is not available on Android.
+
+
+
+
+
+Updating participant permissions is not available on iOS.
+
+
+
+
+
+Updating participant permissions is not available on Flutter.
+
+
+
+
+
+```tsx
+// Allow file upload permissions in public chat
+const newPermissions = {
+ chat: {
+ public: {
+ files: true,
+ },
+ },
+};
+
+meeting.participants.updatePermissions(participantIds, newPermissions);
+```
+
+
+
### Available Permission Fields
+
+
```typescript
interface UpdatedPermissions {
polls?: {
@@ -974,34 +2835,56 @@ interface UpdatedPermissions {
}
```
-
-
+
-### Find Target Participants
+
-```jsx
-const participantIds = meeting.participants.joined
- .toArray()
- .filter((e) => e.name.startsWith("John"))
- .map((p) => p.id);
+```typescript
+interface UpdatedPermissions {
+ polls?: {
+ canCreate?: boolean;
+ canVote?: boolean;
+ };
+ plugins?: {
+ canClose?: boolean;
+ canStart?: boolean;
+ };
+ chat?: {
+ public?: {
+ canSend?: boolean;
+ text?: boolean;
+ files?: boolean;
+ };
+ private?: {
+ canSend?: boolean;
+ text?: boolean;
+ files?: boolean;
+ };
+ };
+}
```
-### Update Permissions
+
-```jsx
-// Allow file upload permissions in public chat
-const newPermissions = {
- chat: {
- public: {
- files: true,
- },
- },
-};
+
-meeting.participants.updatePermissions(participantIds, newPermissions);
-```
+Updating participant permissions is not available on Android.
-### Available Permission Fields
+
+
+
+
+Updating participant permissions is not available on iOS.
+
+
+
+
+
+Updating participant permissions is not available on Flutter.
+
+
+
+
```typescript
interface UpdatedPermissions {
@@ -1028,5 +2911,4 @@ interface UpdatedPermissions {
}
```
-
-
+