diff --git a/public/__redirects b/public/__redirects index af966e9f9c7bf28..0cad1df85fd2a82 100644 --- a/public/__redirects +++ b/public/__redirects @@ -338,7 +338,11 @@ /realtime/pricing/ /realtime/sfu/pricing/ 302 /realtime/changelog/ /realtime/sfu/changelog/ 302 /realtime/realtimekit/get-started/ /realtime/realtimekit/quickstart/ 302 +/realtime/realtimekit/core/remote-participant/ /realtime/realtimekit/core/remote-participants/ 302 /realtime/realtimekit/ui-kit/meeting-lifecycle/ realtime/realtimekit/ui-kit/state-management/ 302 +/realtime/realtimekit/core/video-background/ /realtime/realtimekit/core/video-effects/ 302 +/realtime/realtimekit/voice-meetings/ /realtime/realtimekit/audio-calls/ 302 +/realtime/realtimekit/core/manage-other-participants-in-a-session/ /realtime/realtimekit/core/manage-participants-in-a-session/ 302 /realtime/realtimekit/getting-started/ /realtime/realtimekit/quickstart/ 302 /realtime/introduction/ /realtime/realtimekit/introduction/ 302 /realtime/concepts/ /realtime/realtimekit/concepts/ 302 diff --git a/src/components/realtimekit/RTKSDKSelector/RTKSDKSelector.tsx b/src/components/realtimekit/RTKSDKSelector/RTKSDKSelector.tsx index 7000ad230c08d6d..454efedea09fa11 100644 --- a/src/components/realtimekit/RTKSDKSelector/RTKSDKSelector.tsx +++ b/src/components/realtimekit/RTKSDKSelector/RTKSDKSelector.tsx @@ -40,10 +40,12 @@ export default function SDKSelector({ disabledPlatforms }: SDKSelectorProps) { return ( <> -
- This page is not available for the {disabledPlatformsString} - platform. -
+ {disabledPlatforms && ( +
+ This page is not available for the {disabledPlatformsString} + platform. +
+ )}
{platforms.map((p) => { diff --git a/src/content/docs/realtime/realtimekit/core/ai.mdx b/src/content/docs/realtime/realtimekit/ai.mdx similarity index 99% rename from src/content/docs/realtime/realtimekit/core/ai.mdx rename to src/content/docs/realtime/realtimekit/ai.mdx index 98430a1b0898338..2a268f3f84d895f 100644 --- a/src/content/docs/realtime/realtimekit/core/ai.mdx +++ b/src/content/docs/realtime/realtimekit/ai.mdx @@ -3,7 +3,7 @@ pcx_content_type: how-to title: AI (Transcription and Summary) slug: realtime/realtimekit/core/ai sidebar: - order: 11 + order: 10 --- RealtimeKit provides AI-powered features to enhance your meetings, including real-time transcription and automatic meeting summaries. These features help you capture important discussions and generate concise overviews of your meetings. diff --git a/src/content/docs/realtime/realtimekit/voice-meetings.mdx b/src/content/docs/realtime/realtimekit/audio-calls.mdx similarity index 85% rename from src/content/docs/realtime/realtimekit/voice-meetings.mdx rename to src/content/docs/realtime/realtimekit/audio-calls.mdx index 4f20cd80816181a..50d2a1a36952846 100644 --- a/src/content/docs/realtime/realtimekit/voice-meetings.mdx +++ b/src/content/docs/realtime/realtimekit/audio-calls.mdx @@ -1,15 +1,14 @@ --- -title: Voice meetings +title: Audio Only Calls pcx_content_type: concept -slug: realtime/realtimekit/voice-meetings sidebar: order: 9 --- -RealtimeKit supports voice meetings, allowing you to build audio-only experiences such as audio rooms, support lines, or community hangouts. +RealtimeKit supports voice calls, allowing you to build audio-only experiences such as audio rooms, support lines, or community hangouts. In these meetings, participants use their microphones and hear others, but cannot use their camera. Voice meetings reduce bandwidth requirements and focus on audio communication. -## How voice meetings work +## How Audio Calls Work A participant’s meeting experience is determined by the **Preset** applied to that participant. To run a voice meeting, ensure all participants join with a Preset that has meeting type set to `Voice`. @@ -22,7 +21,7 @@ When a participant joins with a `Voice` meeting type Preset, they are considered For detailed pricing information, refer to [Pricing](/realtime/realtimekit/pricing/). -## Building voice experiences +## Building Audio Experiences You can build voice meeting experiences using either the UI Kit or the Core SDK. diff --git a/src/content/docs/realtime/realtimekit/core/breakout-rooms.mdx b/src/content/docs/realtime/realtimekit/core/breakout-rooms.mdx index b24e31a0e0e0e1e..c802e3c62d2f46d 100644 --- a/src/content/docs/realtime/realtimekit/core/breakout-rooms.mdx +++ b/src/content/docs/realtime/realtimekit/core/breakout-rooms.mdx @@ -10,6 +10,8 @@ import RTKSDKSelector from "~/components/realtimekit/RTKSDKSelector/RTKSDKSelect import RTKCodeSnippet from "~/components/realtimekit/RTKCodeSnippet/RTKCodeSnippet.astro"; import { Render } from "~/components"; + + ### Validate permissions @@ -39,8 +41,6 @@ Before creating breakout rooms, validate the permissions of the current particip ### Create breakout rooms - - ## Introduction -There are three types of messages that can be sent in chat: + -- **Text messages** -- **Images** -- **Files** +There are three types of messages that can be sent in chat: - - +- Text messages +- Images +- Files The meeting chat object is stored in `meeting.chat`, which has methods for sending and receiving messages. @@ -33,8 +34,122 @@ The `meeting.chat.messages` array contains all the messages that have been sent console.log("All chat messages:", meeting.chat.messages); ``` + + + + +There are three types of messages that can be sent in chat: + +- Text messages +- Images +- Files + +The meeting chat object is stored in `meeting.chat`, which has methods for sending and receiving messages. + +```jsx +meeting.chat; +``` + +The `meeting.chat.messages` array contains all the messages that have been sent in the chat. This is an array of objects, where each object is of type `Message`. + +```jsx +meeting.chat.messages; +``` + + + + + +There are three types of messages that can be sent in chat: + +- Text messages +- Images +- Files + +The meeting chat object is stored in `meeting.chat`, which has methods for sending and receiving messages. + +``` +meeting.chat +``` + +The `meeting.chat.messages` array contains all the messages that have been sent in the chat. This is an array of objects, where each object is of type `com.cloudflare.realtimekit.chat.ChatMessage`. + +``` +meeting.chat.messages +``` + + + + + +There are three types of messages that can be sent in chat: + +- Text messages +- Images +- Files + +The meeting chat object is stored in `meeting.chat`, which has methods for sending and receiving messages. + +``` +meeting.chat +``` + +The `meeting.chat.messages` array contains all the messages that have been sent in the chat. This is an array of objects, where each object is of type `RealtimeKit.ChatMessage`. + +``` +meeting.chat.messages +``` + + + + + +There are three types of messages that can be sent in chat: + +- Text messages +- Images +- Files + +The meeting chat object is stored in `meeting.chat`, which has methods for sending and receiving messages. + +``` +meeting.chat; +``` + +The `meeting.chat.messages` array contains all the messages that have been sent in the chat. This is an array of objects, where each object is of type `ChatMessage`. + +``` +meeting.chat.messages; +``` + + + + + +There are three types of messages that can be sent in chat: + +- Text messages +- Images +- Files + +The meeting chat object is stored in `meeting.chat`, which has methods for sending and receiving messages. + +```jsx +meeting.chat; +``` + +The `meeting.chat.messages` array contains all the messages that have been sent in the chat. This is an array of objects, where each object is of type `Message`. + +```jsx +meeting.chat.messages; +``` + + + ### Message Type + + The `Message` type is defined as follows: ```typescript @@ -68,22 +183,9 @@ interface FileMessage extends BaseMessage { type Message = TextMessage | ImageMessage | FileMessage; ``` - - - -The meeting chat object is stored in `meeting.chat`, which has methods for sending and receiving messages. - -```jsx -meeting.chat; -``` - -The `meeting.chat.messages` array contains all the messages that have been sent in the chat. This is an array of objects, where each object is of type `Message`. - -```jsx -meeting.chat.messages; -``` + -### Message Type + The `Message` type is defined as follows: @@ -118,22 +220,9 @@ interface FileMessage extends BaseMessage { type Message = TextMessage | ImageMessage | FileMessage; ``` - - - -The meeting chat object is stored in `meeting.chat`, which has methods for sending and receiving messages. - -``` -meeting.chat -``` - -The `meeting.chat.messages` array contains all the messages that have been sent in the chat. This is an array of objects, where each object is of type `com.cloudflare.realtimekit.chat.ChatMessage`. - -``` -meeting.chat.messages -``` + -### Message type + The `ChatMessage` class is defined as follows: @@ -166,22 +255,9 @@ class FileMessage( ): ChatMessage(...) ``` - - - -The meeting chat object is stored in `meeting.chat`, which has methods for sending and receiving messages. - -``` -meeting.chat -``` - -The `meeting.chat.messages` array contains all the messages that have been sent in the chat. This is an array of objects, where each object is of type `RealtimeKit.ChatMessage`. - -``` -meeting.chat.messages -``` + -### Message type + The `ChatMessage` class is defined as follows: @@ -218,22 +294,9 @@ public final class FileMessage: ChatMessage { } ``` - - - -The meeting chat object is stored in `meeting.chat`, which has methods for sending and receiving messages. - -``` -meeting.chat; -``` - -The `meeting.chat.messages` array contains all the messages that have been sent in the chat. This is an array of objects, where each object is of type `ChatMessage`. - -``` -meeting.chat.messages; -``` + -### Message type + The `ChatMessage` class is defined as follows: @@ -264,22 +327,9 @@ class FileMessage extends ChatMessage { } ``` - - - -The meeting chat object is stored in `meeting.chat`, which has methods for sending and receiving messages. - -```jsx -meeting.chat; -``` - -The `meeting.chat.messages` array contains all the messages that have been sent in the chat. This is an array of objects, where each object is of type `Message`. - -```jsx -meeting.chat.messages; -``` + -### Message type + The `Message` type is defined as follows: @@ -314,17 +364,15 @@ interface FileMessage extends BaseMessage { type Message = TextMessage | ImageMessage | FileMessage; ``` - - + ## Sending a Chat Message - - +### Send a Text Message -There is a method in `meeting.chat` to send a message of each type. + -### Send a Text Message +There is a method in `meeting.chat` to send a message of each type. To send a text message, use the `meeting.chat.sendTextMessage()` method. This accepts a string message and sends it to the room. @@ -333,68 +381,63 @@ const message = "Is this the real life?"; await meeting.chat.sendTextMessage(message); ``` -### Send an Image + -You can send an image with the help of `meeting.chat.sendImageMessage()`. This accepts an image of type `File`, and sends it to the participants in the meeting. + -```html - - - -``` +There is a method in `meeting.chat` to send a message of each type. -```js -async function onSendImage() { - const image = document.getElementById("img"); - await meeting.chat.sendImageMessage(image.files[0]); -} +To send a text message, use the `meeting.chat.sendTextMessage()` method. This accepts a string message and sends it to the room. + +```jsx +const message = "Is this the real life?"; +await meeting.chat.sendTextMessage(message); ``` -### Send a File + -Sending a file is similar to sending an image. The only difference is that when you send an image, a preview will be shown in the meeting chat, which is not the case for sending files. That being said, an image can be sent as a file too using `meeting.chat.sendFileMessage()`. + -```html - - - -``` +There is a method in `meeting.chat` to send a message of each type. -```js -async function onSendFile() { - const file = document.getElementById("file"); - await meeting.chat.sendFileMessage(file.files[0]); -} +To send a text message, use the `meeting.chat.sendTextMessage()` method. This accepts a string message and sends it to the room. + +```kotlin +val message = "Is this the real life?" +meeting.chat.sendTextMessage(message) ``` -### Send Any Message Type + -There is also a common method called `meeting.chat.sendMessage()` that can be used to send any of the three types of messages displayed above. It essentially calls one of the methods from above depending upon the type of payload you send to the method. The `sendMessage()` method accepts a parameter `message` of the following type: + -```typescript -async function sendMessage( - message: - | { type: "text"; message: string } - | { type: "image"; image: File } - | { type: "file"; file: File }, -) { - // ... -} -``` +There is a method in `meeting.chat` to send a message of each type. -Here is how you would use the `sendMessage()` method to send a text message: +To send a text message, use the `meeting.chat.sendTextMessage()` method. This accepts a string message and sends it to the room. -```js -const message = "Is this just fantasy?"; -await meeting.chat.sendMessage({ type: "text", message }); +```swift +var message = "Is this the real life?" +meeting.chat.sendTextMessage(message) ``` - - + + + There is a method in `meeting.chat` to send a message of each type. -### Send a Text Message +To send a text message, use the `meeting.chat.sendTextMessage()` method. This accepts a string message and sends it to the room. + +```dart +final message = "Is this the real life?"; +meeting.chat.sendTextMessage(message); +``` + + + + + +There is a method in `meeting.chat` to send a message of each type. To send a text message, use the `meeting.chat.sendTextMessage()` method. This accepts a string message and sends it to the room. @@ -403,8 +446,31 @@ const message = "Is this the real life?"; await meeting.chat.sendTextMessage(message); ``` + + ### Send an Image + + +You can send an image with the help of `meeting.chat.sendImageMessage()`. This accepts an image of type `File`, and sends it to the participants in the meeting. + +```html + + + +``` + +```js +async function onSendImage() { + const image = document.getElementById("img"); + await meeting.chat.sendImageMessage(image.files[0]); +} +``` + + + + + You can send an image with the help of `meeting.chat.sendImageMessage()`. This accepts an image of type `File`, and sends it to the participants in the meeting. ```jsx @@ -436,149 +502,144 @@ function ChatComponent() { } ``` -### Send a File + -Sending a file is similar to sending an image. The only difference is that when you send an image, a preview will be shown in the meeting chat, which is not the case for sending files. That being said, an image can be sent as a file too using `meeting.chat.sendFileMessage()`. + -```jsx -import { useRef } from "react"; +You can send an image with the help of `meeting.chat.sendImageMessage()` and sends it to the participants in the meeting. -function ChatComponent() { - const fileInputRef = useRef(null); +```kotlin +meeting.chat.sendImageMessage(imageUri) { err -> + // Handle error if any +} +``` - const onSendFile = async () => { - const file = fileInputRef.current; - if (file && file.files[0]) { - await meeting.chat.sendFileMessage(file.files[0]); - } - }; - - return ( - <> - - - - - ); -} -``` + -### Send Any Message Type + -There is also a common method called `meeting.chat.sendMessage()` that can be used to send any of the three types of messages displayed above. It essentially calls one of the methods from above depending upon the type of payload you send to the method. The `sendMessage()` method accepts a parameter `message` of the following type: +You can send an image with the help of `meeting.chat.sendImageMessage()` and sends it to the participants in the meeting. -```typescript -async function sendMessage( - message: - | { type: "text"; message: string } - | { type: "image"; image: File } - | { type: "file"; file: File }, -) { - // ... +```swift +meeting.chat.sendImageMessage(imageURL: url) { err in + // Handle error if any } ``` -Here is how you would use the `sendMessage()` method to send a text message: - -```jsx -const message = "Is this just fantasy?"; -await meeting.chat.sendMessage({ type: "text", message }); -``` + - - + -There is a method in `meeting.chat` to send a message of each type. +You can send an image with the help of `meeting.chat.sendImageMessage()` which sends it to the participants in the meeting. It takes a string filePath as argument. -### Send a text message +```dart +final filePath = "file_path_of_image"; +meeting.chat.sendImageMessage(filePath, (error) { + // Handle error if any +}); +``` -To send a text message, use the `meeting.chat.sendTextMessage()` method. This accepts a string message and sends it to the room. + -```kotlin -val message = "Is this the real life?" -meeting.chat.sendTextMessage(message) -``` + -### Send an image +You can send an image with the help of `meeting.chat.sendImageMessage()`. This accepts an image of type File, and sends it to the participants in the meeting. -You can send an image with the help of `meeting.chat.sendImageMessage()` and sends it to the participants in the meeting. +```jsx +import DocumentPicker from "@react-native-documents/picker"; -```kotlin -meeting.chat.sendImageMessage(imageUri) { err -> - // Handle error if any +async function onSendImage() { + // Get the image uri and create an object with the following fields + const res = await DocumentPicker.pickSingle({ + type: [DocumentPicker.types.images], + }); + const image = { + uri: res.uri, + name: res.name, + size: res.size, + type: res.type, + }; + await meeting.chat.sendImageMessage(image); } ``` -### Send a file + + +### Send a File + + Sending a file is similar to sending an image. The only difference is that when you send an image, a preview will be shown in the meeting chat, which is not the case for sending files. That being said, an image can be sent as a file too using `meeting.chat.sendFileMessage()`. -```kotlin -meeting.chat.sendFileMessage(fileUri) { err -> - // Handle error if any -} +```html + + + ``` - - +```js +async function onSendFile() { + const file = document.getElementById("file"); + await meeting.chat.sendFileMessage(file.files[0]); +} +``` -There is a method in `meeting.chat` to send a message of each type. + -### Send a text message + -To send a text message, use the `meeting.chat.sendTextMessage()` method. This accepts a string message and sends it to the room. +Sending a file is similar to sending an image. The only difference is that when you send an image, a preview will be shown in the meeting chat, which is not the case for sending files. That being said, an image can be sent as a file too using `meeting.chat.sendFileMessage()`. -```swift -var message = "Is this the real life?" -meeting.chat.sendTextMessage(message) -``` +```jsx +import { useRef } from "react"; -### Send an image +function ChatComponent() { + const fileInputRef = useRef(null); -You can send an image with the help of `meeting.chat.sendImageMessage()` and sends it to the participants in the meeting. + const onSendFile = async () => { + const file = fileInputRef.current; + if (file && file.files[0]) { + await meeting.chat.sendFileMessage(file.files[0]); + } + }; -```swift -meeting.chat.sendImageMessage(imageURL: url) { err in - // Handle error if any + return ( + <> + + + + + ); } ``` -### Send a file + + + Sending a file is similar to sending an image. The only difference is that when you send an image, a preview will be shown in the meeting chat, which is not the case for sending files. That being said, an image can be sent as a file too using `meeting.chat.sendFileMessage()`. -```swift -meeting.chat.sendFileMessage(fileURL: url) { err in +```kotlin +meeting.chat.sendFileMessage(fileUri) { err -> // Handle error if any } ``` - - - -There is a method in `meeting.chat` to send a message of each type. - -### Send a text message - -To send a text message, use the `meeting.chat.sendTextMessage()` method. This accepts a string message and sends it to the room. - -```dart -final message = "Is this the real life?"; -meeting.chat.sendTextMessage(message); -``` + -### Send an image + -You can send an image with the help of `meeting.chat.sendImageMessage()` which sends it to the participants in the meeting. It takes a string filePath as argument. +Sending a file is similar to sending an image. The only difference is that when you send an image, a preview will be shown in the meeting chat, which is not the case for sending files. That being said, an image can be sent as a file too using `meeting.chat.sendFileMessage()`. -```dart -final filePath = "file_path_of_image"; -meeting.chat.sendImageMessage(filePath, (error) { +```swift +meeting.chat.sendFileMessage(fileURL: url) { err in // Handle error if any -}); +} ``` -### Send a file + + + You can send a file with the help of `meeting.chat.sendFileMessage()` which sends it to the participants in the meeting. It takes a string filePath as argument. @@ -589,43 +650,9 @@ meeting.chat.sendFileMessage(filePath, (error) { }); ``` - - - -There is a method in `meeting.chat` to send a message of each type. - -### Send a text message - -To send a text message, use the `meeting.chat.sendTextMessage()` method. This accepts a string message and sends it to the room. - -```jsx -const message = "Is this the real life?"; -await meeting.chat.sendTextMessage(message); -``` - -### Send an image - -You can send an image with the help of `meeting.chat.sendImageMessage()`. This accepts an image of type File, and sends it to the participants in the meeting. - -```jsx -import DocumentPicker from "@react-native-documents/picker"; - -async function onSendImage() { - // Get the image uri and create an object with the following fields - const res = await DocumentPicker.pickSingle({ - type: [DocumentPicker.types.images], - }); - const image = { - uri: res.uri, - name: res.name, - size: res.size, - type: res.type, - }; - await meeting.chat.sendImageMessage(image); -} -``` + -### Send a file + Sending a file is similar to sending an image. The only difference is that when you send an image, a preview will be shown in the meeting chat, which is not the case for sending files. That being said, an image can be sent as a file too using `meeting.chat.sendFileMessage()`. @@ -647,7 +674,59 @@ async function onSendFile() { } ``` -### Send any message type + + +### Send Any Message Type + + + +There is also a common method called `meeting.chat.sendMessage()` that can be used to send any of the three types of messages displayed above. It essentially calls one of the methods from above depending upon the type of payload you send to the method. The `sendMessage()` method accepts a parameter `message` of the following type: + +```typescript +async function sendMessage( + message: + | { type: "text"; message: string } + | { type: "image"; image: File } + | { type: "file"; file: File }, +) { + // ... +} +``` + +Here is how you would use the `sendMessage()` method to send a text message: + +```js +const message = "Is this just fantasy?"; +await meeting.chat.sendMessage({ type: "text", message }); +``` + + + + + +There is also a common method called `meeting.chat.sendMessage()` that can be used to send any of the three types of messages displayed above. It essentially calls one of the methods from above depending upon the type of payload you send to the method. The `sendMessage()` method accepts a parameter `message` of the following type: + +```typescript +async function sendMessage( + message: + | { type: "text"; message: string } + | { type: "image"; image: File } + | { type: "file"; file: File }, +) { + // ... +} +``` + +Here is how you would use the `sendMessage()` method to send a text message: + +```jsx +const message = "Is this just fantasy?"; +await meeting.chat.sendMessage({ type: "text", message }); +``` + + + + There is also a common method called `meeting.chat.sendMessage()` that can be used to send any of the three types of messages displayed above. It essentially calls one of the methods from above depending upon the type of payload you send to the method. The `sendMessage()` method accepts a parameter `message` of the following type: @@ -669,13 +748,11 @@ const message = "Is this just fantasy?"; await meeting.chat.sendMessage({ type: "text", message }); ``` - - + ## Receiving Chat Messages - - + The `meeting.chat` object emits events when new chat messages are received. You can listen for the `chatUpdate` event to log when a new chat message is received. @@ -698,8 +775,9 @@ meeting.chat.on("chatUpdate", () => { }); ``` - - + + + The `meeting.chat` object emits events when new chat messages are received. You can listen for the `chatUpdate` event to log when a new chat message is received. @@ -731,8 +809,9 @@ meeting.chat.on("chatUpdate", () => { }); ``` - - + + + To be able to receive chat messages you need to implement a method `onChatUpdates()` method from callback `RtkChatEventListener`. You can subscribe to this events by calling `meeting.addChatEventListener(rtkChatEventListener)`. @@ -758,8 +837,9 @@ The `onNewChatMessage()` method will be called whenever a new chat message is sh The `onMessageRateLimitReset()` method will be called when the rate limit for sending messages of self is reset and you can send messages again. The default rate limit is 180 messages within 60 seconds. - - + + + To be able to receive chat messages you need to implement a method `onChatUpdates()` method from callback `RtkChatEventListener`. You can subscribe to this events by calling `meeting.addChatEventListener(rtkChatEventListener)`. @@ -785,8 +865,9 @@ The `onNewChatMessage()` method will be called whenever a new chat message is sh The `onMessageRateLimitReset()` method will be called when the rate limit for sending messages of self is reset and you can send messages again. The default rate limit is 180 messages within 60 seconds. - - + + + To be able to receive chat messages you need to implement a method `onChatUpdates()` method from callback `RtkChatEventListener`. You can subscribe to this events by calling `meeting.addChatEventListener(rtkChatEventListener)`. @@ -830,8 +911,9 @@ In this context, `messages` refers to a list of all the chat messages in the mee Whenever a chat message is received, the `meeting.chat.messages` list is automatically updated. - - + + + The `meeting.chat` object emits events when new chat messages are received. You can listen for the `chatUpdate` event to log when a new chat message is received. @@ -854,18 +936,16 @@ meeting.chat.on("chatUpdate", () => { }); ``` - - + ## Editing Chat Messages - - +### Edit a Text Message + + There is a method in `meeting.chat` to edit a message of each type. -### Edit a Text Message - To edit a text message, use the `meeting.chat.editTextMessage()` method. This accepts a `messageId` (type `string`) and a `message` (type `string`). ```js @@ -876,8 +956,43 @@ const newMessage = "Is this the real life?"; await meeting.chat.editTextMessage(messageId, newMessage); ``` + + + + +There is a method in `meeting.chat` to edit a message of each type. + +To edit a text message, use the `meeting.chat.editTextMessage()` method. This accepts a `messageId` (type `string`) and a `message` (type `string`). + +```jsx +const message = meeting.chat.messages[0]; +const messageId = message?.id; +const newMessage = "Is this the real life?"; + +await meeting.chat.editTextMessage(messageId, newMessage); +``` + + + + + +There is a method in `meeting.chat` to edit a message of each type. + +To edit a text message, use the `meeting.chat.editTextMessage()` method. This accepts a `messageId` (type `string`) and a `message` (type `string`). + +```jsx +const message = meeting.chat.messages[0]; +const messageId = message?.id; +const newMessage = "Is this the real life?"; +await meeting.chat.editTextMessage(messageId, newMessage); +``` + + + ### Edit an Image + + You can edit an image with the help of `meeting.chat.editImageMessage()`. This accepts a `messageId` of type `string` and an image of type `File`. ```html @@ -894,66 +1009,9 @@ async function onEditImage() { } ``` -### Edit a File + -Editing a file is similar to editing an image. To edit a file, use `meeting.chat.editFileMessage()`. - -```html - - - -``` - -```js -async function onEditFile() { - const messageId = "..."; - const file = document.getElementById("file"); - await meeting.chat.editFileMessage(messageId, file.files[0]); -} -``` - -### Edit Any Message Type - -There is also a common method called `meeting.chat.editMessage()` that can be used to edit any of the three types of messages displayed above. It essentially calls one of the methods from above depending upon the type of payload you send to the method. The `editMessage()` method accepts parameters `messageId` and `message` of the following type: - -```typescript -async function editMessage( - messageId: string, - message: - | { type: "text"; message: string } - | { type: "image"; image: File } - | { type: "file"; file: File }, -) { - // ... -} -``` - -Here is how you would use the `editMessage()` method to edit a text message: - -```js -const messageId = "..."; -const message = "Is this just fantasy?"; -await meeting.chat.editMessage(messageId, { type: "text", message }); -``` - - - - -There is a method in `meeting.chat` to edit a message of each type. - -### Edit a Text Message - -To edit a text message, use the `meeting.chat.editTextMessage()` method. This accepts a `messageId` (type `string`) and a `message` (type `string`). - -```jsx -const message = meeting.chat.messages[0]; -const messageId = message?.id; -const newMessage = "Is this the real life?"; - -await meeting.chat.editTextMessage(messageId, newMessage); -``` - -### Edit an Image + You can edit an image with the help of `meeting.chat.editImageMessage()`. This accepts a `messageId` of type `string` and an image of type `File`. @@ -987,8 +1045,57 @@ function ChatComponent() { } ``` + + + + +You can edit an image with the help of `meeting.chat.editImageMessage()`. This accepts a `messageId` of type `string` and an image of type File. + +```jsx +import DocumentPicker from "@react-native-documents/picker"; + +async function onEditImage() { + const messageId = "..."; + // Get the image uri and create an object with the following fields + const res = await DocumentPicker.pickSingle({ + type: [DocumentPicker.types.images], + }); + const image = { + uri: res.uri, + name: res.name, + size: res.size, + type: res.type, + }; + await meeting.chat.editImageMessage(messageId, image); +} +``` + + + ### Edit a File + + +Editing a file is similar to editing an image. To edit a file, use `meeting.chat.editFileMessage()`. + +```html + + + +``` + +```js +async function onEditFile() { + const messageId = "..."; + const file = document.getElementById("file"); + await meeting.chat.editFileMessage(messageId, file.files[0]); +} +``` + + + + + Editing a file is similar to editing an image. To edit a file, use `meeting.chat.editFileMessage()`. ```jsx @@ -1015,8 +1122,37 @@ function ChatComponent() { } ``` + + + + +Editing a file is similar to editing an image. To edit a file, use `meeting.chat.editFileMessage()`. + +```jsx +import DocumentPicker from "@react-native-documents/picker"; + +async function onEditFile() { + const messageId = "..."; + // Get the file uri and create an object with the following fields + const res = await DocumentPicker.pickSingle({ + type: [DocumentPicker.types.allFiles], + }); + const file = { + uri: res.uri, + name: res.name, + size: res.size, + type: res.type, + }; + await meeting.chat.editFileMessage(messageId, file); +} +``` + + + ### Edit Any Message Type + + There is also a common method called `meeting.chat.editMessage()` that can be used to edit any of the three types of messages displayed above. It essentially calls one of the methods from above depending upon the type of payload you send to the method. The `editMessage()` method accepts parameters `messageId` and `message` of the following type: ```typescript @@ -1033,75 +1169,41 @@ async function editMessage( Here is how you would use the `editMessage()` method to edit a text message: -```jsx +```js const messageId = "..."; const message = "Is this just fantasy?"; await meeting.chat.editMessage(messageId, { type: "text", message }); ``` - - - -There is a method in `meeting.chat` to edit a message of each type. - -### Edit a text message - -To edit a text message, use the `meeting.chat.editTextMessage()` method. This accepts a `messageId` (type `string`) and a `message` (type `string`). - -```jsx -const message = meeting.chat.messages[0]; -const messageId = message?.id; -const newMessage = "Is this the real life?"; -await meeting.chat.editTextMessage(messageId, newMessage); -``` - -### Edit an image + -You can edit an image with the help of `meeting.chat.editImageMessage()`. This accepts a `messageId` of type `string` and an image of type File. + -```jsx -import DocumentPicker from "@react-native-documents/picker"; +There is also a common method called `meeting.chat.editMessage()` that can be used to edit any of the three types of messages displayed above. It essentially calls one of the methods from above depending upon the type of payload you send to the method. The `editMessage()` method accepts parameters `messageId` and `message` of the following type: -async function onEditImage() { - const messageId = "..."; - // Get the image uri and create an object with the following fields - const res = await DocumentPicker.pickSingle({ - type: [DocumentPicker.types.images], - }); - const image = { - uri: res.uri, - name: res.name, - size: res.size, - type: res.type, - }; - await meeting.chat.editImageMessage(messageId, image); +```typescript +async function editMessage( + messageId: string, + message: + | { type: "text"; message: string } + | { type: "image"; image: File } + | { type: "file"; file: File }, +) { + // ... } ``` -### Edit a file - -Editing a file is similar to editing an image. To edit a file, use `meeting.chat.editFileMessage()`. +Here is how you would use the `editMessage()` method to edit a text message: ```jsx -import DocumentPicker from "@react-native-documents/picker"; - -async function onEditFile() { - const messageId = "..."; - // Get the file uri and create an object with the following fields - const res = await DocumentPicker.pickSingle({ - type: [DocumentPicker.types.allFiles], - }); - const file = { - uri: res.uri, - name: res.name, - size: res.size, - type: res.type, - }; - await meeting.chat.editFileMessage(messageId, file); -} +const messageId = "..."; +const message = "Is this just fantasy?"; +await meeting.chat.editMessage(messageId, { type: "text", message }); ``` -### Edit any message type + + + There is also a common method called `meeting.chat.editMessage()` that can be used to edit any of the three types of messages displayed above. It essentially calls one of the methods from above depending upon the type of payload you send to the method. The `editMessage()` method accepts parameters `messageId` and `message` of the following type: @@ -1125,17 +1227,15 @@ const message = "Is this just fantasy?"; await meeting.chat.editMessage(messageId, { type: "text", message }); ``` - - + ## Other Chat Functions - - +### Get Messages by a User -The `meeting.chat` object exposes certain other methods for convenience when working with chat. + -### Get Messages by a User +The `meeting.chat` object exposes certain other methods for convenience when working with chat. You can get messages by a particular user by passing the user's ID to the `meeting.chat.getMessagesByUser()` method. @@ -1148,16 +1248,78 @@ const { userId } = meeting.participants.joined const messages = meeting.chat.getMessagesByUser(userId); ``` + + + + +The `meeting.chat` object exposes certain other methods for convenience when working with chat. + +You can get messages by a particular user by passing the user's ID to the `meeting.chat.getMessagesByUser()` method. + +```jsx +// Find the userId of the user with name "Freddie". +const { userId } = meeting.participants.joined + .toArray() + .find((p) => p.name === "Freddie"); + +const messages = meeting.chat.getMessagesByUser(userId); +``` + + + + + +The `meeting.chat` object exposes certain other methods for convenience when working with chat. + +You can get messages by a particular user by passing the user's ID to the `meeting.chat.getMessagesByUser()` method. + +```jsx +// Find the userId of the user with name "Freddie". +const { userId } = meeting.participants.joined + .toArray() + .find((p) => p.name === "Freddie"); + +const messages = meeting.chat.getMessagesByUser(userId); +``` + + + ### Get Messages of a Particular Type + + You can also get messages of a particular type using the `meeting.chat.getMessagesByType()` method. For example, you can get all image messages present in the chat using the following snippet: ```js const imageMessages = meeting.chat.getMessagesByType("image"); ``` + + + + +You can also get messages of a particular type using the `meeting.chat.getMessagesByType()` method. For example, you can get all image messages present in the chat using the following snippet: + +```jsx +const imageMessages = meeting.chat.getMessagesByType("image"); +``` + + + + + +You can also get messages of a particular type using the `meeting.chat.getMessagesByType()` method. For example, you can get all image messages present in the chat using the following snippet: + +```jsx +const imageMessages = meeting.chat.getMessagesByType("image"); +``` + + + ### Pinning a Chat Message + + You can pin a number of messages to the chat. When you pin a message, the message object will have the attribute `pinned: true`, using which you can identify if a message is pinned. To pin a message: @@ -1198,42 +1360,9 @@ meeting.chat.on("unpinMessage", ({ message }) => { }); ``` -### Deleting a Chat Message + -The `meeting.chat` namespace exposes a method called `deleteMessage()`. It takes a parameter `messageId` of type `string`. - -```js -const messageId = "..."; -await meeting.chat.deleteMessage(messageId); -``` - - - - -The `meeting.chat` object exposes certain other methods for convenience when working with chat. - -### Get Messages by a User - -You can get messages by a particular user by passing the user's ID to the `meeting.chat.getMessagesByUser()` method. - -```jsx -// Find the userId of the user with name "Freddie". -const { userId } = meeting.participants.joined - .toArray() - .find((p) => p.name === "Freddie"); - -const messages = meeting.chat.getMessagesByUser(userId); -``` - -### Get Messages of a Particular Type - -You can also get messages of a particular type using the `meeting.chat.getMessagesByType()` method. For example, you can get all image messages present in the chat using the following snippet: - -```jsx -const imageMessages = meeting.chat.getMessagesByType("image"); -``` - -### Pinning a Chat Message + You can pin a number of messages to the chat. When you pin a message, the message object will have the attribute `pinned: true`, using which you can identify if a message is pinned. @@ -1275,42 +1404,9 @@ meeting.chat.on("unpinMessage", ({ message }) => { }); ``` -### Deleting a Chat Message - -The `meeting.chat` namespace exposes a method called `deleteMessage()`. It takes a parameter `messageId` of type `string`. - -```jsx -const messageId = "..."; -await meeting.chat.deleteMessage(messageId); -``` - - - - -The `meeting.chat` object exposes certain other methods for convenience when working with chat. - -### Get messages by a user - -You can get messages by a particular user by passing the user's ID to the `meeting.chat.getMessagesByUser()` method. - -```jsx -// Find the userId of the user with name "Freddie". -const { userId } = meeting.participants.joined - .toArray() - .find((p) => p.name === "Freddie"); - -const messages = meeting.chat.getMessagesByUser(userId); -``` - -### Get messages of a particular type - -You can also get messages of a particular type using the `meeting.chat.getMessagesByType()` method. For example, you can get all image messages present in the chat using the following snippet: - -```jsx -const imageMessages = meeting.chat.getMessagesByType("image"); -``` + -### Pinning a chat message + You can pin a number of messages to the chat. When you pin a message, the message object will have the attribute `pinned: true`, using which you can identify if a message is pinned. @@ -1352,7 +1448,33 @@ meeting.chat.on("unpinMessage", ({ message }) => { }); ``` -### Deleting a chat message + + +### Deleting a Chat Message + + + +The `meeting.chat` namespace exposes a method called `deleteMessage()`. It takes a parameter `messageId` of type `string`. + +```js +const messageId = "..."; +await meeting.chat.deleteMessage(messageId); +``` + + + + + +The `meeting.chat` namespace exposes a method called `deleteMessage()`. It takes a parameter `messageId` of type `string`. + +```jsx +const messageId = "..."; +await meeting.chat.deleteMessage(messageId); +``` + + + + The `meeting.chat` namespace exposes a method called `deleteMessage()`. It takes a parameter `messageId` of type `string`. @@ -1361,8 +1483,7 @@ const messageId = "..."; await meeting.chat.deleteMessage(messageId); ``` - - + ## Export chat messages diff --git a/src/content/docs/realtime/realtimekit/core/display-active-speakers.mdx b/src/content/docs/realtime/realtimekit/core/display-active-speakers.mdx index 88c482bf20e4f61..6c67c22bb44ea46 100644 --- a/src/content/docs/realtime/realtimekit/core/display-active-speakers.mdx +++ b/src/content/docs/realtime/realtimekit/core/display-active-speakers.mdx @@ -9,6 +9,8 @@ sidebar: import RTKSDKSelector from "~/components/realtimekit/RTKSDKSelector/RTKSDKSelector.astro"; import RTKCodeSnippet from "~/components/realtimekit/RTKCodeSnippet/RTKCodeSnippet.astro"; + + RealtimeKit automatically detects and tracks participants who are actively speaking in a meeting. You can display either a single active speaker or multiple active speakers in your application UI, depending on your design requirements. An active speaker in RealtimeKit is a remote participant with prominent audio activity at any given moment. The SDK maintains two types of data to help you build your UI: @@ -27,8 +29,6 @@ Active speaker properties contain only remote participants. Use `meeting.self` t The maximum number of participants in the `active` map is one less than the grid size configured in the local participant's [Preset](/realtime/realtimekit/concepts/preset/). This reserves space for the local participant in your UI. For example, if the grid size is 6, the `active` map contains a maximum of 5 remote participants. - - ## Display a single active speaker 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 b8b8f26668af9a4..c1e86a5045ac3e4 100644 --- a/src/content/docs/realtime/realtimekit/core/end-a-session.mdx +++ b/src/content/docs/realtime/realtimekit/core/end-a-session.mdx @@ -10,13 +10,14 @@ import { Render } from "~/components"; import RTKSDKSelector from "~/components/realtimekit/RTKSDKSelector/RTKSDKSelector.astro"; import RTKCodeSnippet from "~/components/realtimekit/RTKCodeSnippet/RTKCodeSnippet.astro"; -To end the current [session](/realtime/realtimekit/concepts/meeting/#session/) for all participants, remove all participants using `kickAll()`. This stops any ongoing recording for that session and sets the session status to `ENDED`. +:::note[Prerequisites] -Ending a session is different from leaving a meeting. Leaving disconnects only the current participant. The session remains active if other participants are still present. +Ensure your participant's preset has the **Kick Participants** (`kick_participant`) host permission enabled. +::: -## Prerequisites +To end the current [session](/realtime/realtimekit/concepts/meeting/#session/) for all participants, remove all participants using `kickAll()`. This stops any ongoing recording for that session and sets the session status to `ENDED`. -- Ensure your participant's preset has the **Kick Participants** (`kick_participant`) host permission enabled. +Ending a session is different from leaving a meeting. Leaving disconnects only the current participant. The session remains active if other participants are still present. ## Steps diff --git a/src/content/docs/realtime/realtimekit/core/error-codes.mdx b/src/content/docs/realtime/realtimekit/core/error-codes.mdx index eb51bcd23926e65..6cdf40ac2f0f5fd 100644 --- a/src/content/docs/realtime/realtimekit/core/error-codes.mdx +++ b/src/content/docs/realtime/realtimekit/core/error-codes.mdx @@ -6,12 +6,14 @@ sidebar: order: 15 --- -import { Tabs, TabItem } from "~/components"; +import RTKSDKSelector from "~/components/realtimekit/RTKSDKSelector/RTKSDKSelector.astro"; +import RTKCodeSnippet from "~/components/realtimekit/RTKCodeSnippet/RTKCodeSnippet.astro"; This page describes RealtimeKit error codes to help you identify and troubleshoot issues. - - + + + All Web Frameworks (Web Components, React, Angular) share the same error codes. @@ -553,9 +555,9 @@ await meeting.leave(); - **Possible reason**: An unexpected error occurred that doesn't fall into other categories. - **Possible solution**: Check the logs for more details and retry the operation. If you continue to experience issues, please reach out to Cloudflare support. - + - + :::note React Native shares the same error codes as Web Frameworks. Refer to the Web Frameworks tab for the complete list of error codes and solutions. @@ -865,5 +867,4 @@ All mobile platforms (iOS, Android, Flutter) share the same error codes. All fal - **Possible reason**: The action cannot be performed in the current stage status. - **Possible solution**: Check the participant's stage status and perform the appropriate action. - - + diff --git a/src/content/docs/realtime/realtimekit/core/index.mdx b/src/content/docs/realtime/realtimekit/core/index.mdx index 19cc12d070a80ae..7ff5b6e77983802 100644 --- a/src/content/docs/realtime/realtimekit/core/index.mdx +++ b/src/content/docs/realtime/realtimekit/core/index.mdx @@ -4,7 +4,7 @@ title: Build using Core SDK slug: realtime/realtimekit/core sidebar: order: 6 - label: Initialize SDK + label: Getting Started --- import RTKSDKSelector from "~/components/realtimekit/RTKSDKSelector/RTKSDKSelector.astro"; @@ -35,13 +35,15 @@ export default function App() { initMeeting({ authToken: "" }); }, []); - useEffect(() => { - // next - if (meeting) meeting.join(); - }, [meeting]) + useEffect(() => { + // next - if (meeting) meeting.join(); + }, [meeting]) return
; + } -``` + +```` Use the [Create Participant API](/api/resources/realtime_kit/#create-a-participant) to fetch the `authToken`. @@ -49,12 +51,16 @@ Use the [Create Participant API](/api/resources/realtime_kit/#create-a-participa Install the client SDK. ```bash npm i @cloudflare/realtimekit -``` +```` + Alternatively, you can also use the CDN. + ```html - + ``` + You can initialise the SDK using `RealtimeKitClient.init`. + ```js const authToken = ; RealtimeKitClient.init({ @@ -63,7 +69,9 @@ You can initialise the SDK using `RealtimeKitClient.init`. // next - meeting.join(); }); ``` + Use the [Create Participant API](/api/resources/realtime_kit/#create-a-participant) to fetch the `authToken`. + @@ -78,14 +86,16 @@ class AppComponent { @ViewChild("myid") meetingComponent: RtkMeeting; rtkMeeting: RealtimeKitClient; - async ngAfterViewInit() { - const meeting = await RealtimeKitClient.init({ - authToken: "", - }); - // next - meeting.join(); - } + async ngAfterViewInit() { + const meeting = await RealtimeKitClient.init({ + authToken: "", + }); + // next - meeting.join(); + } + } -``` + +```` Use the [Create Participant API](/api/resources/realtime_kit/#create-a-participant) to fetch the `authToken`. @@ -290,4 +300,4 @@ interface DefaultOptions { autoSwitchAudioDevice?: boolean; recording?: RecordingConfig; } -``` \ No newline at end of file +```` diff --git a/src/content/docs/realtime/realtimekit/core/local-participant.mdx b/src/content/docs/realtime/realtimekit/core/local-participant.mdx index 283ab266b7a80e5..13940381c7a2b8c 100644 --- a/src/content/docs/realtime/realtimekit/core/local-participant.mdx +++ b/src/content/docs/realtime/realtimekit/core/local-participant.mdx @@ -27,7 +27,7 @@ The local user is accessible via `meeting.self` and contains all information and Access participant identifiers and display information: - + ```js // Participant identifiers @@ -112,7 +112,7 @@ meeting.localUser.picture // Display picture URL Access the local user's media tracks and states: - + ```js // Media state flags @@ -221,7 +221,7 @@ meeting.localUser.isMicrophonePermissionGranted // Microphone permission status Access room state and participant status: - + ```js // Room state @@ -374,7 +374,7 @@ meeting.localUser.flags // ParticipantFlags (recorder, hidden) Mute and unmute the microphone: - + ```js // Enable audio (unmute) @@ -443,7 +443,10 @@ let isAudioEnabled = meeting.localUser.audioEnabled ```jsx -import { useRealtimeKitClient, useRealtimeKitSelector } from "@cloudflare/realtimekit-react-native"; +import { + useRealtimeKitClient, + useRealtimeKitSelector, +} from "@cloudflare/realtimekit-react-native"; import { TouchableHighlight, Text } from "react-native"; function AudioControls() { @@ -490,7 +493,7 @@ final isAudioEnabled = meeting.localUser.audioEnabled; Enable and disable the camera: - + ```js // Enable video @@ -603,7 +606,7 @@ final isVideoEnabled = meeting.localUser.videoEnabled; Start and stop screen sharing: - + ```js // Enable screen share @@ -732,7 +735,7 @@ meeting.localUser.disableScreenShare(); Update the display name before joining the meeting: - + ```js await meeting.self.setName("New Name"); @@ -805,7 +808,7 @@ Name changes only reflect across all participants if done before joining the mee ### Get available devices - + ```js // Get all media devices @@ -953,12 +956,12 @@ function DeviceSelector() { + renderItem={({ item }) => ( handleDeviceChange(item)}> {item.label} - } - keyExtractor={item => item.deviceId} + )} + keyExtractor={(item) => item.deviceId} /> ); @@ -995,7 +998,7 @@ final selectedVideoDevice = meeting.localUser.getSelectedVideoDevice(); Switch to a different media device: - + ```js // Get all devices @@ -1079,7 +1082,7 @@ meeting.localUser.switchCamera(); ## Display local video - + ### Register video element @@ -1460,6 +1463,7 @@ To stop the screen share: ```swift meeting.localUser.disableScreenShare() ``` + @@ -1518,6 +1522,7 @@ To stop the screen share: ```dart meeting.localUser.disableScreenShare() ``` + @@ -1570,13 +1575,13 @@ Add this key inside the Info.plist of the main App: Launch the broadcast extension and enable screen share: ```js -meeting.self.enableScreenShare() +meeting.self.enableScreenShare(); ``` To stop the screen share: ```js -meeting.self.disableScreenShare() +meeting.self.disableScreenShare(); ``` @@ -1587,7 +1592,7 @@ meeting.self.disableScreenShare() Fires when the local user joins the meeting: - + ```js meeting.self.on("roomJoined", () => { @@ -1678,7 +1683,7 @@ Flutter SDK uses a different event model. Monitor `roomJoined` property changes Fires when the local user leaves the meeting: - + ```js meeting.self.on("roomLeft", ({ state }) => { @@ -1789,7 +1794,7 @@ meeting.addSelfEventListener(MeetingSelfListener()); Fires when video is enabled or disabled: - + ```js meeting.self.on("videoUpdate", ({ videoEnabled, videoTrack }) => { @@ -1877,7 +1882,7 @@ Flutter SDK uses a different event model. Monitor `videoEnabled` property change Fires when audio is enabled or disabled: - + ```js meeting.self.on("audioUpdate", ({ audioEnabled, audioTrack }) => { @@ -1961,7 +1966,7 @@ Flutter SDK uses a different event model. Monitor `audioEnabled` property change Fires when screen sharing starts or stops: - + ```js meeting.self.on( @@ -2077,7 +2082,7 @@ Flutter SDK uses a different event model. Monitor `screenShareEnabled` property Fires when the active device changes: - + ```js meeting.self.on("deviceUpdate", ({ device }) => { @@ -2201,7 +2206,7 @@ meeting.addSelfEventListener(DeviceChangeListener()); Triggered when the list of available devices changes (device plugged in or out): - + ```js meeting.self.on("deviceListUpdate", ({ added, removed, devices }) => { @@ -2314,7 +2319,7 @@ meeting.addSelfEventListener(DeviceListListener(meeting)); Monitor your own network quality: - + ```js meeting.self.on( @@ -2464,7 +2469,7 @@ Flutter SDK does not currently expose network quality scores. Triggered when permissions are updated dynamically: - + ```js // Listen to specific permission updates @@ -2534,7 +2539,7 @@ Flutter SDK uses a different permissions model. Refer to the Flutter-specific do Triggered when media permissions are denied or media capture fails: - + ```js meeting.self.on("mediaPermissionError", ({ message, kind }) => { @@ -2674,7 +2679,7 @@ final hasMicPermission = meeting.localUser.isMicrophonePermissionGranted; For meetings with waiting room enabled: - + Monitor the `roomState` property for waitlist status. The value `'waitlisted'` indicates the user is in the waiting room. @@ -2794,7 +2799,7 @@ extension MeetingViewModel: RtkSelfEventListener { Pin or unpin yourself in the meeting (requires appropriate permissions): - + Web SDK does not currently support pinning the local participant. @@ -2847,7 +2852,7 @@ Flutter SDK does not currently support pinning the local participant. Update video or screenshare resolution at runtime: - + Web SDK does not currently expose runtime constraint updates for local participant. diff --git a/src/content/docs/realtime/realtimekit/core/manage-other-participants-in-a-session.mdx b/src/content/docs/realtime/realtimekit/core/manage-participants-in-a-session.mdx similarity index 95% rename from src/content/docs/realtime/realtimekit/core/manage-other-participants-in-a-session.mdx rename to src/content/docs/realtime/realtimekit/core/manage-participants-in-a-session.mdx index c91d0ec282127c1..fb76722bb7c200f 100644 --- a/src/content/docs/realtime/realtimekit/core/manage-other-participants-in-a-session.mdx +++ b/src/content/docs/realtime/realtimekit/core/manage-participants-in-a-session.mdx @@ -1,7 +1,6 @@ --- -title: Manage other participants in a session +title: Manage Participants in a Session pcx_content_type: how-to -slug: realtime/realtimekit/core/manage-other-participants-in-a-session sidebar: order: 7 --- @@ -10,6 +9,11 @@ import { Render } from "~/components"; import RTKSDKSelector from "~/components/realtimekit/RTKSDKSelector/RTKSDKSelector.astro"; import RTKCodeSnippet from "~/components/realtimekit/RTKCodeSnippet/RTKCodeSnippet.astro"; +:::note[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/). +::: + 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. @@ -17,12 +21,6 @@ In this guide, the **local participant** refers to the user performing the actio -## 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 diff --git a/src/content/docs/realtime/realtimekit/core/media-acquisition-approaches.mdx b/src/content/docs/realtime/realtimekit/core/media-acquisition-approaches.mdx index 0ed304b604ed8b7..95242ac745cb71e 100644 --- a/src/content/docs/realtime/realtimekit/core/media-acquisition-approaches.mdx +++ b/src/content/docs/realtime/realtimekit/core/media-acquisition-approaches.mdx @@ -15,6 +15,8 @@ This guide assumes that you are already familiar with [initializing the Realtime RealtimeKit provides flexible approaches for acquiring and managing participant media (audio and video tracks). By default, the SDK handles media acquisition automatically when you initialize it. However, certain use cases require accessing media tracks before or independently of SDK initialization. + + ## When to use custom media acquisition Custom media acquisition is useful when you need to: @@ -34,8 +36,6 @@ Initialize the SDK first, then access media tracks from the meeting object. This **When to use**: You need to access media tracks after SDK initialization for logging, analysis, or integration with other services. - - ```ts @@ -102,8 +102,6 @@ class AppComponent { ## Approach 2: Media-first - - Initialize the media handler first using `RealtimeKitClient.initMedia()`, then pass it to the SDK during initialization. The SDK reuses the acquired media tracks without requesting permissions again. @@ -260,8 +258,6 @@ Acquire and manage media tracks independently using browser APIs, then pass them Initialize the SDK with audio and video disabled, then enable them with your custom tracks: - - ```ts diff --git a/src/content/docs/realtime/realtimekit/core/meeting-metadata.mdx b/src/content/docs/realtime/realtimekit/core/meeting-metadata.mdx index 8573094bc6696c8..d04f601754ec0e9 100644 --- a/src/content/docs/realtime/realtimekit/core/meeting-metadata.mdx +++ b/src/content/docs/realtime/realtimekit/core/meeting-metadata.mdx @@ -6,14 +6,17 @@ sidebar: order: 3 --- -import { Tabs, TabItem } from "~/components"; +import RTKSDKSelector from "~/components/realtimekit/RTKSDKSelector/RTKSDKSelector.astro"; +import RTKCodeSnippet from "~/components/realtimekit/RTKCodeSnippet/RTKCodeSnippet.astro"; All metadata pertaining to a meeting is stored in `meeting.meta`. This includes important information about the meeting state, type, and connections. + ## Available metadata - - +Select a framework based on the platform you are building for. + + The `meeting.meta` object contains the following properties: @@ -24,8 +27,9 @@ The `meeting.meta` object contains the following properties: - **`mediaState`** - Media connection state - **`socketState`** - Socket connection state - - + + + The `meeting.meta` object contains the following properties: @@ -36,8 +40,9 @@ The `meeting.meta` object contains the following properties: - **`mediaState`** - Media connection state - **`socketState`** - Socket connection state - - + + + The `meeting.meta` object contains the following properties: @@ -52,8 +57,9 @@ The `meeting.meta` object contains the following properties: - **`mediaConnectionState`** - The current state of the media connection - **`socketConnectionState`** - The current state of the socket connection - - + + + The `meeting.meta` object contains the following properties: @@ -68,8 +74,9 @@ The `meeting.meta` object contains the following properties: - **`mediaConnectionState`** - The current state of the media connection - **`socketConnectionState`** - The current state of the socket connection - - + + + The `meeting.meta` object contains the following properties: @@ -79,8 +86,9 @@ The `meeting.meta` object contains the following properties: - **`meetingType`** - Indicates the meeting type, which can be one of `groupCall`, `webinar`, or `livestream` from the `RtkMeetingType` enum - **`activeTab`** - Information about the currently active tab for the local participant - - + + + The `meeting.meta` object contains the following properties: @@ -91,15 +99,13 @@ The `meeting.meta` object contains the following properties: - **`mediaState`** - Media connection state - **`socketState`** - Socket connection state - - + ## Access meeting metadata To access meeting metadata, use the `meeting.meta` object. - - + ```javascript // Destructure the metadata to get meetingTitle @@ -112,8 +118,9 @@ if (meeting.self.roomJoined) { } ``` - - + + + ```jsx import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react"; @@ -137,30 +144,34 @@ function MeetingInfo() { } ``` - - + + + ```kotlin val meetingTitle = meeting.meta.meetingTitle ``` - - + + + ```swift let meetingTitle = meeting.meta.meetingTitle ``` - - + + + ```dart final meetingTitle = meeting.meta.meetingTitle; print("The local user has joined ${meetingTitle}."); ``` - - + + + ```tsx import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react-native"; @@ -180,8 +191,7 @@ useEffect(() => { }, [roomJoined, meetingTitle]); ``` - - + ## Connection events @@ -189,8 +199,7 @@ The `meta` object also emits events for indicating changes in the connection sta ### Media connection updates - - + Updates to the media connection (WebRTC connection used for the transfer of actual media) are sent via the `mediaConnectionUpdate` event. @@ -208,8 +217,9 @@ The `mediaConnectionUpdate` event provides: - **`transport`** - Either `'consuming'` (receiving media) or `'producing'` (sending media) - **`state`** - Connection state: `'new'`, `'connecting'`, `'connected'`, `'disconnected'`, `'reconnecting'`, or `'failed'` - - + + + Updates to the media connection (WebRTC connection used for the transfer of actual media) are sent via the `mediaConnectionUpdate` event. @@ -246,8 +256,9 @@ The `mediaConnectionUpdate` event provides: - **`transport`** - Either `'consuming'` (receiving media) or `'producing'` (sending media) - **`state`** - Connection state: `'new'`, `'connecting'`, `'connected'`, `'disconnected'`, `'reconnecting'`, or `'failed'` - - + + + You can access the current media connection state directly from the metadata. @@ -255,8 +266,9 @@ You can access the current media connection state directly from the metadata. val mediaConnectionState = meeting.meta.mediaConnectionState ``` - - + + + You can access the current media connection state directly from the metadata. @@ -264,13 +276,15 @@ You can access the current media connection state directly from the metadata. let mediaConnectionState = meeting.meta.mediaConnectionState ``` - - + + + Media connection events are not available in Flutter. Monitor the connection state through the meeting state changes. - - + + + Updates to the media connection (WebRTC connection used for the transfer of actual media) are sent via the `mediaConnectionUpdate` event. @@ -288,13 +302,11 @@ The `mediaConnectionUpdate` event provides: - **`transport`** - Either `'consuming'` (receiving media) or `'producing'` (sending media) - **`state`** - Connection state: `'new'`, `'connecting'`, `'connected'`, `'disconnected'`, `'reconnecting'`, or `'failed'` - - + ### Socket connection updates - - + Updates to the WebSocket connection (used for chat, polls, and other basic signaling) are sent via the `socketConnectionUpdate` event. @@ -323,8 +335,9 @@ The `socketConnectionUpdate` event provides: - **`reconnectionAttempt`** - The number of reconnection attempts made (if reconnecting) - **`reconnected`** - Boolean indicating if the connection was successfully reestablished - - + + + Updates to the WebSocket connection (used for chat, polls, and other basic signaling) are sent via the `socketConnectionUpdate` event. @@ -373,8 +386,9 @@ The `socketConnectionUpdate` event provides: - **`reconnectionAttempt`** - The number of reconnection attempts made (if reconnecting) - **`reconnected`** - Boolean indicating if the connection was successfully reestablished - - + + + You can access the current socket connection state directly from the metadata. @@ -382,8 +396,9 @@ You can access the current socket connection state directly from the metadata. val socketConnectionState = meeting.meta.socketConnectionState ``` - - + + + You can access the current socket connection state directly from the metadata. @@ -391,13 +406,15 @@ You can access the current socket connection state directly from the metadata. let socketConnectionState = meeting.meta.socketConnectionState ``` - - + + + Socket connection events are not available in Flutter. Monitor the connection state through the meeting state changes. - - + + + Updates to the WebSocket connection (used for chat, polls, and other basic signaling) are sent via the `socketConnectionUpdate` event. @@ -426,8 +443,7 @@ The `socketConnectionUpdate` event provides: - **`reconnectionAttempt`** - The number of reconnection attempts made (if reconnecting) - **`reconnected`** - Boolean indicating if the connection was successfully reestablished - - + ## Next steps diff --git a/src/content/docs/realtime/realtimekit/core/meeting-object-explained.mdx b/src/content/docs/realtime/realtimekit/core/meeting-object-explained.mdx index 15890612c42f40d..e55e96bf56763df 100644 --- a/src/content/docs/realtime/realtimekit/core/meeting-object-explained.mdx +++ b/src/content/docs/realtime/realtimekit/core/meeting-object-explained.mdx @@ -1,7 +1,6 @@ --- pcx_content_type: navigation title: Meeting Object Explained -slug: realtime/realtimekit/core/meeting-object-explained sidebar: order: 2 --- diff --git a/src/content/docs/realtime/realtimekit/core/polls.mdx b/src/content/docs/realtime/realtimekit/core/polls.mdx index 2dc5676f57c3b13..f4a3f0ddd08e35d 100644 --- a/src/content/docs/realtime/realtimekit/core/polls.mdx +++ b/src/content/docs/realtime/realtimekit/core/polls.mdx @@ -6,14 +6,16 @@ sidebar: order: 13 --- -import { Tabs, TabItem } from "~/components"; +import RTKSDKSelector from "~/components/realtimekit/RTKSDKSelector/RTKSDKSelector.astro"; +import RTKCodeSnippet from "~/components/realtimekit/RTKCodeSnippet/RTKCodeSnippet.astro"; This guide explains how to create, vote on, and interact with polls in a meeting using Cloudflare RealtimeKit. + + ## Introduction - - + The meetings polls object can be accessed using `meeting.polls`. It provides methods to create polls, vote, and more. @@ -27,8 +29,60 @@ The `meeting.polls.items` property returns an array of all polls created in a me console.log("All polls:", meeting.polls.items); ``` + + + + +The meetings polls object can be accessed using `meeting.polls`. It provides methods to create polls, vote, and more. + +```jsx +console.log("Polls object:", meeting.polls); +``` + +The `meeting.polls.items` property returns an array of all polls created in a meeting, where each element is an object of type `Poll`. + +```jsx +console.log("All polls:", meeting.polls.items); +``` + + + + + +You can access the polls functionality in a meeting using the `meeting.polls` object. This object provides methods to create polls, vote, and perform other poll-related actions. + +To retrieve all polls created during a meeting, use `meeting.polls.items`. This returns an array where each element is an object of type `com.cloudflare.realtimekit.polls.Poll`. + + + + + +The meetings polls object can be accessed using `meeting.polls`. It provides methods to create polls, vote, and more. + +`meeting.polls.items` returns an array of all polls created in a meeting, where each element is an object of type `Poll`. + + + + + +The meetings polls object can be accessed using `meeting.polls`. It provides methods to create polls, vote, and more. + +`meeting.polls.items` returns an array of all polls created in a meeting, where each element is an object of type `Poll`. + + + + + +The meetings polls object can be accessed using `meeting.polls`. It provides methods to create polls, vote, and more. + +`meeting.polls.items` returns an array of all polls created in a meeting, where each element is an object of type `Poll`. + + + ### Poll Type + + The `Poll` type is defined as follows: ```typescript @@ -53,22 +107,9 @@ interface PollOption { } ``` - - - -The meetings polls object can be accessed using `meeting.polls`. It provides methods to create polls, vote, and more. - -```jsx -console.log("Polls object:", meeting.polls); -``` - -The `meeting.polls.items` property returns an array of all polls created in a meeting, where each element is an object of type `Poll`. + -```jsx -console.log("All polls:", meeting.polls.items); -``` - -### Poll Type + The `Poll` type is defined as follows: @@ -94,14 +135,9 @@ interface PollOption { } ``` - - - -You can access the polls functionality in a meeting using the `meeting.polls` object. This object provides methods to create polls, vote, and perform other poll-related actions. - -To retrieve all polls created during a meeting, use `meeting.polls.items`. This returns an array where each element is an object of type `com.cloudflare.realtimekit.polls.Poll`. + -### Poll type + The `Poll` type represents a poll within a meeting: @@ -136,36 +172,9 @@ class PollVote( ) ``` -### Listening to new polls in a meeting - -To receive updates about new polls or poll changes during a meeting, implement the `RtkPollsEventListener` interface. Register your listener using `meeting.addPollsEventListener(rtkPollsEventListener)`. - -The `onNewPoll()` method is called whenever a new poll is created in the meeting. - -The `onPollUpdate()` method is invoked when a specific poll is updated, such as when participants vote or poll details change. The `onPollUpdates()` method is called when there are updates to the list of polls, including new polls being created or multiple polls being updated at once. Use these callbacks to keep your poll UI in sync with the latest poll data. - -```kotlin -meeting.addPollsEventListener(object : RtkPollsEventListener { - override fun onNewPoll(poll: Poll) { - } + - override fun onPollUpdate(poll: Poll) { - } - - override fun onPollUpdates(pollItems: List) { - } - } -) -``` - - - - -The meetings polls object can be accessed using `meeting.polls`. It provides methods to create polls, vote, and more. - -`meeting.polls.items` returns an array of all polls created in a meeting, where each element is an object of type `Poll`. - -### Poll type + The `Poll` type is defined as follows: @@ -196,32 +205,9 @@ class PollVote { } ``` -### Listening to new polls in a meeting - -To be able to receive new poll messages you need to implement a method `onPollUpdates()` method from callback `RtkPollsEventListener`. You can subscribe to this events by calling `meeting.addPollsEventListener(meetingViewModel)` - -```swift -extension MeetingViewModel: RtkPollsEventListener { - func onNewPoll(poll: Poll) { - // code to handle new poll - } - - func onPollUpdates(pollItems: [Poll]) { - // code to handle polls and their vote updates. - } - - func onPollUpdate(poll: Poll) {} -} -``` - - - + -The meetings polls object can be accessed using `meeting.polls`. It provides methods to create polls, vote, and more. - -`meeting.polls.items` returns an array of all polls created in a meeting, where each element is an object of type `Poll`. - -### Poll type + The `Poll` type is defined as follows: @@ -262,40 +248,9 @@ class PollVote { } ``` -### Listening to new polls in a meeting - -To be able to receive new poll messages you need to implement a method `onPollUpdates()` method from callback `RtkPollsEventListener`: - -To get poll updates, listen to `onPollUpdates()` callback: - -```dart -class PollEventsListener extends RtkPollsEventListener { - @override - void onPollUpdates(List pollItems) { - /// code to handle polls - } - - @override - void onNewPoll(Poll poll) { - /// code to handle new poll - } -} -``` - -You can subscribe to these events as follows: - -```dart -meeting.addPollsEventListener(PollEventsListener()); -``` - - - + -The meetings polls object can be accessed using `meeting.polls`. It provides methods to create polls, vote, and more. - -`meeting.polls.items` returns an array of all polls created in a meeting, where each element is an object of type `Poll`. - -### Poll type + The `Poll` type is defined as follows: @@ -321,13 +276,11 @@ interface PollOption { } ``` - - + ## Creating a Poll - - + A new poll can be created using the `create` method from the `meeting.polls` object. The `meeting.polls.create()` method accepts the following parameters: @@ -346,8 +299,9 @@ await meeting.polls.create( ); ``` - - + + + A new poll can be created using the `create` method from the `meeting.polls` object. The `meeting.polls.create()` method accepts the following parameters: @@ -366,8 +320,9 @@ await meeting.polls.create( ); ``` - - + + + To create a new poll, use the `create` method available on the `meeting.polls` object. The `meeting.polls.create()` function requires the following parameters: @@ -389,8 +344,9 @@ val pollsCreateError: PollsError? = meeting.polls.create( ) ``` - - + + + A new poll can be created using the `create` method from the `meeting.polls` object. The `meeting.polls.createPoll()` method accepts the following parameters: @@ -412,8 +368,9 @@ let pollsCreateError: PollsError? = meeting.polls.createPoll( ) ``` - - + + + A new poll can be created using the `create` method from the `meeting.polls` object. The `meeting.polls.create(...)` method accepts the following parameters: @@ -435,8 +392,9 @@ meeting.polls.create( ); ``` - - + + + A new poll can be created using the `create` method from the `meeting.polls` object. The `meeting.polls.create()` method accepts the following parameters: @@ -457,13 +415,11 @@ await meeting.poll.create( ); ``` - - + ## Voting on a Poll - - + The `meeting.polls.vote()` method can be used to register a vote on a poll. It accepts the following parameters: @@ -477,8 +433,9 @@ const poll = meeting.polls.items[0]; await meeting.polls.vote(poll.id, 0); ``` - - + + + The `meeting.polls.vote()` method can be used to register a vote on a poll. It accepts the following parameters: @@ -492,8 +449,9 @@ const poll = meeting.polls.items[0]; await meeting.polls.vote(poll.id, 0); ``` - - + + + To register a vote on a poll, use the `meeting.polls.vote()` method. This method requires the following parameters: @@ -511,8 +469,9 @@ val selectedPollOption: PollOption = poll.options.first() val pollsError: PollsError? = meeting.polls.vote(poll.id, selectedPollOption) ``` - - + + + The `meeting.polls.vote()` method can be used to register a vote on a poll. It accepts the following parameters: @@ -530,8 +489,9 @@ let selectedPollOption: PollOption = poll.options[0] meeting.poll.vote(poll, selectedPollOption) ``` - - + + + The `meeting.polls.vote()` method can be used to register a vote on a poll. It accepts the following parameters: @@ -549,8 +509,9 @@ final selectedPollOption = poll.options[0]; meeting.polls.vote(poll: poll, pollOption: selectedPollOption); ``` - - + + + The `meeting.polls.vote()` method can be used to register a vote on a poll. It accepts the following parameters: @@ -566,16 +527,14 @@ const poll = meeting.polls.items[0]; await meeting.poll.vote(poll.id, 0); ``` - - + ## Other Poll Functions - - - ### View Poll Results + + The total votes on a poll can be accessed in the following manner: ```js @@ -594,24 +553,9 @@ const options = poll.options; `options` returns an array of objects, where each object is of type `PollOption`. -### Get Notified When a Poll is Created or Updated - -An event is fired each time `meeting.polls.items` is updated or created. You can listen for this to get the updated list of polls. The response object contains the following properties: - -- `polls` - List of all polls -- `newPoll` - A boolean variable which is `true` when a new poll has been created - -```js -meeting.polls.on("pollsUpdate", ({ polls, newPoll }) => { - console.log("Polls updated:", polls); - console.log("Is new poll:", newPoll); -}); -``` + - - - -### View Poll Results + The total votes on a poll can be accessed in the following manner: @@ -631,33 +575,9 @@ const options = poll.options; `options` returns an array of objects, where each object is of type `PollOption`. -### Get Notified When a Poll is Created or Updated - -An event is fired each time `meeting.polls.items` is updated or created. You can listen for this to get the updated list of polls. The response object contains the following properties: - -- `polls` - List of all polls -- `newPoll` - A boolean variable which is `true` when a new poll has been created - -```jsx -meeting.polls.on("pollsUpdate", ({ polls, newPoll }) => { - console.log("Polls updated:", polls); - console.log("Is new poll:", newPoll); -}); -``` - -Alternatively, you can use React hooks to listen for poll updates: - -```jsx -import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react"; - -// useRealtimeKitSelector hook only works when `RealtimeKitProvider` is used. -const polls = useRealtimeKitSelector((m) => m.polls.items); -``` - - - + -### View poll results + The total votes on a poll can be accessed in the following manner: @@ -677,10 +597,9 @@ val options = poll.options `options` returns an array of objects, where each object is of type `PollOption`. - - + -### View poll results + The total votes on a poll can be accessed in the following manner: @@ -700,10 +619,9 @@ let options = poll.options `options` returns an array of objects, where each object is of type `PollOption`. - - + -### View poll results + The total votes on a poll can be accessed in the following manner: @@ -723,10 +641,9 @@ final options = poll.options; `options` returns an array of objects, where each object is of type `PollOption`. - - + -### View poll results + The total votes on a poll can be accessed in the following manner: @@ -746,7 +663,52 @@ const options = poll.options; `options` returns an array of objects, where each object is of type `PollOption`. -### Get notified when a poll is created or updated + + +### Get Notified When a Poll is Created or Updated + + + +An event is fired each time `meeting.polls.items` is updated or created. You can listen for this to get the updated list of polls. The response object contains the following properties: + +- `polls` - List of all polls +- `newPoll` - A boolean variable which is `true` when a new poll has been created + +```js +meeting.polls.on("pollsUpdate", ({ polls, newPoll }) => { + console.log("Polls updated:", polls); + console.log("Is new poll:", newPoll); +}); +``` + + + + + +An event is fired each time `meeting.polls.items` is updated or created. You can listen for this to get the updated list of polls. The response object contains the following properties: + +- `polls` - List of all polls +- `newPoll` - A boolean variable which is `true` when a new poll has been created + +```jsx +meeting.polls.on("pollsUpdate", ({ polls, newPoll }) => { + console.log("Polls updated:", polls); + console.log("Is new poll:", newPoll); +}); +``` + +Alternatively, you can use React hooks to listen for poll updates: + +```jsx +import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react"; + +// useRealtimeKitSelector hook only works when `RealtimeKitProvider` is used. +const polls = useRealtimeKitSelector((m) => m.polls.items); +``` + + + + An event is fired each time `meeting.polls.items` is updated or created. You can listen for this to get the updated list of polls. The response object contains the following properties: @@ -759,5 +721,76 @@ meeting.polls.on("pollsUpdate", ({ polls, newPoll }) => { }); ``` - - + + + + +To receive updates about new polls or poll changes during a meeting, implement the `RtkPollsEventListener` interface. Register your listener using `meeting.addPollsEventListener(rtkPollsEventListener)`. + +The `onNewPoll()` method is called whenever a new poll is created in the meeting. + +The `onPollUpdate()` method is invoked when a specific poll is updated, such as when participants vote or poll details change. The `onPollUpdates()` method is called when there are updates to the list of polls, including new polls being created or multiple polls being updated at once. Use these callbacks to keep your poll UI in sync with the latest poll data. + +```kotlin +meeting.addPollsEventListener(object : RtkPollsEventListener { + override fun onNewPoll(poll: Poll) { + } + + override fun onPollUpdate(poll: Poll) { + } + + override fun onPollUpdates(pollItems: List) { + } + } +) +``` + + + + + +To be able to receive new poll messages you need to implement a method `onPollUpdates()` method from callback `RtkPollsEventListener`. You can subscribe to this events by calling `meeting.addPollsEventListener(meetingViewModel)` + +```swift +extension MeetingViewModel: RtkPollsEventListener { + func onNewPoll(poll: Poll) { + // code to handle new poll + } + + func onPollUpdates(pollItems: [Poll]) { + // code to handle polls and their vote updates. + } + + func onPollUpdate(poll: Poll) {} +} +``` + + + + + +To be able to receive new poll messages you need to implement a method `onPollUpdates()` method from callback `RtkPollsEventListener`: + +To get poll updates, listen to `onPollUpdates()` callback: + +```dart +class PollEventsListener extends RtkPollsEventListener { + @override + void onPollUpdates(List pollItems) { + /// code to handle polls + } + + @override + void onNewPoll(Poll poll) { + /// code to handle new poll + } +} +``` + +You can subscribe to these events as follows: + +```dart +meeting.addPollsEventListener(PollEventsListener()); +``` + + diff --git a/src/content/docs/realtime/realtimekit/core/remote-participant.mdx b/src/content/docs/realtime/realtimekit/core/remote-participant.mdx deleted file mode 100644 index 578163189591678..000000000000000 --- a/src/content/docs/realtime/realtimekit/core/remote-participant.mdx +++ /dev/null @@ -1,1032 +0,0 @@ ---- -pcx_content_type: navigation -title: Remote Participant -slug: realtime/realtimekit/core/remote-participant -sidebar: - order: 5 ---- - -import { Tabs, TabItem } from "~/components"; - -This guide explains how to access participant data, display videos, handle events, and manage participant permissions in your RealtimeKit meetings. - -:::note[Prerequisites] -This page assumes you've already initialized the SDK and understand the meeting object structure. Refer to [Initialize SDK](/realtime/realtimekit/core/) and [Meeting Object Explained](/realtime/realtimekit/core/meeting-object-explained/) if needed. -::: - -## Participant Maps - - - - -The data regarding all meeting participants is stored under `meeting.participants`. These do not include the local user. - -The `meeting.participants` object contains the following maps: - -- **`joined`** - All participants currently in the meeting (excluding the local user) -- **`waitlisted`** - All participants waiting to join the meeting -- **`active`** - All participants whose media is subscribed to (participants that should be displayed on screen) -- **`pinned`** - All pinned participants in the meeting - -If you're building a video/audio grid, you'd use the `active` map. To display a list of all participants, use the `joined` map. - -Each participant in these maps is of type `RTKParticipant`. - -### Access Participant Maps - -```js -// Get all joined participants -const joinedParticipants = meeting.participants.joined; - -// Get active participants (those on screen) -const activeParticipants = meeting.participants.active; - -// Get pinned participants -const pinnedParticipants = meeting.participants.pinned; - -// Get waitlisted participants -const waitlistedParticipants = meeting.participants.waitlisted; -``` - -### Listen to Participant Map Events - -Each participant map emits `participantJoined` and `participantLeft` events: - -```js -// Listen for when a participant gets pinned -meeting.participants.pinned.on("participantJoined", (participant) => { - console.log(`Participant ${participant.name} got pinned`); -}); - -// Listen for when a participant gets unpinned -meeting.participants.pinned.on("participantLeft", (participant) => { - console.log(`Participant ${participant.name} got unpinned`); -}); -``` - -### 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); -``` - - - - -The data regarding all meeting participants is stored under `meeting.participants`. These do not include the local user. - -The `meeting.participants` object contains the following maps: - -- **`joined`** - All participants currently in the meeting (excluding the local user) -- **`waitlisted`** - All participants waiting to join the meeting -- **`active`** - All participants whose media is subscribed to (participants that should be displayed on screen) -- **`pinned`** - All pinned participants in the meeting - -If you're building a video/audio grid, you'd use the `active` map. To display a list of all participants, use the `joined` map. - -Each participant in these maps is of type `RTKParticipant`. - -### Access Participant Maps - -Use the `useRealtimeKitSelector` hook to access participant maps: - -```jsx -import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react"; - -// Get all joined participants -const joinedParticipants = useRealtimeKitSelector((m) => m.participants.joined); - -// Get active participants (those on screen) -const activeParticipants = useRealtimeKitSelector((m) => m.participants.active); - -// Get pinned participants -const pinnedParticipants = useRealtimeKitSelector((m) => m.participants.pinned); - -// Get waitlisted participants -const waitlistedParticipants = useRealtimeKitSelector( - (m) => m.participants.waitlisted, -); -``` - -### Listen to Participant Map Events - -You can also use event listeners for specific actions: - -```jsx -import { useRealtimeKitClient } from "@cloudflare/realtimekit-react"; - -function ParticipantListener() { - const [meeting] = useRealtimeKitClient(); - - useEffect(() => { - if (!meeting) return; - - const handleParticipantPinned = (participant) => { - console.log(`Participant ${participant.name} got pinned`); - }; - - meeting.participants.pinned.on( - "participantJoined", - handleParticipantPinned, - ); - - return () => { - meeting.participants.pinned.off( - "participantJoined", - handleParticipantPinned, - ); - }; - }, [meeting]); -} -``` - -### Participant Map Properties - -```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, -); -``` - - - - -## 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"); -``` - -### Set Page in Paginated Mode - -```js -// Switch to second page -await meeting.participants.setPage(2); -``` - - - - -### Set View Mode - -```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"); -``` - -### 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); -``` - - - - -## Waiting Room Methods - - - - -### Accept Waiting Room Request - -```js -await meeting.participants.acceptWaitingRoomRequest(participantId); -``` - -### Reject Waiting Room Request - -```js -await meeting.participants.rejectWaitingRoomRequest(participantId); -``` - - - - -### Accept Waiting Room Request - -```jsx -await meeting.participants.acceptWaitingRoomRequest(participantId); -``` - -### Reject Waiting Room Request - -```jsx -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` - 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 - -**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 - - - - -### 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); -``` - -### 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, - ); - }, -); -``` - -### 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(); -``` - -### Pin and Unpin Participants - -```js -const participant = meeting.participants.joined.get(participantId); - -// Pin a participant -await participant.pin(); - -// Unpin a participant -await participant.unpin(); -``` - - - - -### Access Participant Object - -```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; -``` - -### Listen to Participant Events - -```jsx -import { useRealtimeKitClient } from "@cloudflare/realtimekit-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, -); -``` - -### Host Control Methods - -If you have the relevant permissions, you can control participant media: - -```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(); -``` - -### Pin and Unpin Participants - -```jsx -const participant = meeting.participants.joined.get(participantId); - -// Pin a participant -await participant.pin(); - -// Unpin a participant -await participant.unpin(); -``` - - - - -## Display Participant Videos - - - - -### Register Video Element - -To play a participant's video track on a ` - - -### Using UI Kit Grid Components - -RealtimeKit provides ready-to-use grid components for displaying participant videos: - -```jsx -import { RtkGrid } from "@cloudflare/realtimekit-react-ui"; - -function MeetingGrid() { - return ; -} -``` - -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 - -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) => ( - - ))} -
- ); -} -``` - -
-
- -## Participant Events - - - - -### View Mode Change - -Triggered when the view mode changes: - -```js -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); - }, -); -``` - -### Active Speaker - -Triggered when a participant starts speaking: - -```js -meeting.participants.on("activeSpeaker", (participant) => { - console.log(`${participant.id} is currently speaking`); -}); -``` - -### 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`); -}); -``` - -### 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`); -}); -``` - -### Participant Pinned - -Triggered when a participant is pinned: - -```js -meeting.participants.joined.on("pinned", (participant) => { - console.log(`Participant with id "${participant.id}" was pinned`); -}); -``` - -### Participant Unpinned - -Triggered when a participant is unpinned: - -```js -meeting.participants.joined.on("unpinned", (participant) => { - console.log(`Participant with id "${participant.id}" was unpinned`); -}); -``` - -### Video Update - -Triggered when any participant starts/stops video: - -```js -meeting.participants.joined.on("videoUpdate", (participant) => { - console.log( - `A participant with id "${participant.id}" updated their video track`, - ); - - if (participant.videoEnabled) { - // Use participant.videoTrack - } else { - // Handle stop video - } -}); -``` - -### Audio Update - -Triggered when any participant starts/stops audio: - -```js -meeting.participants.joined.on("audioUpdate", (participant) => { - console.log( - `A participant with id "${participant.id}" updated their audio track`, - ); - - if (participant.audioEnabled) { - // Use participant.audioTrack - } else { - // Handle stop audio - } -}); -``` - -### Screen Share Update - -Triggered when any participant starts/stops screen share: - -```js -meeting.participants.joined.on("screenShareUpdate", (participant) => { - console.log( - `A participant with id "${participant.id}" updated their screen share`, - ); - - if (participant.screenShareEnabled) { - // Use participant.screenShareTracks - } else { - // Handle stop screen share - } -}); -``` - -### Network Quality Score - -Monitor participant network quality: - -```js -meeting.participants.joined.on( - "mediaScoreUpdate", - ({ participantId, kind, isScreenshare, score, scoreStats }) => { - if (kind === "video") { - console.log( - `Participant ${participantId}'s ${isScreenshare ? "screenshare" : "video"} quality score is`, - score, - ); - } - - if (kind === "audio") { - console.log( - `Participant ${participantId}'s audio quality score is`, - score, - ); - } - - if (score < 5) { - console.log(`Participant ${participantId}'s media quality is poor`); - } - }, -); -``` - - - - -### 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 - -```jsx -const joinedParticipants = useRealtimeKitSelector((m) => m.participants.joined); -``` - -Or use event listener: - -```jsx -meeting.participants.joined.on("participantLeft", (participant) => { - console.log(`A participant with id "${participant.id}" has left the meeting`); -}); -``` - -### Participant Pinned/Unpinned - -```jsx -const pinnedParticipants = useRealtimeKitSelector((m) => m.participants.pinned); -``` - -### 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), -); -``` - -### 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), -); -``` - -### 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), -); -``` - -### Network Quality Score - -```jsx -// Use event listener for media score updates -useEffect(() => { - if (!meeting) return; - - const handleMediaScoreUpdate = ({ - participantId, - kind, - isScreenshare, - score, - scoreStats, - }) => { - if (kind === "video") { - console.log( - `Participant ${participantId}'s ${isScreenshare ? "screenshare" : "video"} quality score is`, - score, - ); - } - - if (score < 5) { - console.log(`Participant ${participantId}'s media quality is poor`); - } - }; - - meeting.participants.joined.on("mediaScoreUpdate", handleMediaScoreUpdate); - - return () => { - meeting.participants.joined.off("mediaScoreUpdate", handleMediaScoreUpdate); - }; -}, [meeting]); -``` - - - - -## Picture-in-Picture - -Picture-in-Picture API allows you to render `meeting.participants.active` participant's video as a floating tile outside of the current webpage's context. - -:::note -Supported in Chrome/Edge/Chromium-based browsers only. -::: - - - - -### Check if Supported - -```js -const isSupported = meeting.participants.pip.isSupported(); -``` - -### Enable Picture-in-Picture - -```js -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(); -``` - -### Enable Picture-in-Picture - -```jsx -await meeting.participants.pip.enable(); -``` - -### Disable Picture-in-Picture - -```jsx -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. - - - - -### Find Target Participants - -```js -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 = { - chat: { - public: { - files: true, - }, - }, -}; - -meeting.participants.updatePermissions(participantIds, newPermissions); -``` - -### Available Permission Fields - -```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; - }; - }; -} -``` - - - - -### Find Target Participants - -```jsx -const participantIds = meeting.participants.joined - .toArray() - .filter((e) => e.name.startsWith("John")) - .map((p) => p.id); -``` - -### Update Permissions - -```jsx -// 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?: { - canCreate?: boolean; - canVote?: boolean; - }; - plugins?: { - canClose?: boolean; - canStart?: boolean; - }; - chat?: { - public?: { - canSend?: boolean; - text?: boolean; - files?: boolean; - }; - private?: { - canSend?: boolean; - text?: boolean; - files?: boolean; - }; - }; -} -``` - - - diff --git a/src/content/docs/realtime/realtimekit/core/remote-participants/events.mdx b/src/content/docs/realtime/realtimekit/core/remote-participants/events.mdx new file mode 100644 index 000000000000000..4ae177c334c7298 --- /dev/null +++ b/src/content/docs/realtime/realtimekit/core/remote-participants/events.mdx @@ -0,0 +1,473 @@ +--- +pcx_content_type: navigation +title: Remote Participants Events +sidebar: + label: Events + order: 2 +--- + +import RTKSDKSelector from "~/components/realtimekit/RTKSDKSelector/RTKSDKSelector.astro"; +import RTKCodeSnippet from "~/components/realtimekit/RTKCodeSnippet/RTKCodeSnippet.astro"; + +This page provides an overview of the events emitted by `meeting.participants` and related participant maps, which you can use to keep your UI in sync with changes such as participants joining or leaving, pinning updates, active speaker changes, and grid view mode or page changes. + +:::note[Prerequisites] +This page assumes you've already initialized the SDK and understand the meeting object structure. Refer to [Initialize SDK](/realtime/realtimekit/core/) and [Meeting Object Explained](/realtime/realtimekit/core/meeting-object-explained/) if needed. +::: + + + +## Grid + +These events allow users to monitor changes to the grid. + +### View Mode Change + + + +Triggered when the type of grid changes: + +```js +meeting.participants.on( + "viewModeChanged", + ({ viewMode, currentPage, pageCount }) => { + console.log("view mode changed", viewMode); + }, +); +``` + + + + + +```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 + +Triggered when the page changes in paginated mode: + + + +```js +meeting.participants.on( + "pageChanged", + ({ viewMode, currentPage, pageCount }) => { + console.log("page changed", currentPage); + }, +); +``` + + + + + +```jsx +const currentPage = useRealtimeKitSelector((m) => m.participants.currentPage); +const pageCount = useRealtimeKitSelector((m) => m.participants.pageCount); +``` + +Or use event listener: + +```js +meeting.participants.on("activeSpeaker", (participant) => { + console.log(`${participant.id} is currently speaking`); +}); +``` + + + +### Active Speaker + +Triggered when a participant starts speaking: + + + +```js +meeting.participants.on("activeSpeaker", (participant) => { + console.log(`${participant.id} is currently speaking`); +}); +``` + + + + + +```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 Maps + +These events allow users to monitor changes to remote participant maps. Ideal if you want to get notified when a participant joins or leaves the meeting, is pinned or moves out of the grid. + +### 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`); +}); +``` + + + + + +```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 + +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`); +}); +``` + + + + + +```jsx +const joinedParticipants = useRealtimeKitSelector((m) => m.participants.joined); +``` + +Or use event listener: + +```jsx +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 +const pinnedParticipants = useRealtimeKitSelector((m) => m.participants.pinned); +``` + +Or use event listener: + +```jsx +meeting.participants.joined.on("pinned", (participant) => { + console.log(`Participant with id "${participant.id}" was pinned`); +}); +``` + + + +### Participant Unpinned + +Triggered when a participant is unpinned: + + + +```js +meeting.participants.joined.on("unpinned", (participant) => { + console.log(`Participant with id "${participant.id}" was unpinned`); +}); +``` + + + + + +```jsx +const pinnedParticipants = useRealtimeKitSelector((m) => m.participants.pinned); +``` + +Or use event listener: + +```jsx +meeting.participants.joined.on("unpinned", (participant) => { + console.log(`Participant with id "${participant.id}" was pinned`); +}); +``` + + + +## Participant + +You can monitor changes to a specific participant using the following events: + +### Video Update + + + +Triggered when any participant starts/stops video: + +```js +meeting.participants.joined.on("videoUpdate", (participant) => { + console.log( + `A participant with id "${participant.id}" updated their video track`, + ); + + if (participant.videoEnabled) { + // Use participant.videoTrack + } else { + // Handle stop video + } +}); +``` + + + + + +```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), +); + +meeting.participants.joined.on("videoUpdate", (participant) => { + console.log( + `A participant with id "${participant.id}" updated their video track`, + ); + + if (participant.videoEnabled) { + // Use participant.videoTrack + } else { + // Handle stop video + } +}); +``` + + + +### Audio Update + + + +Triggered when any participant starts/stops audio: + +```js +meeting.participants.joined.on("audioUpdate", (participant) => { + console.log( + `A participant with id "${participant.id}" updated their audio track`, + ); + + if (participant.audioEnabled) { + // Use participant.audioTrack + } else { + // Handle stop audio + } +}); +``` + + + + + +```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), +); + +meeting.participants.joined.on("audioUpdate", (participant) => { + console.log( + `A participant with id "${participant.id}" updated their audio track`, + ); + + if (participant.audioEnabled) { + // Use participant.audioTrack + } else { + // Handle stop audio + } +}); +``` + + + +### Screen Share Update + +Triggered when any participant starts/stops screen share: + + + +```js +meeting.participants.joined.on("screenShareUpdate", (participant) => { + console.log( + `A participant with id "${participant.id}" updated their screen share`, + ); + + if (participant.screenShareEnabled) { + // Use participant.screenShareTracks + } else { + // Handle stop screen share + } +}); +``` + + + + + +```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), +); +``` + +```js +meeting.participants.joined.on("screenShareUpdate", (participant) => { + console.log( + `A participant with id "${participant.id}" updated their screen share`, + ); + + if (participant.screenShareEnabled) { + // Use participant.screenShareTracks + } else { + // Handle stop screen share + } +}); +``` + + + +### Network Quality Score + + + +Monitor participant network quality: + +```js +meeting.participants.joined.on( + "mediaScoreUpdate", + ({ participantId, kind, isScreenshare, score, scoreStats }) => { + if (kind === "video") { + console.log( + `Participant ${participantId}'s ${isScreenshare ? "screenshare" : "video"} quality score is`, + score, + ); + } + + if (kind === "audio") { + console.log( + `Participant ${participantId}'s audio quality score is`, + score, + ); + } + + if (score < 5) { + console.log(`Participant ${participantId}'s media quality is poor`); + } + }, +); +``` + + + + + +```jsx +// Use event listener for media score updates +useEffect(() => { + if (!meeting) return; + + const handleMediaScoreUpdate = ({ + participantId, + kind, + isScreenshare, + score, + scoreStats, + }) => { + if (kind === "video") { + console.log( + `Participant ${participantId}'s ${isScreenshare ? "screenshare" : "video"} quality score is`, + score, + ); + } + + if (score < 5) { + console.log(`Participant ${participantId}'s media quality is poor`); + } + }; + + meeting.participants.joined.on("mediaScoreUpdate", handleMediaScoreUpdate); + + return () => { + meeting.participants.joined.off("mediaScoreUpdate", handleMediaScoreUpdate); + }; +}, [meeting]); +``` + + diff --git a/src/content/docs/realtime/realtimekit/core/remote-participants/index.mdx b/src/content/docs/realtime/realtimekit/core/remote-participants/index.mdx new file mode 100644 index 000000000000000..b4b682ce97560ae --- /dev/null +++ b/src/content/docs/realtime/realtimekit/core/remote-participants/index.mdx @@ -0,0 +1,763 @@ +--- +pcx_content_type: navigation +title: Remote Participants +sidebar: + label: Overview + order: 5 +--- + +import RTKSDKSelector from "~/components/realtimekit/RTKSDKSelector/RTKSDKSelector.astro"; +import RTKCodeSnippet from "~/components/realtimekit/RTKCodeSnippet/RTKCodeSnippet.astro"; + +This guide explains how to access participant data, display videos, handle events, and manage participant permissions in your RealtimeKit meetings. + +:::note[Prerequisites] +This page assumes you've already initialized the SDK and understand the meeting object structure. Refer to [Initialize SDK](/realtime/realtimekit/core/) and [Meeting Object Explained](/realtime/realtimekit/core/meeting-object-explained/) if needed. +::: + + + +The participant object contains all information related to a particular participants, including information about the grid and each participants media streams, name, and state variables. It is accessible via `meeting.participants`. + +### Properties + +#### 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 + + + +```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); +``` + + + + + +Use the `useRealtimeKitSelector` hook to access properties: + +```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, +); +``` + + +#### Media Properties: + +These properties are available on each participant in `meeting.participants`. + +- `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 + +You can fetch a participant from the [Participant Maps](/realtime/realtimekit/core/remote-participants/#participant-maps/). + + + +```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 +console.log(participant.name); +console.log(participant.videoEnabled); +console.log(participant.audioEnabled); +``` + + + +## Participant Maps + + + +All participants are stored under `meeting.participants`. These do not include the local user. + +The `meeting.participants` is an object that contains the following maps: + +- **`joined`** - All participants currently in the meeting (excluding the local user) +- **`waitlisted`** - All participants waiting to join the meeting +- **`active`** - All participants whose media is subscribed to (participants that should be displayed on screen) +- **`pinned`** - All pinned participants in the meeting + +If you're building a video/audio grid, you'd use the `active` map. To display a list of all participants, use the `joined` map. + +Each participant in these maps is of type `RTKParticipant`. + + + + + +The data regarding all meeting participants is stored under `meeting.participants`. These do not include the local user. + +The `meeting.participants` object contains the following maps: + +- **`joined`** - All participants currently in the meeting (excluding the local user) +- **`waitlisted`** - All participants waiting to join the meeting +- **`active`** - All participants whose media is subscribed to (participants that should be displayed on screen) +- **`pinned`** - All pinned participants in the meeting + +If you're building a video/audio grid, you'd use the `active` map. To display a list of all participants, use the `joined` map. + +Each participant in these maps is of type `RTKParticipant`. + + + + + +```js +// Get all joined participants +const joinedParticipants = meeting.participants.joined; + +// Get active participants (those on screen) +const activeParticipants = meeting.participants.active; + +// Get pinned participants +const pinnedParticipants = meeting.participants.pinned; + +// Get waitlisted participants +const waitlistedParticipants = meeting.participants.waitlisted; +``` + + + + + +Use the `useRealtimeKitSelector` hook to access participant maps: + +```jsx +import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react"; + +// Get all joined participants +const joinedParticipants = useRealtimeKitSelector((m) => m.participants.joined); + +// Get active participants (those on screen) +const activeParticipants = useRealtimeKitSelector((m) => m.participants.active); + +// Get pinned participants +const pinnedParticipants = useRealtimeKitSelector((m) => m.participants.pinned); + +// Get waitlisted participants +const waitlistedParticipants = useRealtimeKitSelector( + (m) => m.participants.waitlisted, +); +``` + + + +## Types of Grids + +The SDK offers two kinds of grids out of the box + +- **Active Grid** - Participants are automatically replaced in the grid based on who is speaking or has their video turned on. +- **Paginated Grid** - Allows users to view all participants by changing pages. Use `setPage()` to change the current page. + +The `viewMode` property indicates which grid type is currently active, It can assume two values: `ACTIVE_GRID` mode or `PAGINATED`. + +- **`ACTIVE_GRID` mode** - Participants are automatically replaced in the `meeting.participants.active`map based on their media state. +- **`PAGINATED` mode** - Participants in `meeting.participants.active` are fixed based on the page number. 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"); +``` + + + +### 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); +``` + + + +### Fetch View Mode + + + +```jsx +const viewMode = meeting.participants.viewMode; +const currentPage = meeting.participants.currentPage; +``` + + + + + +```jsx +const viewMode = useRealtimeKitSelector((m) => m.participants.viewMode); +const currentPage = useRealtimeKitSelector((m) => m.participants.currentPage); +``` + + + +## Host Controls + +The participant object allows the host, several controls. These can be selected while creating the host [preset](/api/resources/realtime_kit/subresources/presets/methods/create/). + +### Media controls + +With the correct permissions, the host can disable media for remote participants like so: + + + +```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(); +``` + + +### Waiting Room Controls + +The waiting room allows the host to control which users can join your meeting and when. They can either choose to accept or reject the request. + +You can also automate this flow so that users join the meeting automatically when the host joins the meeting, using [presets](/api/resources/realtime_kit/subresources/presets/methods/create/). + +#### Accept Waiting Room Request + + + +```js +await meeting.participants.acceptWaitingRoomRequest(participantId); +``` + + + + + +```jsx +await meeting.participants.acceptWaitingRoomRequest(participantId); +``` + + + +#### Reject Waiting Room Request + + + +```js +await meeting.participants.rejectWaitingRoomRequest(participantId); +``` + + + + + +```jsx +await meeting.participants.rejectWaitingRoomRequest(participantId); +``` + + + +#### Pin Participants + +The host can choose to pin/unpin participants to the grid. The current limit on pinning is 1 participant. + + + +```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(); +``` + + + +### Update Participant Permissions + +The host can additionally modify the permissions for a participant. Permissions for a participant are defined by their preset. + +:::note + +When the host updates the permissions for a participant, the preset is not modified and the permission changes are limited to the duration of the meeting. + +::: + +First, you need to find the participant(s) you want to update. + + + +```js +const participantIds = meeting.participants.joined + .toArray() + .filter((e) => e.name.startsWith("John")) + .map((p) => p.id); +``` + + + + + +```jsx +const participantIds = meeting.participants.joined + .toArray() + .filter((e) => e.name.startsWith("John")) + .map((p) => p.id); +``` + + + +You can use the `updatePermission` method like so to modify the permissions for the participant. + + + +```js +// Allow file upload permissions in public chat +const newPermissions = { + chat: { + public: { + files: true, + }, + }, +}; + +meeting.participants.updatePermissions(participantIds, newPermissions); +``` + + + + + +```jsx +// Allow file upload permissions in public chat +const newPermissions = { + chat: { + public: { + files: true, + }, + }, +}; + +meeting.participants.updatePermissions(participantIds, newPermissions); +``` + + + +The following permissions can be modified + + + +```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; + }; + }; +} +``` + + + + + +```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; + }; + }; +} +``` + + + +## Display Videos in your UI + +This guide will show you how to display videos in your custom grid. + +Optionally, you can use the `RtkGrid` component from the UI Kit to display participants. This component handles all grid-related events and data automatically. + +### Register Video Element + + + +To play a participant's video track on a ` + +### Deregister Video Element + + + +Clean up when the video element is no longer needed: + +```js +participant.deregisterVideoElement(videoElement); +``` + + + +### Using UI Kit Grid + + + +RealtimeKit provides ready-to-use grid components for displaying participant videos: + +```jsx +import { RtkGrid } from "@cloudflare/realtimekit-react-ui"; + +function MeetingGrid() { + return ; +} +``` + +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 + + + + + +RealtimeKit provides ready-to-use grid components for displaying participant videos: + +```typescript title="meeting-grid.component.ts" +import { Component, Input } from "@angular/core"; + +@Component({ + selector: "app-meeting-grid", + templateUrl: "./meeting-grid.component.html", +}) +export class MeetingGridComponent { + @Input() meeting: any; +} +``` + +```html title="meeting-grid.component.html" + +``` + + + + + +RealtimeKit provides ready-to-use grid components for displaying participant videos: + +```jsx + + + + + + + + + + + + + +``` + +The `rtk-grid` component handles all grid-related events and data automatically. It supports: + +- **`rtk-simple-grid`** - Renders participant tiles in a simple grid +- **`rtk-spotlight-grid`** - Renders pinned participants in a spotlight area with others in a smaller grid +- **`rtk-mixed-grid`** - Renders screenshares and plugins in the main view with a configurable smaller grid + + + +### Custom Grid using UI Kit + + + +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 `rtk-participant-tile` component. + +```typescript title="custom-video-grid.component.ts" +import { Component, Input, OnInit } from "@angular/core"; + +@Component({ + selector: "app-custom-video-grid", + templateUrl: "./custom-video-grid.component.html", +}) +export class CustomVideoGridComponent implements OnInit { + @Input() meeting: any; + + participants: any[] = []; + + ngOnInit() { + this.participants = this.meeting?.participants?.active?.toArray?.() ?? []; + } +} +``` + +```html title="custom-video-grid.component.html" +
+ +
+``` + +
+ + + +For custom video rendering, use the `rtk-participant-tile` component: + +```jsx + + + + + + + + + + + + + +``` + + diff --git a/src/content/docs/realtime/realtimekit/core/remote-participants/pip.mdx b/src/content/docs/realtime/realtimekit/core/remote-participants/pip.mdx new file mode 100644 index 000000000000000..a4a48f42d21b8ca --- /dev/null +++ b/src/content/docs/realtime/realtimekit/core/remote-participants/pip.mdx @@ -0,0 +1,72 @@ +--- +pcx_content_type: navigation +title: Picture in Picture +sidebar: + label: Picture in Picture + order: 2 +--- + +import RTKSDKSelector from "~/components/realtimekit/RTKSDKSelector/RTKSDKSelector.astro"; +import RTKCodeSnippet from "~/components/realtimekit/RTKCodeSnippet/RTKCodeSnippet.astro"; + +Picture-in-Picture API allows you to render `meeting.participants.active` participant's video as a floating tile outside of the current webpage's context. + +:::note +Supported in Chrome/Edge/Chromium-based browsers only. +::: + +### Check Support + +Picture-in-Picture API might not be supported for your browser. It is important to always check for support. + + + +```js +const isSupported = meeting.participants.pip.isSupported(); +``` + + + + + +```jsx +const isSupported = meeting.participants.pip.isSupported(); +``` + + + +### Enable Picture-in-Picture + + + +```js +await meeting.participants.pip.enable(); +``` + + + + + +```jsx +await meeting.participants.pip.enable(); +``` + + + +### Disable Picture-in-Picture + + + +```js +await meeting.participants.pip.disable(); +``` + + + + + +```jsx +await meeting.participants.pip.disable(); +``` + + diff --git a/src/content/docs/realtime/realtimekit/core/stage-management.mdx b/src/content/docs/realtime/realtimekit/core/stage-management.mdx index d2e7edcc954222d..35c2f7160aa244b 100644 --- a/src/content/docs/realtime/realtimekit/core/stage-management.mdx +++ b/src/content/docs/realtime/realtimekit/core/stage-management.mdx @@ -6,13 +6,19 @@ sidebar: order: 10 --- -import { Tabs, TabItem } from "~/components"; +import RTKSDKSelector from "~/components/realtimekit/RTKSDKSelector/RTKSDKSelector.astro"; +import RTKCodeSnippet from "~/components/realtimekit/RTKCodeSnippet/RTKCodeSnippet.astro"; This guide explains how to use stage management APIs for Webinar (WebRTC) use cases in Cloudflare RealtimeKit. -## Overview + -Instead of a traditional publish-subscribe model, where a user can publish their media and others can choose to subscribe, RealtimeKit comes with an optional managed configuration. In this managed configuration, a less privileged user can be configured with a default behavior to not publish media. The user can then request permission to be allowed to publish their media, where a privileged user can choose to grant or deny access. +Instead of a traditional publish-subscribe model, where a user can publish their +media and others can choose to subscribe, RealtimeKit comes with an optional +managed configuration. In this managed configuration, a less privileged user can +be configured with a default behavior to not publish media. The user can then +request permission to be allowed to publish their media, where a privileged user +can choose to grant or deny access. Using RealtimeKit's stage management APIs, a user can perform actions such as: @@ -22,62 +28,62 @@ Using RealtimeKit's stage management APIs, a user can perform actions such as: ## Access the Stage APIs - - - The stage module can be accessed under the `meeting.stage` namespace. + + ```js console.log("Stage object:", meeting.stage); ``` - - +
-The stage module can be accessed under the `meeting.stage` namespace. + + +```js +console.log("Stage object:", meeting.stage); +``` + + + + ```jsx console.log("Stage object:", meeting.stage); ``` - - + ## Properties - - - ### Status The `meeting.stage.status` property returns the current stage status of the local user. + + ```js console.log("Stage status:", meeting.stage.status); ``` -**Possible status values:** - -- **`ON_STAGE`** - The user is currently on the stage and sharing audio and video. -- **`OFF_STAGE`** - The user is viewing the session but is not on the stage and is not sharing audio or video. -- **`REQUESTED_TO_JOIN_STAGE`** - The user has a pending request to join the stage and share audio and video. This status remains until the host accepts or rejects the request. -- **`ACCEPTED_TO_JOIN_STAGE`** - The host has accepted the user's request to join the stage. + -:::note -A user with permission to join stage directly can only assume `ON_STAGE` and `ACCEPTED_TO_JOIN_STAGE` status values. -::: + - - +```js +console.log("Stage status:", meeting.stage.status); +``` -### Status +
-The `meeting.stage.status` property returns the current stage status of the local user. + ```jsx console.log("Stage status:", meeting.stage.status); ``` + + **Possible status values:** - **`ON_STAGE`** - The user is currently on the stage and sharing audio and video. @@ -89,95 +95,94 @@ console.log("Stage status:", meeting.stage.status); A user with permission to join stage directly can only assume `ON_STAGE` and `ACCEPTED_TO_JOIN_STAGE` status values. ::: - - - ## Host Controls RealtimeKit's stage management APIs allow hosts to receive and manage stage requests as well as leave and join the stage. - - - ### Join Stage This method connects the user to the media room, enabling them to interact with other peers in the meeting. + + ```js await meeting.stage.join(); ``` -### Leave Stage + -By employing this method, the user will be disconnected from the media room and subsequently unable to communicate with their peers. Additionally, their audio and video will no longer be visible to others in the room. + ```js -await meeting.stage.leave(); +await meeting.stage.join(); ``` -### Grant Access + -A privileged user can grant access to stage for a set of users with the `grantAccess` method. + -```js -await meeting.stage.grantAccess(userIds); +```jsx +await meeting.stage.join(); ``` -**Parameters:** + -- `userIds` (string[]) - Array of user IDs to grant stage access. You can retrieve user IDs using `meeting.participants.toArray().map(p => p.userId)` +### Leave Stage -### Deny Access +By employing this method, the user will be disconnected from the media room and subsequently unable to communicate with their peers. Additionally, their audio and video will no longer be visible to others in the room. -A privileged user can deny access to stage for a set of users with the `denyAccess` method. + ```js -await meeting.stage.denyAccess(userIds); +await meeting.stage.leave(); ``` -**Parameters:** - -- `userIds` (string[]) - Array of user IDs to deny stage access. You can retrieve user IDs using `meeting.participants.toArray().map(p => p.userId)` - -### Kick Users + -A privileged user can remove a set of users from stage using the `kick` method. + ```js -await meeting.stage.kick(userIds); +await meeting.stage.leave(); ``` -**Parameters:** + -- `userIds` (string[]) - Array of user IDs to remove from stage. You can retrieve user IDs using `meeting.participants.toArray().map(p => p.userId)` + - - +```jsx +await meeting.stage.leave(); +``` -### Join Stage +
-This method connects the user to the media room, enabling them to interact with other peers in the meeting. +### Grant Access -```jsx -await meeting.stage.join(); +A privileged user can grant access to stage for a set of users with the `grantAccess` method. + + + +```js +await meeting.stage.grantAccess(userIds); ``` -### Leave Stage + -By employing this method, the user will be disconnected from the media room and subsequently unable to communicate with their peers. Additionally, their audio and video will no longer be visible to others in the room. + -```jsx -await meeting.stage.leave(); +```js +await meeting.stage.grantAccess(userIds); ``` -### Grant Access + -A privileged user can grant access to stage for a set of users with the `grantAccess` method. + ```jsx await meeting.stage.grantAccess(userIds); ``` + + **Parameters:** - `userIds` (string[]) - Array of user IDs to grant stage access. You can retrieve user IDs using `meeting.participants.toArray().map(p => p.userId)` @@ -186,10 +191,30 @@ await meeting.stage.grantAccess(userIds); A privileged user can deny access to stage for a set of users with the `denyAccess` method. + + +```js +await meeting.stage.denyAccess(userIds); +``` + + + + + +```js +await meeting.stage.denyAccess(userIds); +``` + + + + + ```jsx await meeting.stage.denyAccess(userIds); ``` + + **Parameters:** - `userIds` (string[]) - Array of user IDs to deny stage access. You can retrieve user IDs using `meeting.participants.toArray().map(p => p.userId)` @@ -198,161 +223,193 @@ await meeting.stage.denyAccess(userIds); A privileged user can remove a set of users from stage using the `kick` method. + + +```js +await meeting.stage.kick(userIds); +``` + + + + + +```js +await meeting.stage.kick(userIds); +``` + + + + + ```jsx await meeting.stage.kick(userIds); ``` + + **Parameters:** - `userIds` (string[]) - Array of user IDs to remove from stage. You can retrieve user IDs using `meeting.participants.toArray().map(p => p.userId)` - - - ## Participant Controls RealtimeKit's stage management APIs allow participants to request and manage stage access. - - - ### Request Access This method is used to create a new stage request which can be approved by the host. Each user (viewer or host) must call this method in order to join the stage. When the host calls this method, their status will be updated to `ACCEPTED_TO_JOIN_STAGE`. + + ```js await meeting.stage.requestAccess(); ``` -### Cancel Access Request + -You can call this method to cancel your stage request. + ```js -await meeting.stage.cancelRequestAccess(); +await meeting.stage.requestAccess(); ``` - - - -### Request Access - -This method is used to create a new stage request which can be approved by the host. Each user (viewer or host) must call this method in order to join the stage. +
-When the host calls this method, their status will be updated to `ACCEPTED_TO_JOIN_STAGE`. + ```jsx await meeting.stage.requestAccess(); ``` + + ### Cancel Access Request You can call this method to cancel your stage request. + + +```js +await meeting.stage.cancelRequestAccess(); +``` + + + + + +```js +await meeting.stage.cancelRequestAccess(); +``` + + + + + ```jsx await meeting.stage.cancelRequestAccess(); ``` - - + ## Events - - - The `meeting.stage` module emits the following events: ### `stageAccessRequestUpdate` Emitted when there is an update to stage access requests. + + ```js meeting.stage.on("stageAccessRequestUpdate", (data) => { console.log("Stage access request updated:", data); }); ``` -### `acceptPresentRequests` + -Emitted when present requests are accepted. + ```js -meeting.stage.on("acceptPresentRequests", (data) => { - console.log("Present requests accepted:", data); +meeting.stage.on("stageAccessRequestUpdate", (data) => { + console.log("Stage access request updated:", data); }); ``` -### `stageStatusUpdate` + -Emitted when the stage status changes. + -```js -meeting.stage.on("stageStatusUpdate", (status) => { - console.log("Stage status updated:", status); +```jsx +meeting.stage.on("stageAccessRequestUpdate", (data) => { + console.log("Stage access request updated:", data); }); ``` -### `newStageRequest` + -Emitted when a new stage request is received. +### `acceptPresentRequests` + +Emitted when present requests are accepted. + + ```js -meeting.stage.on("newStageRequest", (request) => { - console.log("New stage request:", request); +meeting.stage.on("acceptPresentRequests", (data) => { + console.log("Present requests accepted:", data); }); ``` -### `stageRequestApproved` + -Emitted when a stage request is approved. + ```js -meeting.stage.on("stageRequestApproved", (data) => { - console.log("Stage request approved:", data); +meeting.stage.on("acceptPresentRequests", (data) => { + console.log("Present requests accepted:", data); }); ``` -### `stageRequestRejected` + -Emitted when a stage request is rejected. + -```js -meeting.stage.on("stageRequestRejected", (data) => { - console.log("Stage request rejected:", data); +```jsx +meeting.stage.on("acceptPresentRequests", (data) => { + console.log("Present requests accepted:", data); }); ``` - - +
-The `meeting.stage` module emits the following events: +### `stageStatusUpdate` -### `stageAccessRequestUpdate` +Emitted when the stage status changes. -Emitted when there is an update to stage access requests. + -```jsx -meeting.stage.on("stageAccessRequestUpdate", (data) => { - console.log("Stage access request updated:", data); +```js +meeting.stage.on("stageStatusUpdate", (status) => { + console.log("Stage status updated:", status); }); ``` -### `acceptPresentRequests` + -Emitted when present requests are accepted. + -```jsx -meeting.stage.on("acceptPresentRequests", (data) => { - console.log("Present requests accepted:", data); +```js +meeting.stage.on("stageStatusUpdate", (status) => { + console.log("Stage status updated:", status); }); ``` -### `stageStatusUpdate` + -Emitted when the stage status changes. + ```jsx meeting.stage.on("stageStatusUpdate", (status) => { @@ -360,37 +417,115 @@ meeting.stage.on("stageStatusUpdate", (status) => { }); ``` + + ### `newStageRequest` Emitted when a new stage request is received. + + +```js +meeting.stage.on("newStageRequest", (request) => { + console.log("New stage request:", request); +}); +``` + + + + + +```js +meeting.stage.on("newStageRequest", (request) => { + console.log("New stage request:", request); +}); +``` + + + + + ```jsx meeting.stage.on("newStageRequest", (request) => { console.log("New stage request:", request); }); ``` + + ### `stageRequestApproved` Emitted when a stage request is approved. + + +```js +meeting.stage.on("stageRequestApproved", (data) => { + console.log("Stage request approved:", data); +}); +``` + + + + + +```js +meeting.stage.on("stageRequestApproved", (data) => { + console.log("Stage request approved:", data); +}); +``` + + + + + ```jsx meeting.stage.on("stageRequestApproved", (data) => { console.log("Stage request approved:", data); }); ``` + + ### `stageRequestRejected` Emitted when a stage request is rejected. + + +```js +meeting.stage.on("stageRequestRejected", (data) => { + console.log("Stage request rejected:", data); +}); +``` + + + + + +```js +meeting.stage.on("stageRequestRejected", (data) => { + console.log("Stage request rejected:", data); +}); +``` + + + + + ```jsx meeting.stage.on("stageRequestRejected", (data) => { console.log("Stage request rejected:", data); }); ``` -Alternatively, you can use React hooks to listen for stage updates: + + +### React hooks + + + +You can also use React hooks to listen for stage updates: ```jsx import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react"; @@ -399,5 +534,10 @@ import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react"; const stageStatus = useRealtimeKitSelector((m) => m.stage.status); ``` - - + + + + +Hooks are not available for this framework. + + diff --git a/src/content/docs/realtime/realtimekit/core/video-background.mdx b/src/content/docs/realtime/realtimekit/core/video-effects.mdx similarity index 95% rename from src/content/docs/realtime/realtimekit/core/video-background.mdx rename to src/content/docs/realtime/realtimekit/core/video-effects.mdx index 8345711a6e70019..e449ac92323ee3c 100644 --- a/src/content/docs/realtime/realtimekit/core/video-background.mdx +++ b/src/content/docs/realtime/realtimekit/core/video-effects.mdx @@ -1,12 +1,13 @@ --- pcx_content_type: content -title: Video Background -slug: realtime/realtimekit/core/video-background +title: Video Effects sidebar: order: 14 --- -import { Tabs, TabItem, PackageManagers } from "~/components"; +import { PackageManagers } from "~/components"; +import RTKSDKSelector from "~/components/realtimekit/RTKSDKSelector/RTKSDKSelector.astro"; +import RTKCodeSnippet from "~/components/realtimekit/RTKCodeSnippet/RTKCodeSnippet.astro"; Add video background effects and blur to participant video feeds in your RealtimeKit meetings using the Core SDK. @@ -14,8 +15,9 @@ Add video background effects and blur to participant video feeds in your Realtim If you are using the `rtk-meeting` component with UI Kit and prefer a higher-level abstraction, refer to [UI Kit Addons](/realtime/realtimekit/ui-kit/addons/) instead. ::: - - + + + ## Installation @@ -116,8 +118,8 @@ const videoBackgroundTransformer = }); ``` - - +
+ ## Installation @@ -226,8 +228,8 @@ const transformer = await RealtimeKitVideoBackgroundTransformer.init({ }); ``` - - + + ## Installation @@ -338,8 +340,8 @@ this.videoBackgroundTransformer = }); ``` - - + + ## Installation @@ -422,5 +424,4 @@ val meeting = RealtimeKitMeetingBuilder .build(activity) ``` - - + diff --git a/src/content/docs/realtime/realtimekit/core/waiting-room.mdx b/src/content/docs/realtime/realtimekit/core/waiting-room.mdx index 09015dbe5831c1d..ff38dcebfb35191 100644 --- a/src/content/docs/realtime/realtimekit/core/waiting-room.mdx +++ b/src/content/docs/realtime/realtimekit/core/waiting-room.mdx @@ -9,12 +9,14 @@ sidebar: import RTKSDKSelector from "~/components/realtimekit/RTKSDKSelector/RTKSDKSelector.astro"; import RTKCodeSnippet from "~/components/realtimekit/RTKCodeSnippet/RTKCodeSnippet.astro"; -The waiting room feature allows hosts to control who can join a meeting. When enabled, participants must wait for approval before entering the meeting. - :::note[Prerequisites] This page assumes you've already initialized the SDK and understand the meeting object structure. Refer to [Initialize SDK](/realtime/realtimekit/core/) and [Meeting Object Explained](/realtime/realtimekit/core/meeting-object-explained/) if needed. ::: +The waiting room feature allows hosts to control who can join a meeting. When enabled, participants must wait for approval before entering the meeting. + + + ## How the Waiting Room Works After you call `meeting.join()`, one of two events will occur: @@ -44,8 +46,6 @@ The diagram below represents only waiting room-related states. The `roomState` p ## Listening to State Changes - - ### Joined Event Triggered when the local user successfully joins the meeting. @@ -94,7 +94,7 @@ useEffect(() => {
- + ```js meeting.self.on("roomJoined", () => { @@ -231,7 +231,7 @@ useEffect(() => { - + ```js meeting.self.on("waitlisted", () => { @@ -393,7 +393,7 @@ useEffect(() => { - + ```js meeting.self.on("roomLeft", ({ state }) => { @@ -564,7 +564,7 @@ function WaitingRoomManager() { - + ```js const currentState = meeting.self.roomState; @@ -683,7 +683,7 @@ function WaitingRoomHost() { - + ```js // Get waitlisted participants diff --git a/src/content/docs/realtime/realtimekit/faq.mdx b/src/content/docs/realtime/realtimekit/faq.mdx index e0532673fc3ab95..61e1fd400584f94 100644 --- a/src/content/docs/realtime/realtimekit/faq.mdx +++ b/src/content/docs/realtime/realtimekit/faq.mdx @@ -3,7 +3,7 @@ pcx_content_type: navigation title: FAQ slug: realtime/realtimekit/faq sidebar: - order: 10 + order: 11 --- import { Details } from "~/components"; diff --git a/src/content/docs/realtime/realtimekit/legal/index.mdx b/src/content/docs/realtime/realtimekit/legal/index.mdx index 83f273fa58126c9..ed3ac3f2d7ad318 100644 --- a/src/content/docs/realtime/realtimekit/legal/index.mdx +++ b/src/content/docs/realtime/realtimekit/legal/index.mdx @@ -3,7 +3,7 @@ pcx_content_type: navigation title: Legal hideChildren: true sidebar: - order: 12 + order: 15 --- - [Privacy Policy](https://www.cloudflare.com/application/privacypolicy/) diff --git a/src/content/docs/realtime/realtimekit/pricing.mdx b/src/content/docs/realtime/realtimekit/pricing.mdx index 6a84b17bff307ca..455ce25dc0a3306 100644 --- a/src/content/docs/realtime/realtimekit/pricing.mdx +++ b/src/content/docs/realtime/realtimekit/pricing.mdx @@ -3,7 +3,7 @@ pcx_content_type: content title: Pricing slug: realtime/realtimekit/pricing sidebar: - order: 11 + order: 14 --- Cloudflare RealtimeKit is currently in Beta and is available at no cost during this period. diff --git a/src/content/docs/realtime/realtimekit/quickstart.mdx b/src/content/docs/realtime/realtimekit/quickstart.mdx index bb8855317c204cc..6fffd76ce0938cf 100644 --- a/src/content/docs/realtime/realtimekit/quickstart.mdx +++ b/src/content/docs/realtime/realtimekit/quickstart.mdx @@ -5,7 +5,7 @@ sidebar: order: 2 --- -import { Tabs, TabItem, Steps, Render } from "~/components"; +import { Render } from "~/components"; import RTKSDKSelector from "~/components/realtimekit/RTKSDKSelector/RTKSDKSelector.astro"; import RTKCodeSnippet from "~/components/realtimekit/RTKCodeSnippet/RTKCodeSnippet.astro"; diff --git a/src/content/docs/realtime/realtimekit/release-notes/index.mdx b/src/content/docs/realtime/realtimekit/release-notes/index.mdx index b501e9bcdad33bc..30d44c51fb5554a 100644 --- a/src/content/docs/realtime/realtimekit/release-notes/index.mdx +++ b/src/content/docs/realtime/realtimekit/release-notes/index.mdx @@ -5,7 +5,7 @@ release_notes_file_name: - realtimekit-web-core sidebar: label: Web Core SDK - order: 10 + order: 12 --- import { ProductReleaseNotes } from "~/components"; diff --git a/src/content/docs/realtime/realtimekit/ui-kit/index.mdx b/src/content/docs/realtime/realtimekit/ui-kit/index.mdx index 154bd31e2c01c73..8bd78391133949e 100644 --- a/src/content/docs/realtime/realtimekit/ui-kit/index.mdx +++ b/src/content/docs/realtime/realtimekit/ui-kit/index.mdx @@ -3,7 +3,7 @@ pcx_content_type: navigation title: Build using UI Kit sidebar: order: 5 - label: Basic Implementation + label: Getting Started --- import { Tabs, TabItem, Render } from "~/components";