diff --git a/.github/workflows/docusaurus-javascript-deploy.yml b/.github/workflows/docusaurus-javascript-deploy.yml deleted file mode 100644 index 23c12b8bd6..0000000000 --- a/.github/workflows/docusaurus-javascript-deploy.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Push JavaScript docusaurus documentation - -on: - push: - paths: - - 'packages/client/docusaurus/**' - - '.github/workflows/docusaurus-javascript-deploy.yml' - -jobs: - push_docusaurus: - name: Build and deploy the documentation - timeout-minutes: 20 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Node Setup - uses: actions/setup-node@v4 - with: - node-version: 20.x - - - name: Push JavaScript documentation - uses: GetStream/push-stream-chat-docusaurus-action@main - with: - target-branch: ${{ github.ref == 'refs/heads/main' && 'main' || 'staging' }} - cli-target-branch: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }} - destination-repository-name: 'stream-video-docusaurus' - source-directory: 'packages/client/docusaurus' - env: - DOCUSAURUS_GH_TOKEN: ${{ secrets.DOCUSAURUS_GH_TOKEN }} diff --git a/.github/workflows/docusaurus-react-deploy.yml b/.github/workflows/docusaurus-react-deploy.yml deleted file mode 100644 index 1ed48774b9..0000000000 --- a/.github/workflows/docusaurus-react-deploy.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Push React docusaurus documentation - -on: - push: - paths: - - 'packages/react-sdk/docusaurus/**' - - '.github/workflows/docusaurus-react-deploy.yml' - -jobs: - push_docusaurus: - name: Build and deploy the documentation - timeout-minutes: 20 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Node Setup - uses: actions/setup-node@v4 - with: - node-version: 20.x - - - name: Push React SDK documentation - uses: GetStream/push-stream-chat-docusaurus-action@main - with: - target-branch: ${{ github.ref == 'refs/heads/main' && 'main' || 'staging' }} - cli-target-branch: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }} - destination-repository-name: 'stream-video-docusaurus' - source-directory: 'packages/react-sdk/docusaurus' - env: - DOCUSAURUS_GH_TOKEN: ${{ secrets.DOCUSAURUS_GH_TOKEN }} diff --git a/.github/workflows/docusaurus-react-native-deploy.yml b/.github/workflows/docusaurus-react-native-deploy.yml deleted file mode 100644 index d9c81c8158..0000000000 --- a/.github/workflows/docusaurus-react-native-deploy.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Push React Native docusaurus documentation - -on: - push: - paths: - - 'packages/react-native-sdk/docusaurus/**' - - '.github/workflows/docusaurus-react-native-deploy.yml' - -jobs: - push_docusaurus: - name: Build and deploy the documentation - timeout-minutes: 20 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Node Setup - uses: actions/setup-node@v4 - with: - node-version: 20.x - - - name: Push React Native SDK documentation - uses: GetStream/push-stream-chat-docusaurus-action@main - with: - target-branch: ${{ github.ref == 'refs/heads/main' && 'main' || 'staging' }} - cli-target-branch: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }} - destination-repository-name: 'stream-video-docusaurus' - source-directory: 'packages/react-native-sdk/docusaurus' - env: - DOCUSAURUS_GH_TOKEN: ${{ secrets.DOCUSAURUS_GH_TOKEN }} diff --git a/packages/client/docusaurus/.env b/packages/client/docusaurus/.env deleted file mode 100644 index e9f70f9cf4..0000000000 --- a/packages/client/docusaurus/.env +++ /dev/null @@ -1 +0,0 @@ -PRODUCT=video diff --git a/packages/client/docusaurus/docs/javascript/01-basics/01-introduction.mdx b/packages/client/docusaurus/docs/javascript/01-basics/01-introduction.mdx deleted file mode 100644 index a9e4010685..0000000000 --- a/packages/client/docusaurus/docs/javascript/01-basics/01-introduction.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Introduction -description: Introduction about Stream's JavaScript SDK -slug: / ---- - -Welcome to the Stream Video JavaScript SDK - a comprehensive toolkit designed to help you swiftly implement features such as video calling, audio calling, audio rooms, and livestreaming within your app. -Our goal is to ensure an optimal developer experience that speeds up your development process. - -Our JavaScript SDK comes with high-level APIs and reactive state management, making your development process seamless. -Moreover, all calls are routed through Stream's global edge network, thereby ensuring lower latency and higher reliability due to proximity to end users. - -It can be used with TypeScript and JavaScript as well. - -If you're new to Stream Video JavaScript SDK, we recommend starting with the following tutorials: - -- [Quickstart](./basics/quickstart) -- [Video and Audio Calling Tutorial](https://getstream.io/video/sdk/javascript/tutorial/video-calling/) -- [Audio Rooms Tutorial](https://getstream.io/video/sdk/javascript/tutorial/audio-room/) -- [Livestream Tutorial](https://getstream.io/video/sdk/javascript/tutorial/livestreaming/) - -After the tutorials the documentation explains how to use core concepts such as initiating a call, switching the camera view, and more. - -It also explains advanced features such as: - -- Recording -- Broadcasting -- Requesting & Granting permissions -- Audio & Video Filters - -If you feel like anything is missing or could be improved, please don't hesitate to [contact us](https://getstream.io/contact/). We're happy to help. diff --git a/packages/client/docusaurus/docs/javascript/01-basics/02-installation.mdx b/packages/client/docusaurus/docs/javascript/01-basics/02-installation.mdx deleted file mode 100644 index 3486faa68d..0000000000 --- a/packages/client/docusaurus/docs/javascript/01-basics/02-installation.mdx +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: Installation & Size -description: Installation instructions and bundle size impact for the Plain-JS Video SDK ---- - -The installation of the packages for the Stream Video Plain-JS SDK can be either done with `yarn`, `npm` or any other package manager. - -For `yarn`: - -```bash -yarn add @stream-io/video-client -``` - -For `npm`: - -```bash -npm install @stream-io/video-client -``` - -## SDK Size - -The SDK size after minification is below 370KB. This size includes all the functionalities provided by the SDK. -After g-zipping, the SDK size can be compressed down to ~100KB. - -For most up-to-date information on the SDK size, please check the [package size](https://bundlephobia.com/result?p=@stream-io/video-client) on Bundlephobia. diff --git a/packages/client/docusaurus/docs/javascript/01-basics/03-quickstart.mdx b/packages/client/docusaurus/docs/javascript/01-basics/03-quickstart.mdx deleted file mode 100644 index 2797623ba7..0000000000 --- a/packages/client/docusaurus/docs/javascript/01-basics/03-quickstart.mdx +++ /dev/null @@ -1,295 +0,0 @@ ---- -title: Quickstart -description: How to Build a Web Video Calling App ---- - -import { TokenSnippet } from '../../../shared/_tokenSnippet.jsx'; - -This tutorial gives you a quick overview of how Stream's video JavaScript client works. -The code snippets use TypeScript, but you can use the library with JavaScript as well. - -## Client setup & Calls - -Create an instance of `StreamVideoClient` that will establish WebSocket connection by connecting a user. Next you create a call object and join the call. We'll specify `create: true` to create the call if it doesn't exist. - -```typescript -import { StreamVideoClient, User } from '@stream-io/video-client'; - -const apiKey = 'your-api-key'; -const token = 'authentication-token'; -const user: User = { id: 'user-id' }; - -const client = new StreamVideoClient({ apiKey, token, user }); -const call = client.call('default', 'call-id'); -call.join({ create: true }); -``` - -`default` is a call type. There are 4 built-in [call types](../../guides/configuring-call-types/) and you can also create your own. The call type controls the permissions and which features are enabled. - -The second argument is the call id. Call ids can be reused, meaning that it's possible to join a call with the same id multiple times (for example, for recurring meetings). - -For an easy setup, you can copy the following credentials to use for the tutorial. It also helps with joining multiple participants: - - - -## Publish audio and video - -Once we join a call, we can start publishing audio and video: - -```typescript -await call.join({ create: true }); - -await call.camera.enable(); -await call.microphone.enable(); -``` - -Or, if you wish to join a call with both, audio and video muted: - -```typescript -await call.microphone.disable(); -await call.camera.disable(); - -await call.join({ create: true }); -``` - -Please note that if the integrator omits enabling or disabling the camera and microphone, -our SDK will respect the [Call Type Settings](../../guides/configuring-call-types/) as defined in the dashboard. - -More information about this topic can be found in the [Camera & Microphone guide](../../guides/camera-and-microphone). - -## Render video and play audio - -The JavaScript client provides [reactive state management](https://rxjs.dev/), which makes it easy to trigger UI updates. To render the audio and video of participants, you can watch for changes on `call.state.participants$`, here is the full example: - -```typescript -import { renderParticipant, cleanupParticipant } from './participant'; - -const parentContainer = document.getElementById('participants')!; - -// This will enable visibility tracking in the client -call.setViewport(parentContainer); - -// Whenever the participants change, we update the UI -call.state.participants$.subscribe((participants) => { - // render / update existing participants - participants.forEach((participant) => { - renderParticipant(call, participant, parentContainer); - }); - - // If you're using a web framework, this part is handled by the framework - // Remove stale elements for stale participants - parentContainer - .querySelectorAll('video, audio') - .forEach((el) => { - const sessionId = el.dataset.sessionId!; - const participant = participants.find((p) => p.sessionId === sessionId); - if (!participant) { - cleanupParticipant(sessionId); - el.remove(); - } - }); -}); -``` - -Now let's see what happens in the `renderParticipant` method, because that's what does the heavy lifting: - -```typescript -import { Call, StreamVideoParticipant } from '@stream-io/video-client'; - -// The quickstart uses fixed video dimensions for simplification -const videoDimension = { - width: 333, - height: 250, -}; - -const videoBindingsCache = new Map(); -const videoTrackingCache = new Map(); -const audioBindingsCache = new Map(); - -const renderVideo = ( - call: Call, - participant: StreamVideoParticipant, - parentContainer: HTMLElement, -) => { - const id = `video-${participant.sessionId}`; - let videoEl = document.getElementById(id) as HTMLVideoElement | null; - if (!videoEl) { - videoEl = document.createElement('video'); - videoEl.style.setProperty('object-fit', 'contain'); - videoEl.id = `video-${participant.sessionId}`; - videoEl.width = videoDimension.width; - videoEl.height = videoDimension.height; - videoEl.dataset.sessionId = participant.sessionId; - - parentContainer.appendChild(videoEl); - - const untrack = call.trackElementVisibility( - videoEl, - participant.sessionId, - 'videoTrack', - ); - - // keep reference to untrack function to call it later - videoTrackingCache.set(id, untrack); - - // registers subscription updates and stream changes - const unbind = call.bindVideoElement( - videoEl, - participant.sessionId, - 'videoTrack', - ); - - // keep reference to unbind function to call it later - videoBindingsCache.set(id, unbind); - } -}; - -const renderAudio = ( - call: Call, - participant: StreamVideoParticipant, - parentContainer: HTMLElement, -) => { - // We don't render audio for local participant - if (participant.isLocalParticipant) return; - - const id = `audio-${participant.sessionId}`; - let audioEl = document.getElementById(id) as HTMLAudioElement | null; - if (!audioEl) { - audioEl = document.createElement('audio'); - audioEl.id = id; - audioEl.dataset.sessionId = participant.sessionId; - - parentContainer.appendChild(audioEl); - - // registers subscription updates and stream changes for audio - const unbind = call.bindAudioElement(audioEl, participant.sessionId); - - // keep reference to unbind function to call it later - audioBindingsCache.set(id, unbind); - } -}; - -export const renderParticipant = ( - call: Call, - participant: StreamVideoParticipant, - parentContainer: HTMLElement, -) => { - renderAudio(call, participant, parentContainer); - renderVideo(call, participant, parentContainer); -}; -``` - -The most important parts are: - -- `call.trackElementVisibility`: this will enable the client to detect if a particpant's `video` element isn't visible on the screen, in which case it will stop requesting the video, saving bandwidth -- `call.bindVideoElement` will bind the given participants video stream to the given `video` element, and takes care of stream changes and resizes -- `call.bindAudioElement` will bind the given participants audio stream to the given `audio` element, and takes care of stream changes - -For more information check out these guides: - -- [Participant visibility tracking](../../guides/visibility-tracking) -- [Playing Video and Audio](../../guides/playing-video-and-audio) - -When a participant leaves the call, we need to unbind their `video` and `audio` elements. - -```typescript -// If you're using a web framework, unbinding is usually handled with a component lifecycle event -export const cleanupParticipant = (sessionId: string) => { - const unbindVideo = videoBindingsCache.get(`video-${sessionId}`); - if (unbindVideo) { - unbindVideo(); - videoBindingsCache.delete(`video-${sessionId}`); - } - - const untrackVideo = videoTrackingCache.get(`video-${sessionId}`); - if (untrackVideo) { - untrackVideo(); - videoTrackingCache.delete(`video-${sessionId}`); - } - - const unbindAudio = audioBindingsCache.get(`audio-${sessionId}`); - if (unbindAudio) { - unbindAudio(); - audioBindingsCache.delete(`audio-${sessionId}`); - } -}; -``` - -If you're using a web framework, you can usually use a component lifecycle event to trigger the unbinding. - -More information about state management can be found in the [Call & Participant State guide](../../guides/call-and-participant-state). - -## Camera & Microphone - -Most video apps will show buttons to mute/unmute the audio or video. - -```tsx -const controls = renderControls(call); -const container = document.getElementById('call-controls')!; -container.appendChild(controls.audioButton); -container.appendChild(controls.videoButton); -``` - -This is how the `renderControls` method looks like: - -```typescript -import { Call } from '@stream-io/video-client'; - -const renderAudioButton = (call: Call) => { - const audioButton = document.createElement('button'); - - audioButton.addEventListener('click', async () => { - await call.microphone.toggle(); - }); - - call.microphone.state.status$.subscribe((status) => { - audioButton.innerText = - status === 'enabled' ? 'Turn off mic' : 'Turn on mic'; - }); - - return audioButton; -}; - -const renderVideoButton = (call: Call) => { - const videoButton = document.createElement('button'); - - videoButton.addEventListener('click', async () => { - await call.camera.toggle(); - }); - - call.camera.state.status$.subscribe((status) => { - videoButton.innerText = - status === 'enabled' ? 'Turn off camera' : 'Turn on camera'; - }); - - return videoButton; -}; - -export const renderControls = (call: Call) => { - return { - audioButton: renderAudioButton(call), - videoButton: renderVideoButton(call), - }; -}; -``` - -More information about this topic can be found in the [Camera & Microphone guide](../../guides/camera-and-microphone). - -## See it in action - -We have prepared a [CodeSandbox example](https://codesandbox.io/s/javascript-quickstart-99th3v) -that demonstrates the above steps in action. - - diff --git a/packages/client/docusaurus/docs/javascript/01-basics/04-supported-platforms.mdx b/packages/client/docusaurus/docs/javascript/01-basics/04-supported-platforms.mdx deleted file mode 100644 index 8039276193..0000000000 --- a/packages/client/docusaurus/docs/javascript/01-basics/04-supported-platforms.mdx +++ /dev/null @@ -1,15 +0,0 @@ ---- -title: Supported Platforms and Size -description: A list of supported platforms and SDK size information ---- - -Our Plain-JS Video SDK can run on all modern browsers and platforms. Here is a list of supported platforms: - -- Chrome 91+ -- Firefox 89+ -- Edge 91+ -- Safari and Mobile Safari 15+ - -## WebView - -Our SDK can run in Web View on Android and iOS. However, we recommend using the SDK in a browser for the best experience. diff --git a/packages/client/docusaurus/docs/javascript/01-basics/_category_.json b/packages/client/docusaurus/docs/javascript/01-basics/_category_.json deleted file mode 100644 index ece9ba735d..0000000000 --- a/packages/client/docusaurus/docs/javascript/01-basics/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "Setup" -} diff --git a/packages/client/docusaurus/docs/javascript/02-guides/01-client-auth.mdx b/packages/client/docusaurus/docs/javascript/02-guides/01-client-auth.mdx deleted file mode 100644 index ba20ad8bb0..0000000000 --- a/packages/client/docusaurus/docs/javascript/02-guides/01-client-auth.mdx +++ /dev/null @@ -1,153 +0,0 @@ ---- -id: client-auth -title: Client & Authentication -description: How to setup the client and authenticate ---- - -import { TokenSnippet } from '../../../shared/_tokenSnippet.jsx'; - -## Client & Auth - -Before joining a call, it is necessary to set up the video client. Here's a basic example: - -```ts -import { StreamVideoClient, User } from '@stream-io/video-client'; - -const user: User = { - id: 'sara', -}; - -const apiKey = 'my-stream-api-key'; -const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'; -const client = new StreamVideoClient({ apiKey, token, user }); -``` - -- The API Key can be found in your dashboard. -- The user can be either authenticated, anonymous or guest. -- Note: You can store custom data on the user object, if required. - -Typically, you'll want to initialize the client when your application loads and store it in your application state to make it available to the rest of your application. - -## Generating a token - -Tokens need to be generated server side. You can use our [server side SDKs](https://getstream.io/video/docs/api/authentication/) to quickly add support for this. -Typically, you integrate this into the part of your codebase where you login or register users. -The tokens provide a way to authenticate a user or give access to a specific set of calls. - - - -:::note -For development purposes, you can use our [Token Generator](https://getstream.io/chat/docs/token_generator/). -::: - -## Different types of users - -- Authenticated users are users that have an account on your app. -- Guest users are temporary user accounts. You can use it to temporarily give someone a name and image when joining a call. -- Anonymous users are users that are not authenticated. It's common to use this for watching a livestream or similar where you aren't authenticated. - -This example shows the client setup for a guest user: - -```ts -import { StreamVideoClient, User } from '@stream-io/video-client'; - -const user: User = { - id: 'jack-guest', - type: 'guest', -}; - -const apiKey = 'my-stream-api-key'; -const client = new StreamVideoClient({ apiKey, user }); -``` - -And here's an example for an anonymous user - -```ts -import { StreamVideoClient, User } from '@stream-io/video-client'; - -const user: User = { - type: 'anonymous', -}; - -const apiKey = 'my-stream-api-key'; -const client = new StreamVideoClient({ apiKey, user }); -``` - -## Client options - -### `token` or `tokenProvider` - -To authenticate users you can either provide a string `token` or a `tokenProvider` function that returns `Promise`. -If you use the `tokenProvider` the SDK will automatically call the provider whenever the token is expired. - -```typescript -import { StreamVideoClient, User } from '@stream-io/video-client'; - -const tokenProvider = async () => { - const response = await fetch('/api/token'); - const data = await response.json(); - return data.token; -}; - -const user: User = { - id: 'sara', -}; -const apiKey = 'my-stream-api-key'; -const client = new StreamVideoClient({ apiKey, tokenProvider, user }); -``` - -### Logging - -You can configure the log level and the logger method used by the SDK. - -The default log level is `warn`, other options are: `debug`, `info`, and `error`. - -The default logger method will log to the browser `console`. - -```ts -import { StreamVideoClient, Logger } from '@stream-io/video-client'; - -const myLogger: Logger; - -const client = new StreamVideoClient({ - apiKey, - token, - user, - options: { - logLevel: 'info', - logger: myLogger, - }, -}); -``` - -### Sentry - -Here is an example showing a basic [Sentry](https://sentry.io/welcome/) integration: - -```ts -import { LogLevel, Logger, logToConsole } from '@stream-io/video-client'; -import * as Sentry from '@sentry/nextjs'; - -const logLevelMapping = new Map(); -logLevelMapping.set('debug', 'debug'); -logLevelMapping.set('info', 'info'); -logLevelMapping.set('warn', 'warning'); -logLevelMapping.set('error', 'error'); - -export const customSentryLogger: Logger = ( - logLevel: LogLevel, - message: string, - extraData?: any, - tags?: string[], -) => { - if (logLevel === 'warn' || logLevel === 'error') { - Sentry.captureEvent({ - level: logLevelMapping.get(logLevel), - extra: extraData, - }); - } - - // Call the SDK's default log method - logToConsole(logLevel, message, extraData, tags); -}; -``` diff --git a/packages/client/docusaurus/docs/javascript/02-guides/02-joining-creating-calls.mdx b/packages/client/docusaurus/docs/javascript/02-guides/02-joining-creating-calls.mdx deleted file mode 100644 index 2497ec272c..0000000000 --- a/packages/client/docusaurus/docs/javascript/02-guides/02-joining-creating-calls.mdx +++ /dev/null @@ -1,278 +0,0 @@ ---- -id: joining-and-creating-calls -title: Joining & Creating Calls -description: An overview of how to create calls and join them ---- - -This guide shows how to create, join, leave, and end calls and ring calls. - -## Call - -`Call` represents the main building block of our SDK. This object abstracts away the user actions, join flows and exposes the call state. - -### Create call - -You can create a call by specifying its `callType` and `callId`: - -The [Call Type](../../guides/configuring-call-types) controls which features are enabled, and sets up permissions. -You can reuse the same call multiple times. As an example, if you're building a telemedicine app calls will be connected to an appointment. -Using your own appointment id as the call id makes it easy to find the call later. - -```typescript -const callType = 'default'; -const callId = 'test-call'; - -const call = client.call(callType, callId); -await call.getOrCreate(); - -// or create it with options: -await call.getOrCreate({ - data: { - /* call creation options */ - }, -}); -``` - -See all possible options at the [Call creation options section](#call-creation-options). - -### Join call - -```typescript -const callType = 'default'; -const callId = 'test-call'; - -const call = client.call(callType, callId); -await call.join(); -``` - -### Create and join a call - -For convenience, you can create and join a call in a single operation. One of the flags you can provide there is `create`. -Set this to `true` if you want to enable creating new call. Set it to `false` if you only want to join an existing call. - -See all possible options at the [Call creation options section](#call-creation-options). - -```typescript -await call.join({ - create: true, - data: { - /* call creation options */ - }, -}); -``` - -### Join with mic and camera on or off - -You can override the default mic and camera settings before you join a call. -Typically, you should configure this in your Lobby view: - -```typescript -const call = client.call('default', 'test-call'); - -// enable mic and camera -await call.camera.enable(); -await call.microphone.enable(); - -// alternatively, you can disable them -await call.camera.disable(); -await call.microphone.disable(); - -// and then join the call -await call.join(); -``` - -### Leave call - -To leave a call, you can use the `leave` method: - -```typescript -await call.leave(); -``` - -### End call - -Ending a call requires a [special permission](../../guides/permissions-and-moderation). This action terminates the call for everyone. - -```typescript -await call.endCall(); -``` - -Only users with a special permission (`OwnCapability.JOIN_ENDED_CALL`) can join an ended call. - -### Load call - -Existing calls can be loaded through the following API: - -```typescript -const call = client.call(type, id); -await call.get(); // just load - -await call.getOrCreate(); // create if not present and load it -``` - -These operations initialize the `call.state` and create a subscription for call updates to our backend. -This means that this `call` instance will receive real-time updates in case it is modified somewhere else. - -Read more about call state here: [Call & Participant State](../../guides/call-and-participant-state/). - -### Update call - -After creating a call, you can update some of its properties: - -```typescript -import { RecordSettingsRequestModeEnum } from '@stream-io/video-client'; - -await call.update({ - custom: { color: 'green' }, - settings_override: { - recording: { - mode: RecordSettingsRequestModeEnum.DISABLED, - }, - }, -}); -``` - -## Call creation options - -The following options are supported when creating a call: - -| Option | Description | Default | -| ---------- | --------------------------------------------------------------------------------------------------------------- | ------- | -| `members` | A list of members to add to this call. You can specify the role and custom data on these members | - | -| `custom` | Any custom data you want to store | - | -| `settings` | You can overwrite certain call settings for this specific call. This overwrites the call type standard settings | - | -| `startsAt` | When the call will start. Used for calls scheduled in the future, livestreams, audio rooms etc | - | -| `team` | Restrict the access to this call to a specific team | - | -| `ring` | If you want the call to ring for each member | `false` | -| `notify` | If you want the call to nofiy each member by sending push notification. | `false` | - -### Set call members - -```typescript -const call = client.call(type, id); -await call.getOrCreate({ - data: { - members: [{ user_id: 'alice', role: 'admin' }, { user_id: 'bob' }], - }, -}); -``` - -### Update call members - -```typescript -await call.updateCallMembers({ - update_members: [{ user_id: 'charlie', role: 'admin' }], - remove_members: ['alice'], -}); -``` - -### Custom call data - -```typescript -await call.getOrCreate({ - data: { - custom: { color: 'blue' }, - }, -}); -``` - -### Settings override - -By default, the `call` instances inherit the settings defined in the call type. -In some cases, you might want to override call settings on the instance itself: - -```typescript -// at creation time -await call.getOrCreate({ - data: { - settings_override: { - audio: { mic_default_on: false }, - video: { camera_default_on: false }, - }, - }, -}); - -// or later -await call.update({ - settings_override: { - video: { camera_default_on: true }, - }, -}); -``` - -### Backstage setup - -The backstage feature makes it easy to build a use-case where you and your co-hosts can set up your camera before going live. -Only after you call `call.goLive()` the regular users will be allowed to join the livestream. - -However, you can also specify a `join_ahead_time_seconds`, -which will allow regular users to join the livestream before the call is live, in the specified join time before the stream starts. - -Here's an example of how to do that: - -```typescript -await call.getOrCreate({ - data: { - starts_at: new Date(Date.now() + 500 * 1000), // 500 seconds from now - settings_override: { - backstage: { - enabled: true, - join_ahead_time_seconds: 300, - }, - }, - }, -}); -``` - -In the code snippet above, we are creating a call that starts 500 seconds from now. -We are also enabling backstage mode, with a `join_ahead_time_seconds` of 300 seconds. -That means that regular users will be able to join the call 200 seconds from now. - -## Restricting access - -You can restrict access to a call by tweaking the [Call Type](../../guides/configuring-call-types/) permissions and roles. -A typical use case is to restrict access to a call to a specific set of users -> call members. - -#### Step 1: Set up the roles and permissions - -On our [dashboard](https://dashboard.getstream.io/), navigate to the **Video & Audio -> Roles & Permissions** section and select the appropriate role and scope. -In this example, we will use `my-call-type` scope. - -By default, all users unless specified otherwise, have the `user` role. - -We start by removing the `JoinCall` permission from the `user` role for the `my-call-type` scope. -It will prevent regular users from joining a call of this type. - -![Revoke JoinCall for user role](../assets/02-guides/02-joining-creating-calls/user-revoke-joincall.png) - -Next, let's ensure that the `call_member` role has the `JoinCall` permission for the `my-call-type` scope. -It will allow users with the `call_member` role to join a call of this type. - -![Grant JoinCall for call_member role](../assets/02-guides/02-joining-creating-calls/call_member-grant-joincall.png) - -Once this is set, we can proceed with setting up a `call` instance. - -#### Step 2: Set up the call - -```typescript -const call = client.call('my-call-type', 'my-call-id'); -await call.getOrCreate({ - data: { - members: [ - // please note the `role` property - { user_id: 'alice', role: 'call_member' }, - { user_id: 'bob', role: 'call_member' }, - ], - }, -}); - -// and if necessary, to grant access to more users -await call.updateCallMembers({ - update_members: [{ user_id: 'charlie', role: 'call_member' }], -}); - -// or, to remove access from some users -await call.updateCallMembers({ - remove_members: ['charlie'], -}); -``` diff --git a/packages/client/docusaurus/docs/javascript/02-guides/03-call-and-participant-state.mdx b/packages/client/docusaurus/docs/javascript/02-guides/03-call-and-participant-state.mdx deleted file mode 100644 index 7971adfc36..0000000000 --- a/packages/client/docusaurus/docs/javascript/02-guides/03-call-and-participant-state.mdx +++ /dev/null @@ -1,180 +0,0 @@ ---- -title: Call & Participant State -description: How the state is exposed ---- - -The call, participant and client states are reactive. Their value is updated on WebSocket events and API calls. We use the [RxJS library](https://rxjs.dev/) for this, each state property is an Observable. - -Let's see an example, this is how you can subscribe to changes related to participants: - -```typescript -const { participants$ } = call.state; -const subscription = participants$.subscribe((participants) => { - console.log(participants); -}); - -// dispose the subscription when you don't need it anymore -subscription.unsubscribe(); -``` - -The `console.log` method will be called each time there is a change to the list of participants in the call. - -This approach makes it possible to access the state and be notified about changes anywhere in your application without having to manually subscribe to WebSocket events. - -Alternatively, you can also access the current value of each state variable: - -```typescript -const { participants } = call.state; -console.log(participants); -``` - -It's useful when you need to know the current value, but you don't need to be notified about future changes. - -## Call state - -The call state can be accessed through `call.state` object as in the examples above. -In this object, you'll find properties that are reactive and static as well. -The reactive/observable properties are suffixed with `$` and the static ones are without the suffix. - -:::note -For the best experience, please make sure that the `call` instance is loaded -and connected to our backend: [Load Call](../../joining-and-creating-calls/#load-call). - -Otherwise, `call.state` observables will emit empty values and you won't get real time updates. -::: - -Here is an excerpt of the call state properties: - -| Reactive value | Static value | Description | -| ------------------------ | ----------------------- | ------------------------------------------------------------------------------------------------------------ | -| `backstage$` | `backstage` | `true` when the call runs in `backstage` mode | -| `blockedUserIds$` | `blockedUserIds` | The list of blocked user IDs. | -| `callingState$` | `callingState` | Provides information about the call state. For example, `RINGING`, `JOINED` or `RECONNECTING`. | -| `callStatsReport$` | `callStatsReport` | When stats gathering is enabled, this observable will emit a new value at a regular (configurable) interval. | -| `createdAt$` | `createdAt` | The time the call was created. | -| `createdBy$` | `createdBy` | The user who created the call. | -| `custom$` | `custom` | Custom data attached to the call. | -| `dominantSpeaker$` | `dominantSpeaker` | The participant that is the current dominant speaker of the call. | -| `egress$` | `egress` | The egress data of the call (for broadcasting and livestreaming). | -| `endedAt$` | `endedAt` | The time the call was ended. | -| `endedBy$` | `endedBy` | The user who ended the call. | -| `hasOngoingScreenShare$` | `hasOngoingScreenShare` | It will return `true` if at least one participant is sharing their screen. | -| `ingress$` | `ingress` | The ingress data of the call (for broadcasting and livestreaming). | -| `members$` | `members` | The list of call members | -| `ownCapabilities$` | `ownCapabilities` | The capabilities of the local participant. | -| `pinnedParticipants$` | `pinnedParticipants` | The participants that are currently pinned. | -| `recording$` | `recording` | The recording state of the call. | -| `session$` | `session` | The data for the current call session. | -| `settings$` | `settings` | The settings of the call. | -| `startedAt$` | `startedAt` | The actual start time of the current call session. | -| `startsAt$` | `startsAt` | The time the call is scheduled to start. | -| `thumbnails$` | `thumbnails` | The thumbnails of the call. | -| `transcribing$` | `transcribing` | The transcribing state of the call. | -| `updatedAt$` | `updatedAt` | The time the call was updated. | - -:::note -Your IDE of choice may help you to discover the other properties of the call state. -::: - -## Participant state - -If you want to display information about the joined participants of the call you can use the following properties in `call.state`: - -| Reactive value | Static value | Description | -| ---------------------------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `localParticipant$` | `localParticipant` | The local participant is the logged-in user. | -| `remoteParticipants$` | `remoteParticipants` | All participants except the local participant. | -| `participants$` | `participants` | All participants, including local and remote participants. | -| `participantCount$` | `participantCount` | The approximate participant count of the active call. This includes the [anonymous users](../client-auth/#anonymous-users) as well, it is computed on the server-side. | -| `anonymousParticipantCount$` | `anonymousParticipantCount` | The approximate participant count of anonymous users in the active call. | - -The `StreamVideoParticipant` object contains the following information: - -| Name | Description | -| ------------------------- | -------------------------------------------------------------------------------- | -| `audioLevel` | The audio level of the participant (determined on the server). | -| `audioStream` | The published audio `MediaStream`. | -| `audioVolume` | The audio volume level of the participant (overridable local audioVolume level). | -| `connectionQuality` | The participant's connection quality. | -| `custom` | The participant's custom data. Comes from the `custom` field of the user object. | -| `image` | The image of the participant. | -| `isDominantSpeaker` | It's `true` if the participant is the current dominant speaker in the call. | -| `isLocalParticipant` | It's `true` if the participant is the local participant. | -| `isSpeaking` | It's `true` if the participant is currently speaking. | -| `joinedAt` | The time the participant joined the call. | -| `name` | The name of the participant. | -| `pin` | Holds pinning information. | -| `publishedTracks` | The track types the participant is currently publishing | -| `reaction` | The last reaction this user has sent to this call. | -| `roles` | The roles of the participant in this call. | -| `sessionId` | The identifier of the participant within the existing call session | -| `screenShareAudioStream` | The published screen share audio `MediaStream`. | -| `screenShareStream` | The published screen share `MediaStream`. | -| `userId` | The user ID of the participant. | -| `videoStream` | The published video `MediaStream`. | -| `viewportVisibilityState` | The viewport visibility state of the participant. | - -The SDK also provides a few utility functions that help you to work with participants: - -```ts -import { - Call, - hasAudio, - hasScreenShare, - hasScreenShareAudio, - hasVideo, - isPinned, -} from '@stream-io/video-client'; - -let call: Call; - -// example usage -const subscription = call.state.participants$.subscribe((participants) => { - for (let participant of participants) { - // check if the participant has audio, video, screen share or screen share audio - const hasAudioOn = hasAudio(participant); - const hasVideoOn = hasVideo(participant); - const hasScreenShareOn = hasScreenShare(participant); - const hasScreenShareAudioOn = hasScreenShareAudio(participant); - const isPinnedOn = isPinned(participant); - } - - // participants with a specific role - const hosts = participants.filter((p) => p.roles.includes('host')); - - // participants that publish video and audio - const videoParticipants = participants.filter( - (p) => hasVideo(p) && hasAudio(p), - ); -}); - -subscription.unsubscribe(); -``` - -In a call with many participants, the value of the `participants$` call state observable is truncated to 250 participants. The participants who are publishing video, audio, or screen sharing have priority over the other participants in the list. This means, for example, that in a livestream with one host and many viewers, the host is guaranteed to be in the list. - -## Client state - -The client state can be accessed by `client.state`. - -Here is the list of client state properties: - -| Reactive value | Static value | Description | -| ---------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `connectedUser$` | `connectedUser` | Returns the connected user. Holds the server-side data of the connected user. | -| `calls$` | `calls` | A list of all tracked calls. These calls can be outgoing (I have called somebody) or incoming (somebody has called me). Loaded calls (`call.get()`) are also part of this list. | - -The `connectedUser` object contains the following properties: - -| Name | Description | -| ------------ | ----------------------------------------------------- | -| `created_at` | The time the user was created. | -| `custom` | Custom user data. | -| `deleted_at` | The time the user was deleted. | -| `devices` | The registered push notification devices of the user. | -| `id` | The id of the user. | -| `image` | The profile image of the user. | -| `name` | The name of the user. | -| `role` | The role of the user. | -| `teams` | The teams the user belongs to. | -| `updated_at` | The time when the user was updated. | diff --git a/packages/client/docusaurus/docs/javascript/02-guides/03-calling-state-and-lifecycle.mdx b/packages/client/docusaurus/docs/javascript/02-guides/03-calling-state-and-lifecycle.mdx deleted file mode 100644 index d640837bb2..0000000000 --- a/packages/client/docusaurus/docs/javascript/02-guides/03-calling-state-and-lifecycle.mdx +++ /dev/null @@ -1,132 +0,0 @@ ---- -id: calling-state-and-lifecycle -title: Calling State and Lifecycle -description: Calling State machine and Call Lifecycle. ---- - -The `call` object instance manages everything related to a particular call instance, such as: - -- creating and joining a call -- performing actions (mute, unmute, send reaction, etc...) -- manage event subscriptions (`call.on('call.session_started', callback)`, etc...) -- and many more - -Every `call` instance should be created through the `client.call(type, id)` helper. - -Our `StreamVideoClient` is responsible for maintaining a WebSocket connection to our servers and also takes care about the API calls that are proxied from the `call` instance. - -As we learned in [Joining and Creating Calls](../../guides/joining-and-creating-calls/) guide, a call instance is managed like this: - -```ts -import { Call, StreamVideoClient } from '@stream-io/video-client'; - -let client: StreamVideoClient; // ... - -const call: Call = client.call(type, id); - -// load existing call information from our servers -await call.get(); - -// Creates the call on our servers in case it doesn't exist. Otherwise, -// loads the call information from our servers. -await call.getOrCreate(); - -// join the call -await call.join(); - -// leave the call and dispose all allocated resources -await call.leave(); -``` - -Every `call` instance has a local state, exposed to integrators through: - -- `call.state.callingState` - a getter that returns the current value -- `call.state.callingState$` - an observable that an integrator can subscribe to and be notified everytime the value changes - -The call instance is a stateful resource that, once acquired with `client.call()`, must be disposed of with `call.leave()`. Failure to dispose of the call properly can result in memory leaks and unexpected behavior. - -## Calling State - -Every `call` instance has its own local state managed by the SDK. - -These values are exposed through the `CallingState` enum: - -```ts -import { CallingState, useCallStateHooks } from '@stream-io/video-client'; - -const { useCallCallingState } = useCallStateHooks(); -const callingState = useCallCallingState(); - -switch (callingState) { - case CallingState.JOINED: - // ... - break; - default: - const exhaustiveCheck: never = callingState; - throw new Error(`Unknown calling state: ${exhaustiveCheck}`); -} -``` - -:::note -As `CallingState` is an enum that can be extended at any time by us, it would be good to make sure you -use it exhaustively. This way, if you use TypeScript, you can get a compile time error and be notified that -there are few more states that you should handle. -::: - -### Calling States - -| State | Description | -| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `CallingState.UNKNOWN` | The state is unknown. This value is set when Calling State isn't initialized properly. | -| `CallingState.IDLE` | A call instance is created on the client side but a WebRTC session isn't established yet. | -| `CallingState.RINGING` | This is an incoming (ring) call. You are the callee. | -| `CallingState.JOINING` | The call join flow is executing (typically right after `call.join()`). Our systems are preparing to accept the new call participant. | -| `CallingState.JOINED` | The join flow has finished successfully and the current participant is part of the call. The participant can receive and publish audio and video. | -| `CallingState.LEFT` | The call has been left (`call.leave()`) and all allocated resources are released. Please create a new `call` instance if you want to re-join. | -| `CallingState.RECONNECTING` | A network connection has been lost (due to various factors) and the `call` instance attempts to re-establish a connection and resume the call. | -| `CallingState.RECONNECTING_FAILED` | The SDK failed to recover the connection after a couple of consecutive attempts. You need to inform the user that he needs to go online and manually attempt to rejoin the call. | -| `CallingState.MIGRATING` | The SFU node that is hosting the current participant is shutting down or tries to rebalance the load. This `call` instance is being migrated to another SFU node. | -| `CallingState.OFFLINE` | No network connection can be detected. Once the connection restores, the SDK will automatically attempt to recover the connection (signalled with `RECONNECTING` state). | - -### Example handling - -To understand these values better, here is a hypothetical example of how these values can be mapped: - -```typescript -import { CallingState, useCallStateHooks } from '@stream-io/video-client'; - -const { useCallCallingState } = useCallStateHooks(); -const callingState = useCallCallingState(); - -switch (callingState) { - case CallingState.UNKNOWN: - case CallingState.IDLE: - return renderLobbyScreen(); - - case CallingState.RINGING: - return renderIncomingCallScreen(); - - case CallingState.JOINING: - return renderLoadingScreen(); - - case CallingState.JOINED: - return renderActiveCallScreen(); - - case CallingState.LEFT: - return renderHaveANiceDayScreen(); - - case CallingState.RECONNECTING: - case CallingState.MIGRATING: - return renderRestoringConnectionScreen(); - - case CallingState.RECONNECTING_FAILED: - return renderGeneralConnectionProblemScreen(); - - case CallingState.OFFLINE: - return renderNoConnectionScreen(); - - default: - const exhaustiveCheck: never = callingState; - throw new Error(`Unknown calling state: ${exhaustiveCheck}`); -} -``` diff --git a/packages/client/docusaurus/docs/javascript/02-guides/04-camera-and-microphone.mdx b/packages/client/docusaurus/docs/javascript/02-guides/04-camera-and-microphone.mdx deleted file mode 100644 index eb8d0e8e79..0000000000 --- a/packages/client/docusaurus/docs/javascript/02-guides/04-camera-and-microphone.mdx +++ /dev/null @@ -1,454 +0,0 @@ ---- -id: camera-and-microphone -title: Camera & Microphone -description: Managing audio and video ---- - -If you want to see the device management API in action, you can check out [the sample app](https://github.com/GetStream/stream-video-js/tree/main/sample-apps/client/ts-quickstart). - -## Camera management - -The SDK does its best to make working with the camera easy. We expose the following objects on the call: - -### Start-stop camera - -```typescript -await call.camera.toggle(); - -// or -await call.camera.enable(); -await call.camera.disable(); -``` - -It's always best to await these calls, however the SDK does its best to resolve potential race conditions: the last call always "wins". - -Here is how you can access the status: - -```typescript -call.camera.state.status; // enabled, disabled or undefined -call.camera.state.status$.subscribe(console.log); // Reactive value for status, you can subscribe to changes -``` - -Status is updated once the camera is actually enabled or disabled. Use `call.camera.state.optimisticStatus` (and the corresponding observable) for the "optimistic" status that is updated immediately after toggling the camera. - -The initial status of the camera is `undefined`. If you don't change the initial status, the default backend settings will be applied once the participant joins the call. - -If you're building a lobby screen, this is how you can apply the backend settings: - -```typescript -await call.get(); -const defaultCameraStatus = call.state.settings?.video.camera_default_on; -defaultCameraStatus ? call.camera.enable() : call.camera.disable(); -``` - -### List and select devices - -```typescript -// List devices -// The error handler is called if the user denies permission to use camera -call.camera - .listDevices() - .subscribe({ next: console.log, error: console.error }); - -// Select device -call.camera.select('device Id'); -``` - -Here is how you can access the selected device: - -```typescript -call.camera.state.selectedDevice; // currently selected camera -call.camera.state.selectedDevice$.subscribe(console.log); // Reactive value for selected device, you can subscribe to changes -``` - -### Camera permissions - -In a browser, you can use the following API to check whether the user has granted permission to access the connected cameras: - -```typescript -call.camera.state.hasBrowserPermission$.subscribe((value) => { - if (value) { - // User has granted permission - } else { - // User has denied or not yet granted permission - } -}); -``` - -### Camera direction - -On mobile devices it's useful if users can switch between the front and back cameras: - -```typescript -await call.camera.selectDirection('front'); // or back - -// Flip camera -await call.camera.flip(); -``` - -This is how you can access the camera direction: - -```typescript -call.camera.state.direction; // front, back or undefined -call.camera.state.direction$.subscribe(console.log); // Reactive value for direction, you can subscribe to changes -``` - -The initial direction of the camera is `undefined`. If you don't change the initial status, the default backend settings will be applied once the participant joins the call. - -If you're building a lobby screen, this is how you can apply the backend settings: - -```typescript -await call.get(); -// fallback in case no backend setting -let defaultDirection: CameraDirection = 'front'; -const backendSetting = call.state.settings?.video.camera_facing; -if (backendSetting) { - defaultDirection = backendSetting === 'front' ? 'front' : 'back'; -} -call.camera.selectDirection(defaultDirection); -``` - -### Render video - -#### In call - -Follow our [Rendering Video and Audio guide](../../guides/playing-video-and-audio/). - -#### Lobby preview - -This is how you can show a preview video before joining the call: - -```typescript -const call = client.call('default', '123'); - -await call.camera.enable(); -const videoEl = document.createElement('video'); -videoEl.playsInline = true; -videoEl.muted = true; -videoEl.autoplay = true; - -if (videoEl.srcObject !== call.camera.state.mediaStream) { - videoEl.srcObject = call.camera.state.mediaStream || null; - if (videoEl.srcObject) { - videoEl.play(); - } -} -``` - -### Access to the Camera's MediaStream - -Our SDK exposes the current `mediaStream` instance that you can use for your needs (for example, local recording, etc...): - -```typescript -// current mediaStream instance -call.camera.state.mediaStream; -// Reactive value for mediaStream, you can subscribe to changes -call.camera.state.mediaStream$.subscribe((mediaStream) => { - const [videoTrack] = mediaStream.getVideoTracks(); - console.log('Video track', videoTrack); -}); -``` - -## Microphone management - -The SDK does its best to make working with the microphone easy. We expose the following objects on the call: - -### Start-stop microphone - -```typescript -await call.microphone.toggle(); - -// or -await call.microphone.enable(); -await call.microphone.disable(); -``` - -It's always best to await these calls, however the SDK does its best to resolve potential race conditions: the last call always "wins". - -This is how you can access the microphone status: - -```typescript -call.microphone.state.status; // enabled, disabled or undefined -call.microphone.state.status$.subscribe(console.log); // Reactive value for status, you can subscribe to changes -``` - -Status is updated once the microphone is actually enabled or disabled. Use `call.microphone.state.optimisticStatus` (and the corresponding observable) for the "optimistic" status that is updated immediately after toggling the microphone. - -The initial status of the microphone is `undefined`. If you don't change the initial status, the default backend settings will be applied once the participant joins the call. - -If you're building a lobby screen, this is how you can apply the backend settings: - -```typescript -await call.get(); -const defaultMicStatus = call.state.settings?.audio.mic_default_on; -if (defaultMicStatus) { - call.microphone.enable(); -} else { - call.microphone.disable(); -} -``` - -### List and select devices - -```typescript -// List devices -// The error handler is called if the user denies permission to use microphone -call.microphone - .listDevices() - .subscribe({ next: console.log, error: console.error }); - -// Select device -call.microphone.select('device Id'); -``` - -This is how you can access the selected device: - -```typescript -call.microphone.state.selectedDevice; // currently selected microphone -call.microphone.state.selectedDevice$.subscribe(console.log); // Reactive value for selected device, you can subscribe to changes -``` - -### Microphone permissions - -In a browser, you can use the following API to check whether the user has granted permission to access the connected microphones: - -```typescript -call.microphone.state.hasBrowserPermission$.subscribe((value) => { - if (value) { - // User has granted permission - } else { - // User has denied or not yet granted permission - } -}); -``` - -### Play audio - -#### In call - -Follow our [Playing Video and Audio guide](../../guides/playing-video-and-audio/). - -#### Lobby preview - -On lobby screens a common UX pattern is to show a visual indicator of the detected audio levels coming from the selected microphone. The client exposes the `createSoundDetector` utility method to help implement this functionality. Here is an example of how you can do that: - -```html - -``` - -```typescript -import { createSoundDetector } from '@stream-io/video-client'; - -let cleanup: Function | undefined; -call.microphone.state.mediaStream$.subscribe(async (mediaStream) => { - const progressBar = document.getElementById('volume') as HTMLProgressElement; - if (mediaStream) { - cleanup = createSoundDetector( - mediaStream, - (event) => { - progressBar.value = event.audioLevel; - }, - { detectionFrequencyInMs: 100 }, - ); - } else { - await cleanup?.(); - progressBar.value = 0; - } -}); -``` - -### Speaking while muted notification - -When the microphone is disabled, the client will automatically start monitoring audio levels, to detect if the user is speaking. -This feature is enabled by default unless the user doesn't have the permission to send audio or explicitly disabled. - -This is how you can subscribe to these notifications: - -```typescript -call.microphone.state.speakingWhileMuted; // current value -call.microphone.state.speakingWhileMuted$.subscribe((isSpeaking) => { - if (isSpeaking) { - console.log(`You're muted, unmute yourself to speak`); - } -}); // Reactive value - -// to disable this feature completely: -await call.microphone.disableSpeakingWhileMutedNotification(); - -// to enable it back: -await call.microphone.enableSpeakingWhileMutedNotification(); -``` - -### Access to the Microphone's MediaStream - -Our SDK exposes the current `mediaStream` instance that you can use for your needs (for example, local recording, etc...): - -```typescript -// current mediaStream instance -call.microphone.state.mediaStream; -// Reactive value for mediaStream, you can subscribe to changes -call.microphone.state.mediaStream$.subscribe((mediaStream) => { - const [audioTrack] = mediaStream.getAudioTracks(); - console.log('Audio track', audioTrack); -}); -``` - -### Noise Cancellation - -Check our [Noise Cancellation](../../guides/noise-cancellation) guide. - -## Camera and Microphone AI/ML Filters - -Both the Camera and the Microphone allow you to apply AI/ML filters to the media stream. -This can be useful for various use-cases, such as: - -- applying a video effects such as background blurring, or background replacement -- applying a custom video filter (e.g. color correction, or face detection) -- applying a custom audio filter (e.g. noise reduction) - -```typescript -import { Call } from '@stream-io/video-client'; - -let call: Call; - -// apply a custom video filter -const { unregister: unregisterMyVideoFilter } = camera.registerFilter( - function myVideoFilter(inputMediaStream: MediaStream) { - // initialize the video filter, do some magic and - // return the modified media stream - return { - output: mediaStreamWithFilterApplied, - stop: () => { - // optional cleanup function - }, - }; - }, -); - -// apply a custom audio filter -const { unregister: unregisterMyAudioFilter } = microphone.registerFilter( - function myAudioFilter(inputMediaStream: MediaStream) { - // initialize the audio filter, do some magic and - // return the modified media stream - return { - output: mediaStreamWithFilterApplied, - stop: () => { - // optional cleanup function - }, - }; - }, -); - -// unregister the filters -unregisterMyVideoFilter(); -unregisterMyAudioFilter(); -``` - -Filters can be registered and unregistered at any time, and the SDK will take care of the rest. -Filters can be chained, and the order of registration matters. -The first registered filter will be the first to modify the raw `MediaStream`. - -A filter may be asynchronous. In this case, the function passed to the `registerFilter` method should _synchronously_ -return an object where `output` is a promise that resolves to a `MediaStream`: - -```typescript -camera.registerFilter(function myVideoFilter(inputMediaStream: MediaStream) { - // note that output is returned synchronously! - return { - output: new Promise((resolve) => resolve(mediaStreamWithFilterApplied)), - stop: () => { - // optional cleanup function - }, - }; -}); -``` - -Registering a filter is not instantaneous. If you need to know when a filter is registered (e.g. to show -a spinner in the UI), await for the `registered` promise returned by the `registerFilter` method: - -```typescript -const { registered } = camera.registerFilter( - (inputMediaStream: MediaStream) => { - // your video filter - }, -); -await registered; -// at this point, the filter is registered -``` - -Once a filter(s) is registered, the SDK will send the last returned `MediaStream` to the remote participants. - -## Speaker management - -### List and select devices - -#### Browser support - -Selecting audio output device [isn't supported by all browsers](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/sinkId), this is how you can check the availability: - -```typescript -call.speaker.state.isDeviceSelectionSupported; -``` - -```typescript -// List devices -// The error handler is called if the user denies permission to use audio -call.speaker.listDevices().subscribe({ - next: (devices) => console.log(devices), - error: (err) => console.error(err), -}); - -// Select device -call.speaker.select('device Id'); -``` - -Device id can also be an empty string, that means to use the system's default audio output. - -Here is how you can access the selected device: - -```typescript -call.speaker.state.selectedDevice; // currently selected audio output -// Reactive value for selected device, you can subscribe to changes -call.speaker.state.selectedDevice$.subscribe((selectedDevice) => { - console.log(selectedDevice); -}); -``` - -### Set volume - -Volume has to be a number between 0 and 1. 0 means mute the audio output. - -```typescript -call.speaker.setVolume(0.5); -``` - -The default system value is 1. - -Here is how you can access the selected device: - -```typescript -call.speaker.state.volume; // current volume -// Reactive value for volume, you can subscribe to changes -call.speaker.state.volume$.subscribe((volume) => { - console.log(volume); -}); -``` - -### Set participant volume - -You can control the volume of a specific participant in the call: - -```typescript -import { StreamVideoParticipant } from '@stream-io/video-client'; - -let p: StreamVideoParticipant; - -// will set the volume of the participant to 50% -call.speaker.setParticipantVolume(p.sessionId, 0.5); - -// will mute the participant -call.speaker.setParticipantVolume(p.sessionId, 0); - -// will reset the volume to the default value -call.speaker.setParticipantVolume(p.sessionId, undefined); -``` diff --git a/packages/client/docusaurus/docs/javascript/02-guides/04-noise-cancellation.mdx b/packages/client/docusaurus/docs/javascript/02-guides/04-noise-cancellation.mdx deleted file mode 100644 index 557698c8fa..0000000000 --- a/packages/client/docusaurus/docs/javascript/02-guides/04-noise-cancellation.mdx +++ /dev/null @@ -1,79 +0,0 @@ ---- -id: noise-cancellation -title: Noise Cancellation -description: Noise Cancellation with Krisp.ai ---- - -Noise Cancellation capabilities of our [Plain-JS Video SDK](https://getstream.io/video/sdk/javascript/) can be enabled by -installing our `@stream-io/audio-filters-web` plugin. Under the hood, this package uses the technology developed -by [krisp.ai](https://krisp.ai/). - -## Compatibility - -This plugin currently supports only modern desktop browsers: - -- Chrome, Firefox and Edge -- Safari 17.4.1+ - -## Install the plugin - -With your favourite package manager, run the following command: - -```bash -yarn add @stream-io/audio-filters-web -# or -npm install @stream-io/audio-filters-web -``` - -## Integration - -In the following code snippet, we show how to check if а platform is supported, initialize the plugin, and enable/disable it. - -```typescript -import { NoiseCancellation } from '@stream-io/audio-filters-web'; -import { Call } from '@stream-io/video-client'; - -let call: Call; // grab a reference to the call instance - -// create a new NoiseCancellation instance -const noiseCancellation = new NoiseCancellation(); - -// check if the current platform is supported -const isSupported = await noiseCancellation.isSupported(); - -// and initialize it -await noiseCancellation.init(); - -// provide the NoiseCancellation instance to the call and register the plugin -await call.microphone.enableNoiseCancellation(noiseCancellation); - -// disable it and unregister the plugin -await call.microphone.disableNoiseCancellation(); - -// convenience methods -await noiseCancellation.isSupported(); // returns true for supported platforms - -noiseCancellation.disable(); // will temporarily disable NC (doesn't unregister the plugin) -noiseCancellation.enable(); // will enable NC (requires the plugin to be registered first) - -// listen for state updates -const off = noiseCancellation.on('change', (v) => { - console.log(`Noise Cancellation is ${v ? 'enabled' : 'disabled'}`); -}); -``` - -In action, the most straight-forward integration should look like this: - -```typescript -import { NoiseCancellation } from '@stream-io/audio-filters-web'; - -const call = client.call(type, id); -await call.get(); // or call.getOrCreate() - -const noiseCancellation = new NoiseCancellation(); -const isSupported = await noiseCancellation.isSupported(); -if (isSupported) { - await noiseCancellation.init(); - await call.microphone.enableNoiseCancellation(noiseCancellation); -} -``` diff --git a/packages/client/docusaurus/docs/javascript/02-guides/05-call-types.mdx b/packages/client/docusaurus/docs/javascript/02-guides/05-call-types.mdx deleted file mode 100644 index 3bd86fa88c..0000000000 --- a/packages/client/docusaurus/docs/javascript/02-guides/05-call-types.mdx +++ /dev/null @@ -1,20 +0,0 @@ ---- -id: configuring-call-types -title: Call Types ---- - -import CallTypesPage from '../../../shared/video/_call-types.mdx'; -import WithExternalLinks from '../../../shared/video/_withExternalLinks'; - - - - diff --git a/packages/client/docusaurus/docs/javascript/02-guides/06-querying-calls.mdx b/packages/client/docusaurus/docs/javascript/02-guides/06-querying-calls.mdx deleted file mode 100644 index 73ad2e64fd..0000000000 --- a/packages/client/docusaurus/docs/javascript/02-guides/06-querying-calls.mdx +++ /dev/null @@ -1,203 +0,0 @@ ---- -id: querying-calls -title: Querying Calls -description: How to query calls ---- - -The Stream Video SDK allows you to query calls and watch them. -This allows you to build apps that display feeds of calls with real-time updates (without joining them). - -You can query calls based on built-in fields as well as any custom field you add to the calls. -Multiple filters can be combined using AND, OR logical operators, -each filter can use its comparison (equality, inequality, greater than, greater or equal, etc.). - -You can use the `StreamVideoClient` to query for: - -- Upcoming calls -- Calls that are currently live -- Popular live streams / audio rooms with a link to the recording - -## Client API - -You can query calls by using the client directly by using the following API: - -```ts -const { calls } = await client.queryCalls({ - filter_conditions: { ...filters }, - sort: [...sortOptions], - limit: 25, - watch: true, -}); -``` - -## Filters - -Filter expressions support multiple match criteria, and it's also possible to combine filters. -You can filter on the following fields: - -| Field | Description | -| -------------------- | ------------------------------------------------------------- | -| `id` | The id for this call | -| `cid` | The cid for this call. IE: `default:123` | -| `team` | The team id for the call. | -| `type` | The call type. Typically `default`, `livestream` etc... | -| `created_by_user_id` | The user id who created the call | -| `created_at` | When the call was created | -| `updated_at` | When the call was updated | -| `ended_at` | When the call ended | -| `starts_at` | When the call starts at | -| `backstage` | If the call is in backstage mode or not | -| `members` | Check if the call has these members listed | -| `ongoing` | Check if the call is ongoing or not | -| `custom` | You can query custom data using the `"custom.myfield"` syntax | - -For more information, visit the [filter operators guide](https://getstream.io/chat/docs/react/query_syntax_operators/?language=javascript). Or check the examples: - -### Calls that are about to start - -In this snippet, you can see how you can query for calls that have `livestream` type and are about to start 30 minutes from now: - -```typescript -import { StreamVideoClient } from '@stream-io/video-client'; - -let client: StreamVideoClient; - -const inNext30mins = new Date(Date.now() + 1000 * 60 * 60 * 30); - -const { calls } = await client.queryCalls({ - filter_conditions: { - type: { $eq: 'livestream' }, - starts_at: { $gt: inNext30mins.toISOString() }, - }, - sort: [{ field: 'starts_at', direction: -1 }], - limit: 10, - watch: true, -}); -``` - -### Call filters on a custom property - -```typescript -import { StreamVideoClient } from '@stream-io/video-client'; - -let client: StreamVideoClient; - -const { calls } = await client.queryCalls({ - filter_conditions: { 'custom.color': 'red' }, - limit: 10, - watch: true, -}); -``` - -### Calls that are ongoing / currently have participants - -```typescript -import { StreamVideoClient } from '@stream-io/video-client'; - -let client: StreamVideoClient; - -const { calls } = await client.queryCalls({ - filter_conditions: { ongoing: true }, -}); -``` - -### Calls the user has created or is a member of - -```typescript -import { StreamVideoClient } from '@stream-io/video-client'; - -let client: StreamVideoClient; - -const { calls } = await client.queryCalls({ - filter_conditions: { - $or: [ - { created_by_user_id: '' }, - { members: { $in: [''] } }, - ], - }, - limit: 10, - watch: true, -}); -``` - -## Sorting - -The `SortParamRequest` model contains two properties: `field` and `direction`. - -The `direction` can be `1` for ascending and `-1` for descending, while the field can be one of the following values: - -| Field | Description | -| ------------ | ------------------------------------------------------- | -| `starts_at` | When the call starts at | -| `created_at` | When the call was created | -| `updated_at` | When the call was updated | -| `ended_at` | When the call ended | -| `type` | The call type. Typically `default`, `livestream` etc... | -| `id` | The id for this call | -| `cid` | The cid for this call. IE: `default:123` | - -```typescript -import { StreamVideoClient } from '@stream-io/video-client'; - -let client: StreamVideoClient; - -const { calls } = await client.queryCalls({ - sort: [{ field: 'starts_at', direction: -1 }], - limit: 10, - watch: true, -}); -``` - -It's possible to provide multiple sort parameters: - -```typescript -import { StreamVideoClient } from '@stream-io/video-client'; - -let client: StreamVideoClient; - -const { calls } = await client.queryCalls({ - sort: [ - { field: 'starts_at', direction: -1 }, - { field: 'created_at', direction: 1 }, - ], - limit: 10, - watch: true, -}); -``` - -## Watching calls - -If you specify `watch: true` as an option, the SDK will create a subscription to the call data on the server and you'll be able to receive updates in real-time. - -The server will send updates to the client when the call data changes -(for example, members are updated, a call session has started, etc...). -This is useful for showing a live preview of who is in the call or building a call dashboard. - -## Pagination - -You can specify the page size using the `limit` option. The API response will include links to the previous/next pages. The following code example shows how pagination works: - -```typescript -import { StreamVideoClient } from '@stream-io/video-client'; - -let client: StreamVideoClient; - -const inNext30mins = new Date(Date.now() + 1000 * 60 * 60 * 30); -const callQuery = { - filter_conditions: { - type: { $eq: 'livestream' }, - starts_at: { $gt: inNext30mins.toISOString() }, - }, - sort: [{ field: 'starts_at', direction: -1 }], - limit: 10, - watch: true, -}; - -let { calls, prev, next } = await client.queryCalls(callQuery); - -// Go to the next page -({ calls, prev, next } = await client.queryCalls({ ...callQuery, next })); - -// Go to the previous page -({ calls, prev, next } = await client.queryCalls({ ...callQuery, prev })); -``` diff --git a/packages/client/docusaurus/docs/javascript/02-guides/07-query-call-members.mdx b/packages/client/docusaurus/docs/javascript/02-guides/07-query-call-members.mdx deleted file mode 100644 index 8ff806876d..0000000000 --- a/packages/client/docusaurus/docs/javascript/02-guides/07-query-call-members.mdx +++ /dev/null @@ -1,58 +0,0 @@ ---- -id: querying-call-members -title: Querying Call Members -description: How to query call members ---- - -import FilterConditions from '../../../shared/_filter-operators.mdx'; -import CallMemberFilters from '../../../shared/video/_call-member-filters.mdx'; -import CallMemberSort from '../../../shared/video/_call-member-sort-fields.mdx'; - -When you create or join a call you get a list of call members, however this can return at most 100 members: - -```typescript -// The maximum limit is 100 -// The default limit is 25 -await call.getOrCreate({ members_limit: 100 }); - -// or -await call.join({ members_limit: 100 }); -``` - -To get the complete list of call members the Stream API allows you to query, filter and sort members of a call using a paginated list. - -## Examples - -Below are a few examples of how to use this API: - -```typescript -const result = await call.queryMembers(); - -// sorting and pagination -const queryMembersReq = { - sort: [{ field: 'user_id', direction: 1 }], - limit: 2, -}; -const result = await call.queryMembers(queryMembersReq); - -// loading the next page -const result = await call.queryMembers({ - ...queryMembersReq, - next: result.next, -}); - -// filtering -const result = await call.queryMembers({ - filter_conditions: { role: { $eq: 'admin' } }, -}); -``` - -## Sort options - - - -## Filter options - - - - diff --git a/packages/client/docusaurus/docs/javascript/02-guides/08-permissions-and-moderation.mdx b/packages/client/docusaurus/docs/javascript/02-guides/08-permissions-and-moderation.mdx deleted file mode 100644 index 6c78a0d509..0000000000 --- a/packages/client/docusaurus/docs/javascript/02-guides/08-permissions-and-moderation.mdx +++ /dev/null @@ -1,167 +0,0 @@ ---- -title: Permissions & Moderation -description: Explanation of call permissions and moderation features ---- - -In many types of calls, there is a requirement for providing different users with certain permissions and capabilities. -A typical example is a webinar where the host wants to control who can speak or who can share their video or screen. - -The Stream Video SDK provides a certain set of permissions and capabilities -that can be used to control the behavior of participants in a call. - -## Conceptual overview - -### Roles - -The Stream Video API allows assigning roles to users. Each user has a global role, and they will also have a call-level role for each call they join. The Stream Video API provides a set of predefined roles, but it's also possible to create your own roles. - -### Call types - -[Call types](../../guides/configuring-call-types/) also allow for a more granular control system: - -- you can enable/disable certain features on a call type level -- you can configure how each call-level role works on a call type level - -### Capabilities - -Based on a user's roles and the call type settings, we can determine which actions are allowed for a user joined to a specific call. - -## Permissions - -As soon as you join a call, the `Call` instance would allow you to check the permissions of the local user -or perform some permission-related actions: - -### Check permissions - -```ts -import { OwnCapability } from '@stream-io/video-client'; - -const call = client.call(type, id); -const canSendAudio = call.permissionsContext.hasPermission( - OwnCapability.SEND_AUDIO, -); -``` - -### Request permissions - -Every user may request permission to perform certain actions depending on the call type and call settings. -For example, in an audio-room call type, only the hosts have `send-audio` permission by default. -Other users should request this permission before they can start sending audio if the call settings allow it. - -```ts -import { OwnCapability } from '@stream-io/video-client'; - -const call = client.call(type, id); -if (!call.permissionsContext.canRequest(OwnCapability.SEND_AUDIO)) { - console.log('The host has disabled the ability to request this permission'); - return; -} -await call.requestPermissions({ - permissions: [OwnCapability.SEND_AUDIO], -}); -``` - -### Approving permission requests - -Call hosts and moderators can approve permission requests from other users. -Whenever a user requests a certain permission, a `call.permission_request` event will be emitted on the `Call` instance. -You can listen to this event and approve the request. - -```ts -import { - PermissionRequestEvent, - StreamCallEvent, -} from '@stream-io/video-client'; - -const call = client.call(type, id); -call.on('call.permission_request', async (event: StreamCallEvent) => { - const request = event as PermissionRequestEvent; - if (shouldApproveRequest(request)) { - await call.grantPermissions(request.user.id, request.permissions); - } -}); -``` - -### Moderation - -At any time, a moderator or host can decide to either grant or revoke certain permission to any participant. - -```ts -import { OwnCapability } from '@stream-io/video-client'; - -const call = client.call(type, id); -await call.updateUserPermissions({ - user_id: 'demo-user', - grant_permission: [OwnCapability.SEND_AUDIO, OwnCapability.SEND_VIDEO], - revoke_permissions: [OwnCapability.SCREENSHARE], -}); - -// alternate API for granting user permissions: -await call.grantPermissions('demo-user', [ - OwnCapability.SEND_AUDIO, - OwnCapability.SEND_VIDEO, -]); - -// alternate API for revoking user permissions: -await call.revokePermissions('demo-user', [OwnCapability.SCREENSHARE]); -``` - -The end user would get notified via a WebSocket event with a type: `call.permissions_updated`. -In the case of revoked permissions, the client would automatically stop publishing the appropriate tracks. - -### Muting participants - -In addition to granting or revoking permissions, a moderator or host can also mute a participant. -This is a common scenario as quite often, participants may be a source of unwanted noise or distraction. - -```ts -const call = client.call(type, id); -await call.muteUser('demo-user-id', 'audio'); -await call.muteUser('demo-user-id', 'video'); -await call.muteUser('demo-user-id', 'screenshare'); - -// or, mute in bulk -await call.muteUser(['demo-user-id', 'demo-user-id-2'], 'audio'); - -// or, muting self -await call.muteSelf('audio'); - -// or, muting others -await call.muteOthers('audio'); - -// or, mute all, including self. -await call.muteAllUsers('audio'); -``` - -This operation doesn't revoke any permission, and the user would still be able to un-mute itself. - -### Ending call for everyone - -In some cases, a moderator or host may want to end the call for everyone. - -```ts -const call = client.call(type, id); -await call.endCall(); -``` - -This operation will emit `call.ended` event to every participant in the call. -The client would automatically stop publishing any tracks and leave the call. - -Ended calls can't be re-joined. - -## Capabilities - -Every user connecting to a Stream Call has a set of Capabilities. -The capabilities of the local user live in the state of the `Call` instance. - -```tsx -const call = client.call(type, id); -const { ownCapabilities } = call.state; - -// or if you want to subscribe to changes: -call.state.ownCapabilities$.subscribe((capabilities) => { - // do something with the capabilities -}); -``` - -Capabilities will have the type [`OwnCapability`](https://github.com/GetStream/stream-video-js/blob/main/packages/client/src/gen/coordinator/index.ts). diff --git a/packages/client/docusaurus/docs/javascript/02-guides/09-reactions.mdx b/packages/client/docusaurus/docs/javascript/02-guides/09-reactions.mdx deleted file mode 100644 index 332f639b1c..0000000000 --- a/packages/client/docusaurus/docs/javascript/02-guides/09-reactions.mdx +++ /dev/null @@ -1,66 +0,0 @@ ---- -title: Reactions -description: How reactions work ---- - -Reactions allow call participants to send emojis in real-time. - -Custom events let participants send and receive arbitrary WebSocket messages. For example, if you want to implement a drawing feature in your call, you can use custom events for synchronizing the drawing board between participants. - -## Reactions - -### Sending reactions - -You can send a reaction using the `sendReaction` method of a `Call` instance. - -```typescript -const call: Call; - -await call.sendReaction({ type: 'raised-hand' }); -``` - -The value of the `type` attribute can be any string. - -It's also possible to provide additional data for the reaction: - -```typescript -const call: Call; - -await call.sendReaction({ - type: 'raised-hand', - emoji_code: ':raise-hand:', - custom: { clearAfterTimeout: true }, -}); -``` - -The `emoji_code` attribute is used by the SDK components to decide which emoji to display on the UI. - -The `custom` property can contain any data. - -### Receiving reactions - -Reactions are only delivered to clients that are [watching the call](../../advanced/events/#call-events). - -The [participant state](../../guides/call-and-participant-state/#observe-participant-state) will contain the latest reaction of each participant: - -```typescript -const { participants } = call.state; -// or subscribe to call.state.participants$ - -const reactions = participants.map((p) => p.reaction); -``` - -You can also subscribe to the `call.reaction_new` WebSocket event to receive reactions. For more information, check out our [Events guide](../../advanced/events). - -### Clearing reactions - -If you're using the [participant state](../../guides/call-and-participant-state/#observe-participant-state) for receiving reactions, you can also clear the latest reaction using the `resetReaction` method: - -```typescript -const { participants } = call.state; -// or subscribe to call.state.participants$ - -call.resetReaction(participants[0].sessionId); -``` - -This is a local action, it won't send any WebSocket messages. It's helpful if you only want to display reactions for a set period of time. diff --git a/packages/client/docusaurus/docs/javascript/02-guides/10-custom-events.mdx b/packages/client/docusaurus/docs/javascript/02-guides/10-custom-events.mdx deleted file mode 100644 index 99d6354c14..0000000000 --- a/packages/client/docusaurus/docs/javascript/02-guides/10-custom-events.mdx +++ /dev/null @@ -1,41 +0,0 @@ ---- -title: Custom Events -description: How custom events work ---- - -You can use custom events to send data among the participants in the call. -This is a realtime layer that you can use to broadcast your own events to. - -## Sending custom events - -For example, if you are building a collaborative drawing app, you can send the coordinates to the other participants with the following code: - -```typescript -await call.sendCustomEvent({ - type: 'draw', - x: 10, - y: 30, -}); -``` - -Please note that the total payload for these events is limited to 5KB in size. - -## Receiving custom events - -Custom events are only delivered to clients that are [watching the call](../../advanced/events/#call-events). - -To receive custom events, you need to subscribe to the `custom` event on the call instance: - -```typescript -const unsubscribe = call.on('custom', (event: CustomVideoEvent) => { - const payload = event.custom; - if (payload.type === 'draw') { - console.log(`Received draw event: x=${payload.x}, y=${payload.y}`); - } -}); - -// Unsubscribe when you no longer need to listen to custom events -unsubscribe(); -``` - -For more information, check out our [Events guide](../../advanced/events). diff --git a/packages/client/docusaurus/docs/javascript/02-guides/10-sorting-api.mdx b/packages/client/docusaurus/docs/javascript/02-guides/10-sorting-api.mdx deleted file mode 100644 index ec41208d33..0000000000 --- a/packages/client/docusaurus/docs/javascript/02-guides/10-sorting-api.mdx +++ /dev/null @@ -1,227 +0,0 @@ ---- -title: Participant Sorting -description: Overview of the Sorting API used to sort call participants. ---- - -The Participant Sorting API is a powerful tool built on top of the internal `Comparator` API, -providing developers with the ability to sort participants in various scenarios. This API offers common comparators and built-in presets that can be easily customized or used out-of-the-box, making participant sorting a seamless experience. - -When dealing with real-time communication applications, it is often necessary to sort participants based on specific criteria. -Whether you need to adjust the sorting in existing view layouts or define new sorting presets, the **Participant Sorting API** is here to simplify the process. - -By utilizing the `Comparator` API and the provided built-in comparators and presets, developers can effortlessly sort participants according to their requirements. - -## `Comparator` API overview - -The `Comparator` API is the foundation upon which the Participant Sorting API is built. -It defines a function type `Comparator` that takes two arguments `a` and `b` of type `T` and returns `-1`, `0`, or `1` based on the comparison between the two items. -This API allows developers to create custom comparators tailored to their specific needs. - -Ultimately, this API can be used in conjunction with the `Array.sort` method to sort any type of data. - -```ts -import { - Comparator, - combineComparators, - conditional, - descending, -} from '@stream-io/video-client'; - -type Participant = { - id: number; - name: string; -}; - -// comparator that sorts by name in ascending order -const byName: Comparator = (a, b) => { - if (a.name < b.name) return -1; - if (a.name > b.name) return 1; - return 0; -}; - -// comparator that sorts by id in ascending order -const byId: Comparator = (a, b) => { - if (a.id < b.id) return -1; - if (a.id > b.id) return 1; - return 0; -}; - -// comparator that sorts by age in ascending order -const byAge: Comparator = (a, b) => { - if (a.age < b.age) return -1; - if (a.age > b.age) return 1; - return 0; -}; - -// creates a new comparator that sorts by name in descending order -const byNameDescending: Comparator = descending(byName); - -// `conditional` creates a new comparator that applies the provided comparator only -// if the provided predicate returns `true`. The `predicate` itself, takes the two arguments -// and returns a boolean value. -const byAgeIfEnabled: Comparator = conditional( - (a, b) => opts.isSortByAgeEnabled, -)(descending(byAge)); - -// combineComparator creates a new Comparator that combines the provided comparators in one. -// this comparator will sort by name in descending order, by age if enabled, -// and then by id in ascending order -const sortingCriteria = combineComparators( - byNameDescending, - byAgeIfEnabled, - byId, -); - -// participants array -const sorted = [p1, p2, p3].sort(sortingCriteria); -``` - -:::tip -The `Comparator` API is quite generic and can be used to sort any type of data. -Works great in pair with browser's [`Array.sort`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) API. -::: - -## Built-in common comparators - -The Participant Sorting API provides a set of common comparators that cover common sorting scenarios. -These comparators are specifically designed for participant sorting and offer convenience when defining sorting criteria. - -The built-in common comparators include: - -- `dominantSpeaker`: Sorts participants based on their dominance in the call. -- `speaking`: Sorts participants based on whether they are currently speaking. -- `screenSharing`: Sorts participants based on whether they are currently screen sharing. -- `publishingVideo`: Sorts participants based on whether they are currently publishing video. -- `publishingAudio`: Sorts participants based on whether they are currently publishing audio. -- `pinned`: Sorts participants based on whether they are pinned in the user interface. -- `reactionType(type)`: Sorts participants based on the type of reaction they have. -- `role(...roles)`: Sorts participants based on their assigned role. -- `name`: Sorts participants based on their names. - -All of these comparators are available in the `@stream-io/video-client` package and can be imported as follows: - -```ts -import { - dominantSpeaker, - speaking, - screenSharing, - publishingVideo, - publishingAudio, - pinned, - reactionType, - role, - name, -} from '@stream-io/video-client'; - -// ... -``` - -These built-in comparators serve as a starting point for sorting participants -and can be used individually or combined to create more complex sorting criteria. - -## Sorting customization on the call level - -The Participant Sorting API allows dynamic sorting customization during runtime. -Developers can utilize the `call.setSortParticipantsBy(comparator)` API to change the sorting criteria based on user interactions or application logic. -This flexibility empowers developers to provide sorting controls within their application, giving users the ability to customize participant sorting according to their preferences. - -Lets take a look at an example: - -```ts -import { - combineComparators, - dominantSpeaker, - publishingVideo, - publishingAudio, - screenSharing, - speaking, - reactionType, - pinned, -} from '@stream-io/video-client'; - -// ... boilerplate code - -// we take the existing call instance -const call: Call; - -// we create a new comparator that combines the built-in comparators -// and sorts participants by the following criteria: -const comparator = combineComparators( - pinned, // 1. pinned participants first - screenSharing, // 2. participants who are screensharing - dominantSpeaker, // 3. dominant speaker - reactionType('raised-hand'), // 4. participants with raised hand - speaking, // 5. participants currently speaking - publishingVideo, // 6. participants publishing video - publishingAudio, // 7. participants publishing audio - // 8. everyone else -); - -// will apply the new sorting criteria immediately -call.setSortParticipantsBy(comparator); -``` - -:::note -In some scenarios, we might want to have special sorting criteria for a specific component in our app. -For example, in the participant list component, we might want to sort participants by name. -::: - -```typescript -TODO: example; -``` - -## Built-in sorting presets - -To further simplify participant sorting, the Participant Sorting API offers built-in presets. -These presets are pre-configured sorting criteria linked to specific call types, reducing the effort required to define sorting rules. - -The following presets are available: - -- `defaultSortPreset`: The default sorting preset applicable to general call scenarios, the client uses this by default. -- `speakerLayoutSortPreset`: A preset specifically designed for the [`'default'` call type](../02-guides/05-call-types.mdx#default), optimizing participant sorting for speaker layout view. -- `livestreamOrAudioRoomSortPreset`: A preset tailored for the [`'livestream'`](../../guides/configuring-call-types#livestream) and [`'audio_room'`](../../guides/configuring-call-types#audio-room) call types, ensuring optimal participant sorting in livestream or audio room scenarios. The client will use this for `livestream` and `audio_room` call types. - -All of these presets are available in the `@stream-io/video-client` package and can be imported as follows: - -```ts -import { - defaultSortPreset, - speakerLayoutSortPreset, - livestreamOrAudioRoomSortPreset, -} from '@stream-io/video-client'; -``` - -## Sorting customization on the call type - -Sometimes, you want to keep your UI components free from sorting logic and instead, define sorting criteria per call type. -To do so, you can register your sorting presets for your custom call types or override the existing ones -by using our SDK's `CallTypes` registry. - -```ts -import { combineComparators, CallTypes, CallType } from '@stream-io/video-client'; - -// setup your custom sorting preset -const myCustomSortPreset = combineComparators(/* ... */); - -// update existing type -CallTypes.get('default').options.sortParticipantsBy = myCustomSortPreset; - -// register new type -CallTypes.register(new CallType('my-custom-type', { - options: { - sortParticipantsBy: myCustomSortPreset, - }, -}); -``` - -## Disabling participant sorting - -In some cases, you may want to disable participant sorting altogether. -This can be achieved by setting our special `noopComparator` as the sorting criteria of the Call or the CallType. - -```ts -import { noopComparator, useCall } from '@stream-io/video-client'; - -const call = useCall(); -call.setSortParticipantsBy(noopComparator()); -``` diff --git a/packages/client/docusaurus/docs/javascript/02-guides/11-playing-video-and-audio.mdx b/packages/client/docusaurus/docs/javascript/02-guides/11-playing-video-and-audio.mdx deleted file mode 100644 index 4363b71665..0000000000 --- a/packages/client/docusaurus/docs/javascript/02-guides/11-playing-video-and-audio.mdx +++ /dev/null @@ -1,84 +0,0 @@ ---- -id: playing-video-and-audio -title: Rendering Video and Audio -description: Learn how to correctly play participants' video and audio. ---- - -In this guide, we'll learn how to render and play participants' video and audio by using the JS SDK provided primitives. - -## Playing Video - -Our JS SDK exposes a low-level method that binds a video element to a participant's video track. -This method can be found in `call.bindVideoElement`. It takes three arguments: - -- the video element to bind to -- the participant's `sessionId` -- the kind of video track to bind to (either `videoTrack` or `screenShareTrack` for screen sharing) - -This method needs to be called only once, usually after the element is mounted in the DOM. - -```js -let videoElement = document.getElementById('my-video-element'); -if (!videoElement) { - videoElement = document.createElement('video'); - videoElement.id = 'my-video-element'; - document.body.appendChild(videoElement); - - // bind the video element to the participant's video track - // use the returned `unbind()` function to unbind the video element - const unbind = call.bindVideoElement( - videoElement, - participant.sessionId, - 'videoTrack', - ); -} -``` - -## Playing Screen Sharing - -Similar to the _Rendering Video_, a screenshare track can be rendered like this: - -```js -let screenElement = document.getElementById('my-screenshare-element'); -if (!screenElement) { - screenElement = document.createElement('video'); - screenElement.id = 'my-screenshare-element'; - document.body.appendChild(screenElement); - - // bind the video element to the participant's screen track - // use the returned `unbind()` function to unbind the video element - const unbind = call.bindVideoElement( - screenElement, - participant.sessionId, - 'screenShareTrack', // note the 'screenShareTrack' argument - ); -} -``` - -## Playing Audio - -Our JS SDK exposes a low-level method that binds an audio element to a participant's audio track. -This method can be found in `call.bindAudioElement`. It takes two arguments: - -- the audio element to bind to -- the participant's `sessionId` -- the kind of track to bind to (either `audioTrack` or `screenShareAudioTrack` for screen sharing) - -This method needs to be called only once, usually after the element is mounted in the DOM. - -```js -let audioElement = document.getElementById('my-audio-element'); -if (!audioElement) { - audioElement = document.createElement('audio'); - audioElement.id = 'my-audio-element'; - document.body.appendChild(audioElement); - - // bind the audio element to the participant's audio track - // use the returned `unbind()` function to unbind the audio element - const unbind = call.bindAudioElement( - audioElement, - participant.sessionId, - 'audioTrack', - ); -} -``` diff --git a/packages/client/docusaurus/docs/javascript/02-guides/12-visibility-tracking.mdx b/packages/client/docusaurus/docs/javascript/02-guides/12-visibility-tracking.mdx deleted file mode 100644 index 5ded56c32e..0000000000 --- a/packages/client/docusaurus/docs/javascript/02-guides/12-visibility-tracking.mdx +++ /dev/null @@ -1,85 +0,0 @@ ---- -title: Participant visibility tracking -description: Learn how to set up participant visibility tracking the screen. ---- - -Depending on the UI layout of your call, some participants may not be visible on the screen at all times. -This is especially true for large calls with many participants. We can optimize the performance and bandwidth usage of our SDK -by only subscribing to video for participants that are visible on the screen or within a certain viewport. - -To help you keep track of which participants are visible on the screen, our SDK provides a few helpers. -Once you set up visibility tracking to a participant, the client can automatically subscribe and unsubscribe to the video and screen stream of the given participant. - -## Setup a visibility tracker - -Any DOM element can be tracked for visibility. In a typical scenario, that would be the element that wraps the participant's "box". -You can register a visibility tracker with the SDK by calling the `call.trackElementVisibility` method: - -```js -const myParticipantElement = document.getElementById('my-participant-element'); - -// Track the visibility of the participant's element -// you can use the returned function to stop tracking an element. -const untrack = call.trackElementVisibility( - myParticipantElement, - participant.sessionId, - 'videoTrack', // or 'screenShareTrack' if you want to track screen share visibility -); -``` - -## Setup a viewport - -In our context, a _Viewport_ is a section/container on the screen that wraps participant's video elements. -Typically, this is a scrollable container. You can register a viewport with the SDK by calling the `call.setViewport` method: - -```js -const viewport = document.getElementById('my-participant-container'); - -// sets the viewport -// you can use the returned function to unset the viewport. -const unset = call.setViewport(viewport); -``` - -## Access the visible participants - -Every participant can have three visibility states for its video and screen share tracks: - -- `VISIBLE` - the track is visible on the screen -- `INVISIBLE` - the track is not visible on the screen -- `UNKNOWN` - the track's visibility is unknown (e.g. the participant is not tracked) - -Visibility state is available in the participant's state: - -```js -import { VisibilityState } from '@stream-io/video-client'; - -call.state.participants.forEach((participant) => { - const { viewportVisibilityState } = participant; - - // The participant's video visibility in the viewport - switch (viewportVisibilityState.videoTrack) { - case VisibilityState.VISIBLE: - // The participant's video track is visible in the viewport - break; - case VisibilityState.INVISIBLE: - // The participant's video track is not visible in the viewport - break; - case VisibilityState.UNKNOWN: - // The participant's video track visibility in the viewport is unknown - break; - } - - // The participant's screen share visibility in the viewport - switch (viewportVisibilityState.screenShareTrack) { - case VisibilityState.VISIBLE: - // The participant's screen share track is visible in the viewport - break; - case VisibilityState.INVISIBLE: - // The participant's screen share track is not visible in the viewport - break; - case VisibilityState.UNKNOWN: - // The participant's screen share track visibility in the viewport is unknown - break; - } -}); -``` diff --git a/packages/client/docusaurus/docs/javascript/02-guides/13-call-preview-thumbnail.mdx b/packages/client/docusaurus/docs/javascript/02-guides/13-call-preview-thumbnail.mdx deleted file mode 100644 index d22492bceb..0000000000 --- a/packages/client/docusaurus/docs/javascript/02-guides/13-call-preview-thumbnail.mdx +++ /dev/null @@ -1,47 +0,0 @@ ---- -title: Call Preview and Thumbnail -description: Learn how to show a call preview image (thumbnail) in your app. ---- - -Depending on your call type settings, our system can occasionally generate a preview image (thumbnail) for your call. -You can use this image to show a preview of the call in your app, typically in a lobby, waiting room, etc. - -## Get the preview thumbnail - -```tsx -const thumbnails = call.state.thumbnails; -const imageUrl = thumbnails.image_url; -console.log(imageUrl); - -// or -const subscription = call.state.thumbnails$.subscribe((thumbnails) => { - const imageUrl = thumbnails.image_url; - console.log(imageUrl); -}); - -// and don't forget to unsubscribe -subscription.unsubscribe(); -``` - -## Show the preview thumbnail - -Our SDK provides a helper method that takes care of the image loading and error handling for you. - -All we need is a reference to an `` element that has the correct dimensions. -We will take the dimensions in consideration, when loading the image, and our CDN will generate the correct image size for you. - -```tsx -// render image element in your view layer -Call Preview Thumbnail; - -// in your code -const myCallPreviewImg = document.getElementById('my-call-preview-img'); -const cleanup = call.bindCallThumbnailElement(myCallPreviewImg); - -// and don't forget to clean up -cleanup(); -``` diff --git a/packages/client/docusaurus/docs/javascript/02-guides/14-call-quality-rating.mdx b/packages/client/docusaurus/docs/javascript/02-guides/14-call-quality-rating.mdx deleted file mode 100644 index c082b1c2fd..0000000000 --- a/packages/client/docusaurus/docs/javascript/02-guides/14-call-quality-rating.mdx +++ /dev/null @@ -1,24 +0,0 @@ ---- -id: call-quality-rating -title: Call Quality Rating ---- - -Our Video SDK provides an API for collecting this feedback which later can be seen in the call stats section of our dashboard. -It is a good practice to ask your end users about their overall experience after the end of the call or, while being in a call. - -## Submit Feedback API - -```ts -import { Call } from '@stream-io/video-client'; - -let call: Call; -await call.submitFeedback( - rating, // a rating grade from 1 to 5, - { - reason: 'it worked great!', // the main feedback - custom: { - // ... any extra properties that you wish to collect - }, - }, -); -``` diff --git a/packages/client/docusaurus/docs/javascript/02-guides/15-transcriptions.mdx b/packages/client/docusaurus/docs/javascript/02-guides/15-transcriptions.mdx deleted file mode 100644 index 2ed8f12e79..0000000000 --- a/packages/client/docusaurus/docs/javascript/02-guides/15-transcriptions.mdx +++ /dev/null @@ -1,63 +0,0 @@ ---- -id: transcriptions -title: Transcriptions -description: Documentation on implementing transcriptions. ---- - -# Transcriptions - -Enabling your application to provide a transcript for a call can be beneficial for your users. We understand, though, that this can be a challenging feature to implement/support. - -This is why, the Stream Video SDK comes with out of the box Transcription support that you can easily manage. - -The `Call` object provides a few levels of control. The first one is in the `call.state.settings.transcription` where you can find settings related to transcription, as they have been configured from the dashboard. -The `mode` property defines the feature's availability with: - -- `available`: the feature is available for your call and can be enabled. -- `disabled`: the feature is not available for your call. In this case, it's a good idea to "hide" any UI element you have related to transcription. -- `auto-on`: the feature is available, and it will be enabled automatically, once the user is connected on the call. - -The second level of control is the `call.state.transcribing` which allows you to check if the transcription is enabled at any given time. - -For both of these, we expose a utility observables that we recommend you to subscribe on: - -```typescript -import { Call } from '@stream-io/video-client'; - -let call: Call; - -const unsubscribeSettings = call.state.settings$.subscribe((settings) => { - if (!settings) return; - const { transcription } = settings; - // ... -}); - -const unsubscribeIsTranscribing = call.state.transcribing$.subscribe( - (isTranscribing) => { - console.log('Is transcribing', isTranscribing); - }, -); - -// clean up -unsubscribeSettings(); -unsubscribeIsTranscribing(); -``` - -To enable call transcriptions you can use the following two methods: - -```typescript -import { Call, TranscriptionSettingsModeEnum } from '@stream-io/video-client'; - -let call: Call; - -const isTranscribing = call.state.transcribing; -if ( - call.state.settings?.transcriptions.mode !== - TranscriptionSettingsModeEnum.DISABLED && - !isTranscribing -) { - await call.startTranscription(); -} else { - await call.stopTranscription(); -} -``` diff --git a/packages/client/docusaurus/docs/javascript/02-guides/_category_.json b/packages/client/docusaurus/docs/javascript/02-guides/_category_.json deleted file mode 100644 index a2907d8ee4..0000000000 --- a/packages/client/docusaurus/docs/javascript/02-guides/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Core concepts", - "position": 3 -} diff --git a/packages/client/docusaurus/docs/javascript/10-advanced/01-troubleshooting-guide.mdx b/packages/client/docusaurus/docs/javascript/10-advanced/01-troubleshooting-guide.mdx deleted file mode 100644 index 12450673c8..0000000000 --- a/packages/client/docusaurus/docs/javascript/10-advanced/01-troubleshooting-guide.mdx +++ /dev/null @@ -1,69 +0,0 @@ ---- -id: troubleshooting -title: Troubleshooting -description: A troubleshooting guide for common issues ---- - -There are several possible integration issues that can lead to calls not being established. -This section will cover the most frequent ones. - -## Connection issues - -Connection issues usually happen when you provide an invalid token during the SDK setup. When this happens, a web socket connection can't be established with our backend, resulting in errors when trying to connect to a call. - -### Expired tokens - -When you initialize the `StreamVideoClient` object, you provide a token, as described [here](../../guides/client-auth). -The tokens generated in the docs have an expiry date, therefore, please make sure to always use a token with a valid expiry date. -You can check the contents of a JWT token on websites like [this one](https://jwt.io). - -Additionally, when expiring tokens are used, you need to provide a `tokenProvider` when creating `StreamVideoClient`, -that will be invoked when the existing token expires. -This is your chance to update the token by generating a new one on your backend. - -### Wrong secret for token generation - -When you start integrating the SDK into your app, you might copy-paste the token from the docs into your project. -However, that will not work. - -Tokens are generated with the help of the app secret (available in your dashboard), and are unique per app id. -Your app id is different from the demo apps we have as examples in our docs. - -On websites like [this one](https://jwt.io), you can verify if the token is signed with the correct signature. - -While developing, you can manually generate tokens by providing your secret and the user's ID [here](https://getstream.io/chat/docs/javascript/tokens_and_authentication/?language=javascript). -However, note that for production usage, your backend would need to generate these tokens. - -### User-token mismatch - -The token can be valid and correct, but for the wrong user. -Make sure that the token you provide matches the id of the user that is used when creating the `StreamVideoClient` object. - -### Debuggers - -Our SDK will log errors or warnings in browser's console unless disabled. We try to have a descriptive error messages -that should guide you to faster problem resolution. Also, using browser's Network tab can help you spot failing network requests and the reasons for it. - -## Ringing calls issues - -Ringing calls issues usually present themselves in a failure to show the incoming call screen or failure to deliver a notification event to the user we're trying to call. - -### Members of a call - -One common issue is that you only specify one user and try to call the same user on another device. -This will not work, if you are the caller, you will not receive a notification that you're being called - you can't call yourself. - -As you would do it in the real world, you would need to specify another member (or members) that you want to call. -Another important note - that member should also exist in Stream's platform (it must have connected at least once). -This is needed because we need to know the user's device and where to send the call notification. - -### Reusing a call id - -Call IDs in general can be reused - you can join a call with the same id many times. -However, the ringing is done only once per call ID. -Therefore, if you implement calls with ringing, make sure that you provide a unique ID every time, -in order for the ring functionality to work. One option is to use a `uuid` as a call ID. - -## Logging - -For easier debugging, you can turn on more verbose logging. To do that, [follow these instructions](../../guides/client-auth/#logging). diff --git a/packages/client/docusaurus/docs/javascript/10-advanced/04-ringing.mdx b/packages/client/docusaurus/docs/javascript/10-advanced/04-ringing.mdx deleted file mode 100644 index c8f9527c1a..0000000000 --- a/packages/client/docusaurus/docs/javascript/10-advanced/04-ringing.mdx +++ /dev/null @@ -1,130 +0,0 @@ ---- -id: ringing-calls -title: Ringing calls -description: An overview of how to create ringing calls ---- - -The `Call` object provides several options to ring and notify users about a call. - -## Create call - -To create a ring call, we need to set the `ring` flag to `true` and provide the list of members we want to call. -It is important to note that the caller should also be included in the list of members. - -```typescript -await client.call('default', 'test-outgoing-call').getOrCreate({ - // highlight-next-line - ring: true, - data: { - members: [ - // highlight-next-line - { user_id: 'myself' }, - { user_id: 'my friend' }, - ], - }, -}); -``` - -### Call creation options - -The following options are supported when creating a call: - -| Option | Description | Default | -| ---------- | --------------------------------------------------------------------------------------------------------------- | ------- | -| `members` | A list of members to add to this call. You can specify the role and custom data on these members | - | -| `custom` | Any custom data you want to store | - | -| `settings` | You can overwrite certain call settings for this specific call. This overwrites the call type standard settings | - | -| `startsAt` | When the call will start. Used for calls scheduled in the future, livestreams, audio rooms etc | - | -| `team` | Restrict the access to this call to a specific team | - | -| `ring` | If you want the call to ring for each member | `false` | -| `notify` | If you want the call to nofiy each member by sending push notification. | `false` | - -This step will start the signaling flow. -The caller will automatically join the call once the first callee accepts the call. -The call will automatically stop if every callee rejects the call. - -## Watch for incoming and outgoing calls - -The easiest way to watch for incoming and outgoing calls is to use the `client.state.calls$` Observable. - -```typescript -import { CallingState } from '@stream-io/video-client'; - -client.state.calls$.subscribe((calls) => { - const incomingCalls = calls.filter( - (call) => - call.isCreatedByMe === false && - call.state.callingState === CallingState.RINGING, - ); - console.log('Incoming calls', incomingCalls); - - const outgoingCalls = calls.filter( - (call) => - call.isCreatedByMe === true && - call.state.callingState === CallingState.RINGING, - ); - console.log('Outgoing calls', outgoingCalls); -}); -``` - -## Canceling a call - -A caller can cancel an outgoing call until the first callee accepts the call. Canceling a call will stop the signaling flow. - -```typescript -await call.leave(); -``` - -Please note that calling `call.leave()` after joining the call won't stop the signaling flow. - -## Accepting a call - -A callee can accept or reject an incoming call. To accept and join the call: - -```typescript -await call.join(); -``` - -Please note that it's possible to join multiple calls. If you only want to allow one active call, you must leave joined calls before accepting an incoming call. - -## Rejecting a call - -A callee can accept or reject an incoming call. To reject the call: - -```typescript -await call.leave({ reject: true }); -``` - -## Leave call - -To leave a joined call, you can use the `leave` method: - -```typescript -await call.leave(); -``` - -## End call - -Ending a call requires a [special permission](../../guides/permissions-and-moderation). This action terminates the call for everyone. - -```typescript -await call.endCall(); -``` - -## Notifying - -In some cases, you just want to notify users that you joined a call, instead of ringing. -To do this, you should use the `notify` option: - -```typescript -await call.getOrCreate({ notify: true }); -``` - -When notify is true, a regular push notification will be sent to all the members. -This can be useful for livestreams apps or huddles. - -Similarly to ringing, you can use the get method if you are sure that the call exists: - -```typescript -await call.get({ notify: true }); -``` diff --git a/packages/client/docusaurus/docs/javascript/10-advanced/04-screensharing.mdx b/packages/client/docusaurus/docs/javascript/10-advanced/04-screensharing.mdx deleted file mode 100644 index a0eef95340..0000000000 --- a/packages/client/docusaurus/docs/javascript/10-advanced/04-screensharing.mdx +++ /dev/null @@ -1,92 +0,0 @@ ---- -id: screensharing -title: Screen Sharing -description: Managing Screen Sharing ---- - -If you want to see the device management API in action, you can check out [the sample app](https://github.com/GetStream/stream-video-js/tree/main/sample-apps/client/ts-quickstart). - -## Screen Sharing - -Our Video SDK provides an easy ways to enable screensharing in your application. -In the following examples, we would learn to control and configure screen sharing. - -:::note -Screen Sharing is supported only on Desktop browsers. For more details, -please refer to the [Browser Compatibility](https://developer.mozilla.org/en-US/docs/Web/API/Screen_Capture_API/Using_Screen_Capture#browser_compatibility) section. -::: - -For a user to be able to share their screen, they must have the `screenshare` capability -(provided through the `Screenshare` permission) configured for the call they are in. - -Screen sharing can be enabled or disabled on the dashboard for your call type: - -![Screenshot shows screensharing dashboard setting](../assets/02-guides/04-screensharing/screensharing-dashboard.png) - -### Start/Stop Screen Sharing - -```typescript -call.screenShare.toggle(); - -// or -call.screenShare.enable(); -call.screenShare.disable(); -``` - -### Screen Sharing Status - -Here is how you can access the status of screen sharing: - -```typescript -call.screenShare.state.status; // enabled, disabled or undefined - -// or, if you want to subscribe to changes -call.screenShare.state.status$.subscribe((status) => { - // enabled, disabled or undefined -}); -``` - -### Screen Sharing Settings - -The behavior of the screen share video track can be customized, and a few parameters can be set: - -```typescript -call.screenShare.setSettings({ - maxFramerate: 15, // will be clamped between 1 and 15 fps - maxBitrate: 1500000, // will use at most 1.5Mbps -}); - -call.screenShare.enable(); -``` - -### Render Screen Share - -Please follow our [Playing Video and Audio guide](../../guides/playing-video-and-audio/). - -## Screen Share Audio - -### Start/Stop Screen Share Audio - -```typescript -// enable it -call.screenShare.enableScreenShareAudio(); - -// publish video and audio (if available, and supported by the browser) -call.screenShare.enable(); - -// disable it -call.screenShare.disableScreenShareAudio(); -``` - -### Play Screen Share Audio - -Please follow our [Rendering Video and Audio guide](../../guides/playing-video-and-audio/). - -### Caveats - -Screen Share Audio has limited support across browsers and platforms. -For most up-to-date information, please take a look at [Browser Compatibility](https://developer.mozilla.org/en-US/docs/Web/API/Screen_Capture_API/Using_Screen_Capture#browser_compatibility). - -In addition to that, there are a [few caveats](https://caniuse.com/?search=getDisplayMedia) that you should be aware of: - -- On Windows, the entire system audio can be captured, but on MacOS and Linux, only the audio of a tab can be captured. diff --git a/packages/client/docusaurus/docs/javascript/10-advanced/05-join-from-link.mdx b/packages/client/docusaurus/docs/javascript/10-advanced/05-join-from-link.mdx deleted file mode 100644 index e2b2d749ef..0000000000 --- a/packages/client/docusaurus/docs/javascript/10-advanced/05-join-from-link.mdx +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: Joining a call from a link -description: Learn how you can add a flow where users can join from a link. ---- - -Many video calling apps that offer video and audio conferencing features, support joining a call from a link. -This is a great way to allow users to join a call without having to go through many complex steps. - -In the next few steps, we will show how such a flow can be implemented using the Stream's Video SDKs. - -## Define the URL structure - -The first step is to define the URL structure that will be used to join a call. -Typically, the URL will contain the following information: - -- your app's domain -- an optional route pointing to the calling features of your app -- the call ID -- optional call type information, in case your app supports multiple call types - -In our example, we will use the following URL structure: - -- `https://myapp.com/join?call_id=123&call_type=default` - -## Get call information and join a call - -Once the user opens the link, your app needs to read the call ID and call type from the URL as they are required parameters for our SDK. -Next, we will use the call information to setup and join the call. - -In the example below, we will use standard browser APIs to read the call ID and call type from the URL. - -```ts -import { StreamVideoClient } from '@stream-io/video-client'; - -const urlParams = new URLSearchParams(window.location.search); -const callId = urlParams.get('call_id'); -const callType = urlParams.get('call_type') || 'default'; // or your custom call type - -// initialize the client, call and join the call -const client = new StreamVideoClient({ apiKey, user, token }); -const call = client.call(callType, callId); -await call.join(); -``` diff --git a/packages/client/docusaurus/docs/javascript/10-advanced/06-chat-with-video.mdx b/packages/client/docusaurus/docs/javascript/10-advanced/06-chat-with-video.mdx deleted file mode 100644 index 658514bbe9..0000000000 --- a/packages/client/docusaurus/docs/javascript/10-advanced/06-chat-with-video.mdx +++ /dev/null @@ -1,108 +0,0 @@ ---- -title: Chat Integration -description: How to integrate chat & video ---- - -There are multiple use-cases where you might want to use audio/video calls combined with chat messaging. You might want to offer chat capabilities during a video meeting, or you might need to build a chat messaging tool where users can call each other. Either way, Stream's video and [chat](https://getstream.io/chat/docs/) APIs are here to help. - -The following guide gives a conceptual overview about how you can integrate Stream's video and chat API together. - -## Install dependencies - -Stream provides separate packages for chat and video integration, so you'll have to install two packages. - -- For chat, you can choose [any of the chat SDK](https://getstream.io/chat/sdk/) that fits your needs. -- For video, depending on your package manager of choice run one of these commands: - -```bash -yarn add @stream-io/video-client -# or -npm install @stream-io/video-client -``` - -## Create clients - -Just like you installed two packages, you need to create two clients, one for video, and for chat. - -- For video, follow the [Client & Authentication](../../guides/client-auth) guide. -- For chat, you will find more information about this in the documentation of the selected SDK. - -You can create the two clients using the same API key, user ID and token/token provider. - -## Connect calls and channels - -It's up to you how you want to connect calls and channels together, but we'll cover some common use-cases below. - -### Create a new channel for each call - -If you're building an audio/video call and want to offer participants the chance to chat, you can create a separate channel for each call. The simplest way to connect the channel to the call, is to use the call id as the channel id. - -Example code: - -```typescript -const call = videoClient.call('default', 'all-hands-meeting'); -await call.getOrCreate(); - -const channel = chatClient.channel('messaging', 'all-hands-meeting'); -await channel.create(); -``` - -- You can read more about [call types](../../guides/configuring-call-types/) to find the best one for your needs. -- You can read more about [channel types](https://getstream.io/chat/docs/javascript/channel_features/?language=javascript) to find the best one for your needs. - -If you're reusing the call id (for example for recurring calls), you can create a new channel for each call session: - -```typescript -const call = videoClient.call('default', 'all-hands-meeting'); -await call.join(); -const channel = chatClient.channel('messaging', call.session.id); -await channel.create(); -``` - -#### Call members - -You need to manually sync the call members with the channel members. You have two options for this: - -- If you're using calls where anyone with the link can join, you need to make sure that anyone can read the channel of the given call. You can do that by using the [chat API's permission system](https://getstream.io/chat/docs/javascript/chat_permission_policies/?language=javascript). -- If your calls are limited to members, you can create the channel with the given members: - -```typescript -const channel = chatClient.channel('messaging', 'all-hands-meeting', { - members: [ - /* user ids of call members */ - ], -}); -``` - -Whenever a member is added to the call, or removed from it, you need to [update the channel as well](https://getstream.io/chat/docs/javascript/channel_members/?language=javascript): - -```typescript -await channel.addMembers(['thierry', 'josh']); -await channel.removeMembers(['tommaso']); -``` - -### Connect a call to a channel - -If you're building a messaging tool where users can call each other, you can connect a call to a channel. - -Example code: - -```typescript -import { MemberRequest } from '@stream-io/video-client'; - -await videoClient.call('default', 'call-id').getOrCreate({ - ring: true, - data: { - custom: { - channelCid: channel.cid, - }, - members: Object.values(channel.state.members).map( - (member) => ({ - user_id: member.user_id!, - }), - ), - }, -}); -``` - -If you want to list the calls for a given channel, you can use filtering on `custom` data, for more information see the [Querying Calls guide](../../guides/querying-calls/#filters). diff --git a/packages/client/docusaurus/docs/javascript/10-advanced/06-events.mdx b/packages/client/docusaurus/docs/javascript/10-advanced/06-events.mdx deleted file mode 100644 index c7f20cba3e..0000000000 --- a/packages/client/docusaurus/docs/javascript/10-advanced/06-events.mdx +++ /dev/null @@ -1,197 +0,0 @@ ---- -id: events -title: Events -description: How to listen to events ---- - -In most cases, you can simply use the [reactive state store](../../guides/call-and-participant-state) to be notified about state changes. -However, for some advanced use cases, you might need to subscribe to the underlying WebSocket events. - -## List of events - -### Client events - -Client events are always delivered if a user is connected to the client using the `connectUser` method. - -The list of client events: - -| Name | Description | -| ------------------ | ----------------------------------------------------------- | -| `connection.ok` | Fired when the authentication process finished successfully | -| `connection.error` | Fired when the WS connections fails | - -### Call events - -These events are related to a specific call. Some of these events are only delivered to clients that are watching the specific call. There are 3 ways to watch a call: - -1. Call the `queryCalls` method with the `watch` option set to `true`: - -```typescript -import { StreamVideoClient } from '@stream-io/video-client'; - -let client: StreamVideoClient; - -await client.queryCalls({ - // ... - watch: true, -}); -``` - -2. Join a call: - -```typescript -import { StreamVideoClient } from '@stream-io/video-client'; - -let client: StreamVideoClient; - -await client.call('default', 'test-call').join(); -``` - -3. Watch a call: - -```typescript -import { StreamVideoClient } from '@stream-io/video-client'; - -let client: StreamVideoClient; - -await client.call('default', 'test-call').getOrCreate(); -// or -await client.call('default', 'test-call').get(); -``` - -The list of call events: - -| Name | Description | Delivered to | -| --------------------------------- | -------------------------------------------------------------- | ---------------- | -| `call.accepted` | A user accepts a notification to join a call | All call members | -| `call.blocked_user` | A user is blocked from the call | Call watchers | -| `call.created` | The call was created | All call members | -| `call.ended` | The call was ended | All call members | -| `call.hls_broadcasting_failed` | The call failed to broadcast | Call watchers | -| `call.hls_broadcasting_started` | The call started to broadcast | Call watchers | -| `call.hls_broadcasting_stopped` | The call stopped broadcasting | Call watchers | -| `call.live_started` | The call left backstage mode | Call watchers | -| `call.member_added` | One or more members were added to the call | All call members | -| `call.member_removed` | One or more members were removed from the call | All call members | -| `call.member_updated` | One or more members were updated | All call members | -| `call.member_updated_permission` | One or more members' role was updated | All call members | -| `call.notification` | A user is calling all call members | All call members | -| `call.permission_request` | A user is requesting permissions | Call watchers | -| `call.permissions_updated` | A member's permissions were updated | Call watchers | -| `call.reaction_new` | A new reaction was sent | Call watchers | -| `call.recording_failed` | A recording failed | Call watchers | -| `call.recording_ready` | A recording is ready | Call watchers | -| `call.recording_started` | A recording has been started | Call watchers | -| `call.recording_stopped` | The recording was stopped | Call watchers | -| `call.rejected` | A user declined to join the call | All call members | -| `call.ring` | A user is calling all call members | All call members | -| `call.session_ended` | A call session ended (all participants have left the call) | Call watchers | -| `call.session_participant_joined` | A participant joined to the call sessions | Call watchers | -| `call.session_participant_left` | A participant left a call session | Call watchers | -| `call.session_started` | A call session started (the first participant joined the call) | Call watchers | -| `call.unblocked_user` | A user is unblocked | Call watchers | -| `call.updated` | The call was updated | Call watchers | -| `custom` | Custom event | All call members | - -## Listening to client and call events - -You can use the `on` method of the `StreamVideoClient` instance to subscribe to client and call WebSocket events. - -The `on` method takes the type of the event you want to subscribe to or the 'all' keyword indicating that you want to be notified about all events. - -The event handler will receive an object with type [`StreamVideoEvent`](https://github.com/GetStream/stream-video-js/blob/main/packages/client/src/coordinator/connection/types.ts) that has a `type` attribute that tells the type of the event. The available event types are described by the [`EventTypes`](https://github.com/GetStream/stream-video-js/blob/main/packages/client/src/coordinator/connection/types.ts) type. - -The `on` method returns a method that can be called to unsubscribe from WebSocket events. - -Subscribing to all events: - -```typescript -import { StreamVideoClient, StreamVideoEvent } from '@stream-io/video-client'; - -let client: StreamVideoClient; - -// Subscribe to all events -const unsubscribe = client.on('all', (event: StreamVideoEvent) => { - console.log(event); -}); - -// Unsubscribe -unsubscribe(); -``` - -Subscribing to `call.created` events: - -```typescript -import { StreamVideoClient, StreamVideoEvent } from '@stream-io/video-client'; - -let client: StreamVideoClient; - -// Subscribe to all events -const unsubscribe = client.on('call.created', (event: StreamVideoEvent) => { - if (event.type === 'call.created') { - console.log(`Call created: ${event.call_cid}`); - } -}); - -// Unsubscribe -unsubscribe(); -``` - -## Listening to call events - -You can use the `on` method of the `call` instance to subscribe to WebSocket events belonging to a specific call. - -The `call.on` method takes the type of the event you want to subscribe to. -The event handler will receive an object with type [`StreamCallEvent`](https://github.com/GetStream/stream-video-js/blob/main/packages/client/src/coordinator/connection/types.ts) that has a `type` attribute that tells the type of the event. -The available event types are described by the [`CallEventTypes`](https://github.com/GetStream/stream-video-js/blob/main/packages/client/src/coordinator/connection/types.ts) type. -The `call.on` method returns a method that can be called to unsubscribe from WebSocket events. - -**Example:** Subscribing to `call.reaction_new` event: - -```typescript -import { Call, StreamVideoEvent } from '@stream-io/video-client'; - -let call: Call; - -// Subscribe to new reactions event -const unsubscribe = call.on('call.reaction_new', (event: StreamVideoEvent) => { - if (event.type === 'call.reaction_new') { - console.log(`New reaction: ${event.reaction}`); - } -}); - -// Unsubscribe -unsubscribe(); -``` - -## Custom events - -You can send custom events between the call participants using the `sendCustomEvent` method of the `call` instance. -Note that the payload for these events is limited to 5KB in size. - -```typescript -import { - Call, - CustomVideoEvent, - StreamVideoEvent, -} from '@stream-io/video-client'; - -let call: Call; - -// sending a custom event -await call.sendCustomEvent({ - type: 'my-event-type', - payload: { - foo: 'bar', - }, -}); - -// receiving a custom event -call.on('custom', (event: StreamVideoEvent) => { - const customEvent = event as CustomVideoEvent; - const payload = customEvent.custom; - if (payload.type === 'my-event-type') { - console.log(payload.foo); - } -}); -``` diff --git a/packages/client/docusaurus/docs/javascript/10-advanced/08-recording.mdx b/packages/client/docusaurus/docs/javascript/10-advanced/08-recording.mdx deleted file mode 100644 index 8c63a34baa..0000000000 --- a/packages/client/docusaurus/docs/javascript/10-advanced/08-recording.mdx +++ /dev/null @@ -1,63 +0,0 @@ ---- -id: recording -title: Recording -description: Recording ---- - -One highly sought-after feature in many communication applications is call recording. Whether it's for legal compliance, quality assurance, or simply for future reference, the ability to record calls is essential in numerous scenarios. - -This documentation article serves as a guide for implementing a call recording feature in your application. We will explore the technical aspects and best practices involved in capturing and storing recording data during calls. - -## Recording calls - -The `Call` objects exposes the call recording API. To start recording, we simply invoke `call.startRecording()`. To stop recording, we use `call.stopRecording()`. -To determine, whether a call recording is in progress, we use `call.state.recording$` Observable. This may serve us well, when we want to provide visual clues about the recording state. We have to be aware, that it can take few moments until a call recording starts. We recommend to keep own state signalling that the call recording is starting, but has not begun yet. - -```typescript -import { Call } from '@stream-io/video-client'; - -let call: Call; -await call.startRecording(); - -// to stop the recording -await call.stopRecording(); -``` - -### Permissions - -To start and stop recording, the user has to have corresponding permissions. - -```typescript -import { Call, OwnCapability } from '@stream-io/video-client'; -let call: Call; - -if (call.permissionsContext.hasPermission(OwnCapability.START_RECORD_CALL)) { - await call.startRecording(); -} else { - // handle lack of permissions -} - -// to stop the recording -if (call.permissionsContext.hasPermission(OwnCapability.STOP_RECORD_CALL)) { - await call.stopRecording(); -} else { - // handle lack of permissions -} -``` - -To learn more about permissions, take a look at our [permissions guide](../../guides/permissions-and-moderation). - -## Acquiring call recordings data - -The call recording data can be retrieved by calling the `Call` method `queryRecordings()`. By default, it retrieves all the call recordings for a given call. However, it accepts a single argument `callSessionId` that allows us to retrieve only recordings done during a single call session. The method returns `ListRecordingsResponse` that carries an array of `CallRecording` objects accessible through `recordings` key. - -:::note -Multiple recordings can be made during a single call session, but a single call CID can be reused for multiple sessions, too. -::: - -If we wanted to keep and up-to-date list of call recordings during an active call, we would need to query them at the start of the call as well as with every [`call.recording_stopped` event](../events). - -:::note -The call recording is not immediately available when the `call.recording_stopped` event is delivered. -It may take 30 or more seconds for a recording to be available. Once the recording is available, we will be notified with `call.recording_ready` event. -::: diff --git a/packages/client/docusaurus/docs/javascript/10-advanced/09-broadcasting.mdx b/packages/client/docusaurus/docs/javascript/10-advanced/09-broadcasting.mdx deleted file mode 100644 index 29a3d7242a..0000000000 --- a/packages/client/docusaurus/docs/javascript/10-advanced/09-broadcasting.mdx +++ /dev/null @@ -1,73 +0,0 @@ ---- -id: broadcasting -title: Broadcasting -description: Approaches to broadcasting & RTMP ---- - -Broadcasting serves as a means of transmitting live or pre-recorded content to a wide audience. - -We can choose from two approaches to broadcasting the media: - -1. [HLS](https://en.wikipedia.org/wiki/HTTP_Live_Streaming) - slight delay, better buffering -2. [WebRTC](https://webrtc.org/) - lower latency, less reliability - -It is up to the integrators to decide, what approach will be used in their apps for the audience to consume the streams. - -## Call type for broadcasting - -Stream infrastructure recognizes few pre-built call types. Among them, the type [`livestream` type](../../guides/configuring-call-types#livestream) is the best suited for broadcasting events. When a `livestream` call is created, it is set to `backstage` mode by default. - -## Starting and stopping the broadcasting - -We have the following `Call` methods at our disposal to start and stop the broadcasting: - -```ts -call.startHLS(); -call.stopHLS(); -``` - -Once started broadcasting, the data source URL is available through `playlist_url` property accessible through the `Call` state: - -```ts -// omitted code ... -const call: Call; -// m3u8 playlist URL -const playlistUrl = call.state.egress?.hls?.playlist_url; -``` - -To play the video over HLS, a third-party library is required (for example [HLS.js](https://nochev.github.io/hls.js/docs/html/)). - -If you want to use WebRTC based streaming, your viewers would have to follow the [join steps](../../guides/joining-and-creating-calls) discussed previously. - -## Broadcasting via RTMP - -Our systems provide first-class support for streaming from RTMP clients as [OBS](https://obsproject.com/). -To connect your OBS project in a Stream Call, please follow the next steps: - -### RTMP URL and stream key - -Our `call` instance exposes its RTMP address through `call.state.ingress` and `call.state.ingress$` observable. -`Stream Key` in our case is a standard [user token](../../guides/client-auth/#generating-a-token). - -You can take this information and use it to configure OBS: - -```typescript -const call = client.call(type, id); -await call.getOrCreate(); - -const { ingress } = call.state; - -const rtmpURL = ingress?.rtmp.address; -const streamKey = myUserAuthService.getUserToken(rtmpUserId); - -console.log('RTMP url:', rtmpURL, 'Stream key:', streamKey); -``` - -### Configure OBS - -- Go to Settings > Stream -- Select `custom` service -- Server: enter the `rtmpURL` logged in the console -- Stream Key: enter the `streamKey` logged in the console - -Press `Start Streaming` in OBS and the RTMP stream will now show up in your call just like a regular video participant. diff --git a/packages/client/docusaurus/docs/javascript/10-advanced/10-custom-data.mdx b/packages/client/docusaurus/docs/javascript/10-advanced/10-custom-data.mdx deleted file mode 100644 index 89f3ec8647..0000000000 --- a/packages/client/docusaurus/docs/javascript/10-advanced/10-custom-data.mdx +++ /dev/null @@ -1,51 +0,0 @@ ---- -id: custom-data -title: Custom Data -description: Managing Call's custom data ---- - -Custom data is additional information that can be added to the default data of Stream Call. -It is an object with key-value pairs that can be attached to users, events, and pretty much almost every domain model in the Stream Video SDK. - -The type definition is the following: - -```typescript -export type Custom = { - [key: string]: any; // where `any` should be JSON-serializable value -}; -``` - -## Adding custom data - -Adding extra data can be done through the Server-Side SDKs or through the Client SDKs. -In the Stream Video SDK, you can add extra data when creating/updating a user, event, reaction and other models. - -As a simple example, let's see how you can add a new `topic` field to a `call` instance. - -```typescript -const call = client.call(type, id); -await call.getOrCreate({ - data: { custom: { topic: 'Monthly sync' } }, -}); - -// or update a custom field -await call.update({ - custom: { topic: 'Weekly sync' }, -}); -``` - -## Reading custom data - -The custom data is exposed through the `custom` state property (and `custom$` observable): - -```typescript -const call = client.call(type, id); -await call.getOrCreate(); - -const topic = call.state.custom?.topic; -console.log('The topic of the current call is:', topic); - -call.state.custom$.subscribe((custom) => { - console.log('The topic is changed to:', custom?.topic); -}); -``` diff --git a/packages/client/docusaurus/docs/javascript/10-advanced/11-session-timers.mdx b/packages/client/docusaurus/docs/javascript/10-advanced/11-session-timers.mdx deleted file mode 100644 index 5204766d59..0000000000 --- a/packages/client/docusaurus/docs/javascript/10-advanced/11-session-timers.mdx +++ /dev/null @@ -1,64 +0,0 @@ ---- -id: session-timers -title: Session Timers ---- - -A session timer allows you to limit the maximum duration of a call. The duration -[can be configured](https://getstream.io/video/docs/api/calls/#session-timers) -for all calls of a certain type, or on a per-call basis. When a session timer -reaches zero, the call automatically ends. - -## Creating a call with a session timer - -Let's see how to create a single call with a limited duration: - -```ts -const callType = 'default'; -const callId = 'test-call'; - -const call = client.call(callType, callId); -await call.getOrCreate({ - data: { - settings_override: { - limits: { - max_duration_seconds: 3600, - }, - }, - }, -}); -``` - -This code creates a call with a duration of 3600 seconds (1 hour) from the time -the session is starts (a participant joins the call). - -After joining the call with the specified `max_duration_seconds`, you can -examine a session's `timer_ends_at` field, which provides the timestamp when the -call will end. When a call ends, all participants are removed from the call. - -```ts -await call.join(); -console.log(call.state.session?.timer_ends_at); -``` - -## Extending a call - -​You can also extend the duration of a call, both before or during the call. To -do that, you should use the `call.update` method: - -```ts -await call.get(); -// extend by 1 minute -const duration = call.state.settings?.limits.max_duration_seconds + 60; - -await call.update({ - settings_override: { - limits: { - max_duration_seconds: duration, - }, - }, -}); -``` - -If the call duration is extended, the `timer_ends_at` is updated to reflect this -change. Call participants will receive the `call.updated` event to notify them -about this change. diff --git a/packages/client/docusaurus/docs/javascript/10-advanced/12-manual-video-quality-selection.mdx b/packages/client/docusaurus/docs/javascript/10-advanced/12-manual-video-quality-selection.mdx deleted file mode 100644 index 008fd4989d..0000000000 --- a/packages/client/docusaurus/docs/javascript/10-advanced/12-manual-video-quality-selection.mdx +++ /dev/null @@ -1,59 +0,0 @@ ---- -id: manual-video-quality-selection -title: Manual Video Quality Selection ---- - -By default, our SDK chooses the incoming video quality that best matches the size of a video element for a given participant. It makes less sense to waste bandwidth receiving Full HD video when it's going to be displayed in a 320 by 240 pixel rectangle. - -However, it's still possible to override this behavior and manually request higher resolution video for better quality, or lower resolution to save bandwidth. It's also possible to disable incoming video altogether for an audio-only experience. - -## Overriding Preferred Resolution - -To override the preferred incoming video resolution, use the `call.setPreferredIncomingVideoResolution` method: - -```js -await call.setPreferredIncomingVideoResolution({ width: 640, height: 480 }); -``` - -:::note -Actual incoming video quality depends on a number of factors, such as the quality of the source video, and network conditions. Manual video quality selection allows you to specify your preference, while the actual resolution is automatically selected from the available resolutions to match that preference as closely as possible. -::: - -It's also possible to override the incoming video resolution for only a selected subset of call participants. The `call.setPreferredIncomingVideoResolution` method optionally takes an array of participant session identifiers as its second argument. Session identifiers can be obtained from the call participant state: - -```js -const [firstParticipant, secondParticipant] = call.state.participants; -// Set preferred incoming video resolution for the first two participants only: -await call.setPreferredIncomingVideoResolution({ width: 640, height: 480 }, [ - [firstParticipant.sessionId, secondParticipant.sessionId], -]); -``` - -Calling this method will enable incoming video for the selected participants if it was previously disabled. - -To clear a previously set preference, pass `undefined` instead of resolution: - -```js -// Clear resolution preference for selected participants: -await call.setPreferredIncomingVideoResolution(undefined, [ - participant.sessionId, -]); -// Clear resolution preference for all participants: -await call.setPreferredIncomingVideoResolution(undefined); -``` - -## Disabling Incoming Video - -To completely disable incoming video (either to save data, or for an audio-only experience), use the `call.setIncomingVideoEnabled` method: - -```js -await call.setIncomingVideoEnabled(false); -``` - -To enable incoming video again, pass `true` as an argument: - -```js -await call.setIncomingVideoEnabled(true); -``` - -Calling this method will clear the previously set resolution preferences. diff --git a/packages/client/docusaurus/docs/javascript/10-advanced/_category_.json b/packages/client/docusaurus/docs/javascript/10-advanced/_category_.json deleted file mode 100644 index 0e23d1f4f2..0000000000 --- a/packages/client/docusaurus/docs/javascript/10-advanced/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "Advanced Guides" -} diff --git a/packages/client/docusaurus/docs/javascript/assets/02-guides/02-joining-creating-calls/call_member-grant-joincall.png b/packages/client/docusaurus/docs/javascript/assets/02-guides/02-joining-creating-calls/call_member-grant-joincall.png deleted file mode 100644 index d950f9b309..0000000000 Binary files a/packages/client/docusaurus/docs/javascript/assets/02-guides/02-joining-creating-calls/call_member-grant-joincall.png and /dev/null differ diff --git a/packages/client/docusaurus/docs/javascript/assets/02-guides/02-joining-creating-calls/user-revoke-joincall.png b/packages/client/docusaurus/docs/javascript/assets/02-guides/02-joining-creating-calls/user-revoke-joincall.png deleted file mode 100644 index 1aee4570d5..0000000000 Binary files a/packages/client/docusaurus/docs/javascript/assets/02-guides/02-joining-creating-calls/user-revoke-joincall.png and /dev/null differ diff --git a/packages/client/docusaurus/docs/javascript/assets/02-guides/04-screensharing/screensharing-dashboard.png b/packages/client/docusaurus/docs/javascript/assets/02-guides/04-screensharing/screensharing-dashboard.png deleted file mode 100644 index 36ce4d42bb..0000000000 Binary files a/packages/client/docusaurus/docs/javascript/assets/02-guides/04-screensharing/screensharing-dashboard.png and /dev/null differ diff --git a/packages/client/docusaurus/readme.md b/packages/client/docusaurus/readme.md new file mode 100644 index 0000000000..1ff52c5a32 --- /dev/null +++ b/packages/client/docusaurus/readme.md @@ -0,0 +1,3 @@ +### Info + +Docs were moved to the following repo: [docs-content](https://github.com/GetStream/docs-content). diff --git a/packages/react-native-sdk/docusaurus/.env b/packages/react-native-sdk/docusaurus/.env deleted file mode 100644 index e9f70f9cf4..0000000000 --- a/packages/react-native-sdk/docusaurus/.env +++ /dev/null @@ -1 +0,0 @@ -PRODUCT=video diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/01-setup/01-introduction.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/01-setup/01-introduction.mdx deleted file mode 100644 index 27f97fc2d4..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/01-setup/01-introduction.mdx +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: Introduction -description: Introduction about Stream's video SDK -slug: / ---- - -Welcome to the Stream Video React Native SDK - a comprehensive toolkit designed to help you swiftly implement features such as video calling, audio calling, audio rooms, and livestreaming within your app. Our goal is to ensure an optimal developer experience that enables your application to go live within days. - -Our React Native SDK comes with user-friendly UI components, easy-to-use React hooks, and context providers/wrappers, making your development process seamless. Moreover, all calls are routed through Stream's global edge network, ensuring lower latency and higher reliability due to proximity to end users. - -If you're new to Stream React Native Video SDK, we recommend starting with the following three tutorials, depending on your requirements: - -- [Video & Audio Calling Tutorial](https://getstream.io/video/sdk/react-native/tutorial/video-calling/) -- [Audio Room Tutorial](https://getstream.io/video/sdk/react-native/tutorial/audio-room/) -- [Livestream Tutorial](https://getstream.io/video/sdk/react-native/tutorial/livestreaming/) - -After the tutorials, the documentation explains how to use: - -- Core concepts such as [initiating a call](./core/joining-and-creating-calls/), switching the [camera](./core/camera-and-microphone/) view, and more -- Effective utilization of our UI components -- Insights on building your own UI with our [UI Cookbook](./ui-cookbook/overview/) - -It also explains advanced features such as: - -- [Ringing/Calls](./core/joining-and-creating-calls/) -- [Requesting and Granting permissions](./core/permissions-and-moderation/) -- [Participants Layout Switching](./ui-cookbook/runtime-layout-switching/) -- Friendly support for [Push notifications](./advanced/push-notifications/overview), [deep linking](./advanced/deeplinking/) and Reconnection of calls. - -If you feel like anything is missing or could be improved, please don't hesitate to [contact us](https://getstream.io/contact/). We're happy to help. diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/01-setup/02-installation/01-react-native.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/01-setup/02-installation/01-react-native.mdx deleted file mode 100644 index c17e2c2693..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/01-setup/02-installation/01-react-native.mdx +++ /dev/null @@ -1,140 +0,0 @@ ---- -title: React Native ---- - -import Troubleshooting from "../../common-content/setup/installation/troubleshooting.mdx"; - -Installation and usage of our React Native SDK is simple and involves the following steps: - -### Prerequisites - -First things first, make sure you have set up the development environment for React Native. -You can find the official guide [here](https://reactnative.dev/docs/environment-setup). - -## SDK Installation - -In order to install the Stream Video React Native SDK, run the following command in your terminal of choice: - -```bash title=Terminal -yarn add @stream-io/video-react-native-sdk -``` - -Stream Video React Native SDK requires installing some peer dependencies to provide you with a great calling experience. You can run the following command to install them: - -```bash title=Terminal -yarn add @stream-io/react-native-webrtc \ - react-native-incall-manager react-native-svg \ - @react-native-community/netinfo -npx pod-install -``` - -So what did we install precisely? - -- `@stream-io/video-react-native-sdk` (SVRN) is Stream's Video SDK which contains UI components, hooks and util functions that will enable audio/video calls. -- `@stream-io/react-native-webrtc` is a WebRTC module for React Native, SVRN depends on this dependency, it's components and utilities to render audio/video tracks and interact with the phone's media devices. -- `react-native-incall-manager` handles media-routes/sensors/events during an audio/video call. -- `react-native-svg` provides SVG support to React Native, SVRN's components and it's icons are reliant on this dependency. -- `@react-native-community/netinfo` - is used to detect the device's connectivity state, type and quality. - -### Android Specific installation - - -#### Update the minSdk version - -In `android/build.gradle` add the following inside the `buildscript` section: - -```java -buildscript { - ext { - ... - minSdkVersion = 24 - } - ... -} -``` - - -#### Enable Java 8 Support - -In `android/app/build.gradle` add the following inside the `android` section: - -```java -compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_11 -} -``` - -#### Optional: R8/ProGuard Support - -If you require R8/ProGuard support then in `android/app/proguard-rules.pro` add the following on a new line: - -```groovy --keep class org.webrtc.** { *; } -``` - -### Declaring Permissions - -Making video or audio calls requires the usage of the device's camera and microphone accordingly. In both platforms, we must declare the permissions. - -#### iOS - -Add the following keys and values to `Info.plist` file at a minimum: - -- `Privacy - Camera Usage Description` - "`` requires camera access to capture and transmit video" -- `Privacy - Microphone Usage Description` - "`` requires microphone access to capture and transmit audio" - -:::note -You should replace `` (or also use your custom strings instead). -::: - -#### Android - -In `AndroidManifest.xml` add the following permissions before the `` section. - -```xml - - - - - - - - - - - -``` - -If you plan to also support Bluetooth devices then also add the following. - -```xml - - - -``` - -:::infoINFO -Permissions need to be granted by the user as well. Requests for Camera and Microphone usage are automatically asked when the stream is first requested by the app. But other permissions like `BLUETOOTH_CONNECT` in Android need to be requested manually. However, we recommend that all necessary permissions be manually asked at an appropriate place in your app for the best user experience. - -We recommend the usage of [`react-native-permissions`](https://github.com/zoontek/react-native-permissions) library to request permissions in the app. - -::: - -### Run on device - -#### iOS - -In iOS simulators, recording audio or video is not supported. So always test your app on an actual device for the best experience. - -#### Android - -In Android emulators, a static video stream can be sent and so it can be used for testing. However, we recommend that you always test your app on an actual device for the best experience. - -## New Architecture (Fabric) - -The SDK's native modules and views are compatible with the [New Architecture](https://reactnative.dev/docs/the-new-architecture/landing-page) and [Bridgeless mode](https://github.com/reactwg/react-native-new-architecture/discussions/154) through the **New Renderer Interop Layers**. These layers are [automatically enabled](https://github.com/reactwg/react-native-new-architecture/discussions/175) when you turn on the New Architecture in React Native 0.74 and above. We recommend that you use React Native 0.74+ if you are using the New Architecture with the SDK. - -## Troubleshooting - - \ No newline at end of file diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/01-setup/02-installation/02-expo.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/01-setup/02-installation/02-expo.mdx deleted file mode 100644 index a2ea2e7615..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/01-setup/02-installation/02-expo.mdx +++ /dev/null @@ -1,112 +0,0 @@ ---- -title: Expo -description: Install the SDK in Expo Development Builds ---- - -import Troubleshooting from "../../common-content/setup/installation/troubleshooting.mdx"; - -Our SDK is not available on Expo Go due to native code being required, but you can use the [expo-dev-client](https://docs.expo.dev/development/create-development-builds/) library to run your Expo app with a development build. - -### Development Build - -If you haven't already, prepare your project for [expo development builds](https://docs.expo.dev/develop/development-builds/installation/). - -## SDK Installation - -Add the Stream Video React Native SDK and its required dependencies to your project: - -```bash title=Terminal -npx expo install @stream-io/video-react-native-sdk -npx expo install @stream-io/react-native-webrtc -npx expo install @config-plugins/react-native-webrtc -npx expo install react-native-incall-manager -npx expo install react-native-svg -npx expo install @react-native-community/netinfo -``` - -So what did we install precisely? - -- `@stream-io/video-react-native-sdk` (SVRN) is Stream's Video SDK which contains UI components, hooks and util functions that will enable audio/video calls. -- `@stream-io/react-native-webrtc` is a WebRTC module for React Native, SVRN depends on this dependency, it's components and utilities to render audio/video tracks and interact with the phone's media devices. -- `@config-plugins/react-native-webrtc` config plugin to auto-configure `@stream-io/react-native-webrtc` when the native code is generated (`npx expo prebuild`). -- `react-native-incall-manager` handles media-routes/sensors/events during an audio/video call. -- `react-native-svg` provides SVG support to React Native, SVRN's components and it's icons are reliant on this dependency. -- `@react-native-community/netinfo` - is used to detect the device's connectivity state, type and quality. - -### Android Specific installation - - -#### Update the minSdk version - -In your `app.json` file add the following to the `expo-build-properties` plugin: - -```js title=app.json -{ - "expo": { - ... - "plugins": [ - // highlight-start - "expo-build-properties", - { - "android": { - "minSdkVersion": 24 - } - } - // highlight-end - ] - } -} -``` - - -### Add config plugin - -Add the config plugin for [`@stream-io/video-react-native-sdk`](https://github.com/GetStream/stream-video-js/tree/main/packages/react-native-sdk/expo-config-plugin/README.md) and [`react-native-webrtc`](https://www.npmjs.com/package/@config-plugins/react-native-webrtc) to your `app.json` file: - -```js title=app.json -{ - "expo": { - ... - "plugins": [ - // highlight-start - "@stream-io/video-react-native-sdk", - [ - "@config-plugins/react-native-webrtc", - { - // add your explanations for camera and microphone - "cameraPermission": "$(PRODUCT_NAME) requires camera access in order to capture and transmit video", - "microphonePermission": "$(PRODUCT_NAME) requires microphone access in order to capture and transmit audio" - } - ] - // highlight-end - ] - } -} -``` - -If Expo EAS build is not used, please do `npx expo prebuild --clean` to generate the native directories again after adding the config plugins. - -:::infoINFO -Permissions need to be granted by the user as well. Requests for Camera and Microphone usage are automatically asked when the stream is first requested by the app. But other permissions like `BLUETOOTH_CONNECT` in Android need to be requested manually. However, we recommend that all necessary permissions be manually asked at an appropriate place in your app for the best user experience. - -We recommend the usage of [`react-native-permissions`](https://github.com/zoontek/react-native-permissions) library to request permissions in the app. - -::: - -### Run on device - -#### iOS - -In iOS simulators, recording audio or video is not supported. So always test your app on an actual device for the best experience. - -#### Android - -In Android emulators, a static video stream can be sent and so it can be used for testing. However, we recommend that you always test your app on an actual device for the best experience. - -## New Architecture (Fabric) - -The SDK's native modules and views are compatible with the [New Architecture](https://reactnative.dev/docs/the-new-architecture/landing-page) and [Bridgeless mode](https://github.com/reactwg/react-native-new-architecture/discussions/154) through the **New Renderer Interop Layers**. These layers are [automatically enabled](https://github.com/reactwg/react-native-new-architecture/discussions/175) when you turn on the New Architecture in React Native 0.74 and above. We recommend that you use React Native 0.74+ if you are using the New Architecture with the SDK. - -## Troubleshooting - - diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/01-setup/02-installation/_category_.json b/packages/react-native-sdk/docusaurus/docs/reactnative/01-setup/02-installation/_category_.json deleted file mode 100644 index f0dac49176..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/01-setup/02-installation/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "Installation" -} diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/01-setup/03-quickstart.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/01-setup/03-quickstart.mdx deleted file mode 100644 index 3e31c59c1a..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/01-setup/03-quickstart.mdx +++ /dev/null @@ -1,111 +0,0 @@ ---- -title: Quickstart -description: For when you're in a hurry and want to quickly get up and running ---- - -In this guide, we will cover the basics of making your first call using Stream Video. -If you haven't already, we recommend starting with the **[introduction](../../)** and **installation**([Native CLI](../installation/react-native) or [Expo](../installation/expo)) steps first, as this guide will build on the material covered in those sections. - -## Client setup & Calls - -Create an instance of `StreamVideoClient` that will establish a WebSocket connection by connecting a user. Next, you create a call object and join the call. We'll specify `create: true` to create the call if it doesn't exist. - -```tsx -import { - StreamCall, - StreamVideo, - StreamVideoClient, - User, -} from '@stream-io/video-react-native-sdk'; -import { useEffect, useState } from 'react'; - -const apiKey = 'your-api-key'; -const userId = 'user-id'; -const token = 'authentication-token'; -const callId = 'my-call-id'; -const user: User = { id: userId }; - -const client = new StreamVideoClient({ apiKey, user, token }); -const call = client.call('default', callId); -call.join({ create: true }); - -export default function App() { - return ( - - {/* Your UI */} - - ); -} -``` - -When creating a call on the client, the string `default` is a call type. There are 4 built-in [call types](../../core/configuring-call-types/) and you can also create your own. -The call type controls the permissions and which features are enabled. - -The second argument is the call id. Call ids can be reused, meaning it's possible to join a call with the same id multiple times (for example, for recurring meetings). However, for ringing calls, you should always provide a unique call id. - -## Rendering video - -The call's state can be accessed using hooks, all exposed through the top-level `useCallStateHooks` hook. - -The call participant's state can be accessed using hooks like `useParticipants`. Have a look below for a basic example of how to render the videos of all participants: - -```tsx -import { - useCallStateHooks, - CallParticipantsList, -} from '@stream-io/video-react-native-sdk'; - -function VideoUI() { - const { useParticipants } = useCallStateHooks(); - const participants = useParticipants(); - - return ; -} -``` - -The participant object contains all essential information to render videos, such as audio/video tracks, user information, audio/video enabled status, etc. - -More information about state management can be found in the [Call & Participant State guide](../../core/call-and-participant-state). - -## Camera & Microphone - -Most video apps will show buttons to mute/unmute the audio or video. The example below shows how to use the camera and microphone: - -```tsx -import { Button } from 'react-native'; -import { useCallStateHooks } from '@stream-io/video-react-sdk'; - -export const MyVideoButton = () => { - const { useCameraState } = useCallStateHooks(); - const { camera, isMute } = useCameraState(); - return ( - - ); -}; - -export const MyMicrophoneButton = () => { - const { useMicrophoneState } = useCallStateHooks(); - const { microphone, isMute } = useMicrophoneState(); - return ( - - ); -}; -``` - -More information about this topic can be found in the [Camera & Microphone guide](../../core/camera-and-microphone). - -## UI Components - -The goal of this library is to make it easy to build any type of video/calling experience. You have few options for the UI: - -- Build your own UI components using the state as shown above. -- Use our library of built-in components. -- Mix & Match between your own and built-in components. - -If you're using our built-in components, you can easily customize them through theming and props. For creating your own components, the [UI Cookbook section](../../ui-cookbook/overview) is there to help you get started. diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/01-setup/_category_.json b/packages/react-native-sdk/docusaurus/docs/reactnative/01-setup/_category_.json deleted file mode 100644 index ece9ba735d..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/01-setup/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "Setup" -} diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/01-client-auth.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/01-client-auth.mdx deleted file mode 100644 index 641ce941c7..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/01-client-auth.mdx +++ /dev/null @@ -1,178 +0,0 @@ ---- -id: client-auth -title: Client & Authentication -description: How to setup the client and authenticate ---- - -import { TokenSnippet } from '../../../shared/_tokenSnippet.jsx'; - -## Client & Auth - -Before joining a call, it is necessary to set up the video client. Here's a basic example: - -```ts -import { StreamVideoClient, User } from '@stream-io/video-react-native-sdk'; - -const user: User = { - id: 'sara', -}; -const apiKey = 'my-stream-api-key'; -const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'; -const client = StreamVideoClient.getOrCreateInstance({ apiKey, token, user }); -``` - -- The API Key can be found in your dashboard. -- The user can be either authenticated, anonymous or guest. -- Note: You can store custom data on the user object, if required. - -Typically, you'll want to initialize the client when your application loads and use a context provider to make it available to the rest of your application. - -## Generating a token - -Tokens need to be generated server side. You can use our [server side SDKs](https://getstream.io/video/docs/api/authentication/) to quickly add support for this. -Typically, you integrate this into the part of your codebase where you log in or register users. -The tokens provide a way to authenticate a user or give access to a specific set of calls. - - - -:::note -For development purposes, you can use our [Token Generator](https://getstream.io/chat/docs/token_generator/). -::: - -## Different types of users - -- Authenticated users are users that have an account on your app. -- Guest users are temporary user accounts. You can use it to temporarily give someone a name and image when joining a call. -- Anonymous users are users that are not authenticated. It's common to use this for watching a livestream or similar where you aren't authenticated. - -This example shows the client setup for a guest user: - -```ts -import { StreamVideoClient, User } from '@stream-io/video-react-native-sdk'; - -const user: User = { - id: 'jack-guest', - type: 'guest', -}; -const apiKey = 'my-stream-api-key'; -const client = StreamVideoClient.getOrCreateInstance({ apiKey, user }); -``` - -And here's an example for an anonymous user - -```ts -import { StreamVideoClient, User } from '@stream-io/video-react-native-sdk'; - -const user: User = { - type: 'anonymous', -}; - -const apiKey = 'my-stream-api-key'; -const client = StreamVideoClient.getOrCreateInstance({ apiKey, user }); -``` - -Anonymous users don't establish an active web socket connection, therefore they won't receive any events. They are just able to watch a livestream or join a call. - -The token for an anonymous user should contain the `call_cids` field, which is an array of the call `cid`'s that the user is allowed to join. - -Here's an example JWT token payload for an anonymous user: - -```ts -{ - "iss": "@stream-io/dashboard", - "iat": 1726406693, - "exp": 1726493093, - "user_id": "!anon", - "role": "viewer", - "call_cids": [ - "livestream:123" - ] -} -``` - -## Client options - -### `token` or `tokenProvider` - -To authenticate users you can either provide a string `token` or a `tokenProvider` function that returns `Promise`. - -:::info -If you use the `tokenProvider` the SDK will automatically execute it to refresh the token whenever the token is expired. -::: - -```typescript -import { StreamVideoClient, User } from '@stream-io/video-react-native-sdk'; - -const tokenProvider = async () => { - const response = await fetch('/api/token'); - const data = await response.json(); - return data.token; -}; - -const user: User = { - id: 'sara', -}; -const apiKey = 'my-stream-api-key'; -const client = StreamVideoClient.getOrCreateInstance({ apiKey, tokenProvider, user }); -``` - -### Logging - -You can configure the log level and the logger method used by the SDK. - -The default log level is `warn`, other options are: `trace`, `debug`, `info`, and `error`. - -The default logger method will log to the debugging console. - -```ts -import { StreamVideoClient, Logger } from '@stream-io/video-react-native-sdk'; - -const myLogger: Logger = (logLevel, message, ...args) => { - // Do something with the log message -}; - -const client = StreamVideoClient.getOrCreateInstance({ - apiKey, - token, - user, - options: { - logLevel: 'info', - logger: myLogger, - }, -}); -``` - -## StreamVideo context provider - -You can use the `StreamVideo` context provider to make the SDK client available to the rest of the application. We also use the `tokenProvider` to show how to perform auth server-side. - -```tsx -import { useEffect, useState } from 'react'; -import { - StreamVideo, - StreamVideoClient, -} from '@stream-io/video-react-native-sdk'; -const apiKey = 'my-stream-api-key'; -const user: User = { - id: 'sara', -}; - -export const MyApp = () => { - const [client, setClient] = useState(); - useEffect(() => { - const tokenProvider = () => Promise.resolve(''); - const myClient = StreamVideoClient.getOrCreateInstance({ apiKey, user, tokenProvider }); - setClient(myClient); - return () => { - myClient.disconnectUser(); - setClient(undefined); - }; - }, []); - - return ( - - - - ); -}; -``` diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/02-joining-creating-calls.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/02-joining-creating-calls.mdx deleted file mode 100644 index 2f5528dbfb..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/02-joining-creating-calls.mdx +++ /dev/null @@ -1,209 +0,0 @@ ---- -id: joining-and-creating-calls -title: Joining & Creating Calls -description: An overview of how to create calls and join them ---- - -This guide shows how to create, join, leave, and end calls and ring calls. - -## Call - -`Call` represents the main building block of our SDK. This object abstracts away the user actions, join flows and exposes the call state. - -### Create call - -You can create a call by specifying its `callType` and `callId`: - -The [Call Type](../../core/configuring-call-types) controls which features are enabled, and sets up permissions. -You can reuse the same call multiple times. As an example, if you're building a telemedicine app calls will be connected to an appointment. -Using your own appointment id as the call id makes it easy to find the call later. - -```typescript -const callType = 'default'; -const callId = 'test-call'; - -const call = client.call(callType, callId); -await call.getOrCreate(); - -// or create it with options: -await call.getOrCreate({ - data: { - /* call creation options */ - }, -}); -``` - -See all possible options at the [Call creation options section](#call-creation-options). - -### Join call - -```typescript -const callType = 'default'; -const callId = 'test-call'; - -const call = client.call(callType, callId); -await call.join(); -``` - -### Create and join a call - -For convenience, you can create and join a call in a single operation. One of the flags you can provide there is `create`. -Set this to `true` if you want to enable creating new call. Set it to `false` if you only want to join an existing call. - -See all possible options at the [Call creation options section](#call-creation-options). - -```typescript -await call.join({ - create: true, - data: { - /* call creation options */ - }, -}); -``` - -### Leave call - -To leave a call, you can use the `leave` method: - -```typescript -await call.leave(); -``` - -### End call - -Ending a call requires a [special permission](../../core/permissions-and-moderation). This action terminates the call for everyone. - -```typescript -await call.endCall(); -``` - -Only users with a special permission (`OwnCapability.JOIN_ENDED_CALL`) can join an ended call. - -### Load call - -Existing calls can be loaded through the following API: - -```typescript -const call = client.call(type, id); -await call.get(); // just load - -await call.getOrCreate(); // create if not present and load it -``` - -These operations initialize the `call.state` and create a subscription for call updates to our backend. -This means that this `call` instance will receive real-time updates in case it is modified somewhere else. - -Read more about call state here: [Call & Participant State](../../core/call-and-participant-state/). - -### Update call - -After creating a call, you can update some of its properties: - -```typescript -import { RecordSettingsRequestModeEnum } from '@stream-io/video-react-sdk'; - -await call.update({ - custom: { color: 'green' }, - settings_override: { - recording: { - mode: RecordSettingsRequestModeEnum.DISABLED, - }, - }, -}); -``` - -## Call creation options - -The following options are supported when creating a call: - -| Option | Description | Default | -| ---------- | --------------------------------------------------------------------------------------------------------------- | ------- | -| `members` | A list of members to add to this call. You can specify the role and custom data on these members | - | -| `custom` | Any custom data you want to store | - | -| `settings` | You can overwrite certain call settings for this specific call. This overwrites the call type standard settings | - | -| `startsAt` | When the call will start. Used for calls scheduled in the future, livestreams, audio rooms etc | - | -| `team` | Restrict the access to this call to a specific team | - | -| `ring` | If you want the call to ring for each member | false | -| `notify` | If you want the call to notify each member by sending push notification | false | - -### Set call members - -```typescript -const call = client.call(type, id); -await call.getOrCreate({ - data: { - members: [{ user_id: 'alice', role: 'admin' }, { user_id: 'bob' }], - }, -}); -``` - -### Update call members - -```typescript -await call.updateCallMembers({ - update_members: [{ user_id: 'charlie', role: 'admin' }], - remove_members: ['alice'], -}); -``` - -### Custom call data - -```typescript -await call.getOrCreate({ - data: { - custom: { color: 'blue' }, - }, -}); -``` - -### Settings override - -By default, the `call` instances inherit the settings defined in the call type. -In some cases, you might want to override call settings on the instance itself: - -```typescript -// at creation time -await call.getOrCreate({ - data: { - settings_override: { - audio: { mic_default_on: false }, - video: { camera_default_on: false }, - }, - }, -}); - -// or later -await call.update({ - settings_override: { - video: { camera_default_on: true }, - }, -}); -``` - -### Backstage setup - -The backstage feature makes it easy to build a use-case where you and your co-hosts can set up your camera before going live. -Only after you call `call.goLive()` the regular users will be allowed to join the livestream. - -However, you can also specify a `join_ahead_time_seconds`, -which will allow regular users to join the livestream before the call is live, in the specified join time before the stream starts. - -Here's an example of how to do that: - -```typescript -await call.getOrCreate({ - data: { - starts_at: new Date(Date.now() + 500 * 1000), // 500 seconds from now - settings_override: { - backstage: { - enabled: true, - join_ahead_time_seconds: 300, - }, - }, - }, -}); -``` - -In the code snippet above, we are creating a call that starts 500 seconds from now. -We are also enabling backstage mode, with a `join_ahead_time_seconds` of 300 seconds. -That means that regular users will be able to join the call 200 seconds from now. diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/03-call-and-participant-state.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/03-call-and-participant-state.mdx deleted file mode 100644 index 20cd790ba3..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/03-call-and-participant-state.mdx +++ /dev/null @@ -1,267 +0,0 @@ ---- -title: Call & Participant State -description: How the state is exposed ---- - -You can access call, participant and client state using hooks. These hooks are reactive (their value is updated on WebSocket events and API calls). - -## Call state - -To observe call state you need to provide a `Call` instance to the [`StreamCall` component](../../ui-components/core/stream-call). - -:::note -For the best experience, please make sure that the provided `Call` instance is loaded -and connected to our backend: [Load Call](../../joining-and-creating-calls/#load-call). - -Otherwise, `call.state` and the call state hooks will provide empty values. -::: - -Let's see an example where we use the `useCall`, `useCallCallingState` and `useParticipants` hooks to display some basic information about the call: - -```tsx -import { - Call, - useCall, - useCallStateHooks, -} from '@stream-io/video-react-native-sdk'; - -export default function MyApp() { - let call: Call; - - return ( - - - - ); -} - -const MyCallUI = () => { - const call = useCall(); - - const { useCallCallingState, useParticipants } = useCallStateHooks(); - const callingState = useCallCallingState(); - const participants = useParticipants(); - - return ( -
-
Call: {call?.cid}
-
State: {callingState}
-
Participants: {participants.length}
-
- ); -}; -``` - -This approach makes it possible to access the call state and be notified about changes anywhere in your application without having to manually subscribe to WebSocket events. - -The `StreamCall` component is a context provider that makes the call state available to all child components. -The `useCall` hook returns the `Call` instance that is registered with `StreamCall`. You need the `Call` instance to initiate API calls. - -## Call State Hooks - -Here is an excerpt of the available call state hooks: - -| Name | Description | -| ------------------------------------ | ------------------------------------------------------------------------------------------------------------- | -| `useCall` | The `Call` instance that is registered with `StreamCall`. You need the `Call` instance to initiate API calls. | -| `useCallBlockedUserIds` | The list of blocked user IDs. | -| `useCallCallingState` | Provides information about the call state. For example, `RINGING`, `JOINED` or `RECONNECTING`. | -| `useCallCreatedAt` | The time the call was created. | -| `useCallCreatedBy` | The user that created the call. | -| `useCallCustomData` | The custom data attached to the call. | -| `useCallEgress` | The egress information of the call. | -| `useCallEndedBy` | The user that ended the call. | -| `useCallIngress` | The ingress information of the call. | -| `useCallMembers` | The list of call members | -| `useCallSession` | The information for the current call session. | -| `useCallSettings` | The settings of the call. | -| `useCallStartedAt` | The actual start time of the current call session. | -| `useCallStartsAt` | The scheduled start time of the call. | -| `useCallStatsReport` | When stats gathering is enabled, this observable will emit a new value at a regular (configurable) interval. | -| `useCallThumbnail` | The thumbnail of the call. | -| `useCallUpdatedAt` | The time the call was last updated. | -| `useCameraState` | The camera state of the local participant. | -| `useDominantSpeaker` | The participant that is the current dominant speaker of the call. | -| `useHasOngoingScreenShare` | It will return `true` if at least one participant is sharing their screen. | -| `useHasPermissions` | Returns `true` if the local participant has all the given permissions. | -| `useIsCallHLSBroadcastingInProgress` | It's `true` if the call is being broadcasted in HLS mode. | -| `useIsCallLive` | It's `true` if the call is currently live. | -| `useIsCallRecordingInProgress` | It's' `true` if the call is being recorded. | -| `useIsCallTranscribingInProgress` | It's `true` if the call is being transcribed. | -| `useMicrophoneState` | The microphone state of the local participant. | -| `useOwnCapabilities` | The capabilities of the local participant. | -| `useScreenShareState` | The screen share state of the local participant. | -| `useSpeakerState` | The speaker state of the local participant. | -| `useIncomingVideoSettings` | The state of manual overrides to incoming video quality. | - -In your IDE of choice, you can see the full list if you de-structure the `useCallStateHooks` object: - -```ts -import { useCallStateHooks } from '@stream-io/video-react-native-sdk'; - -const { - useCallMembers, - useDominantSpeaker, - useParticipants, - useLocalParticipant, - useIsCallRecordingInProgress, - // ... -} = useCallStateHooks(); -``` - -## Participant state - -If you want to display information about the joined participants of the call you can use these hooks: - -| Name | Description | -| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `useLocalParticipant` | The local participant is the logged-in user. | -| `useRemoteParticipants` | All participants except the local participant. | -| `useParticipants` | All participants, including local and remote participants. | -| `useParticipantCount` | The approximate participant count of the active call. This includes the [anonymous users](../client-auth/#anonymous-users) as well, it is computed on the server-side. | -| `useAnonymousParticipantCount` | The approximate participant count of anonymous users in the active call. | - -```tsx -import { - useCallStateHooks, - StreamCall, -} from '@stream-io/video-react-native-sdk'; - -export default function App() { - let call: call; - - return ( - - - - ); -} - -const MyCallUI = () => { - const { useLocalParticipant, useParticipantCount } = useCallStateHooks(); - const participantCount = useParticipantCount(); - const localParticipant = useLocalParticipant(); - - return ( - - Number of participants: {participantCount} - Session ID: {localParticipant.sessionId} - - ); -}; -``` - -The `StreamVideoParticipant` object contains the following information: - -| Name | Description | -| ------------------------- | -------------------------------------------------------------------------------- | -| `audioLevel` | The audio level of the participant (determined on the server). | -| `audioStream` | The published audio `MediaStream`. | -| `audioVolume` | The audio volume level of the participant (overridable local audioVolume level). | -| `connectionQuality` | The participant's connection quality. | -| `custom` | The participant's custom data. Comes from the `custom` field of the user object. | -| `image` | The image of the participant. | -| `isDominantSpeaker` | It's `true` if the participant is the current dominant speaker in the call. | -| `isLocalParticipant` | It's `true` if the participant is the local participant. | -| `isSpeaking` | It's `true` if the participant is currently speaking. | -| `joinedAt` | The time the participant joined the call. | -| `name` | The name of the participant. | -| `pin` | Holds pinning information. | -| `publishedTracks` | The track types the participant is currently publishing | -| `reaction` | The last reaction this user has sent to this call. | -| `roles` | The roles of the participant in this call. | -| `sessionId` | The identifier of the participant within the existing call session | -| `screenShareAudioStream` | The published screen share audio `MediaStream`. | -| `screenShareStream` | The published screen share `MediaStream`. | -| `userId` | The user ID of the participant. | -| `videoStream` | The published video `MediaStream`. | -| `viewportVisibilityState` | The viewport visibility state of the participant. | - -The SDK also provides a few utility functions that help you to work with participants: - -```ts -import { - hasAudio, - hasVideo, - hasScreenShare, - hasScreenShareAudio, - isPinned, - useCallStateHooks, -} from '@stream-io/video-react-native-sdk'; - -// example usage -const { useParticipants } = useCallStateHooks(); - -// check if the participant has audio, video, screen share or screen share audio -const [participant] = useParticipants(); -const hasAudioOn = hasAudio(participant); -const hasVideoOn = hasVideo(participant); -const hasScreenShareOn = hasScreenShare(participant); -const hasScreenShareAudioOn = hasScreenShareAudio(participant); -const isPinnedOn = isPinned(participant); - -// participants with a specific role -const hosts = participants.filter((p) => p.roles.includes('host')); - -// participants that publish video and audio -const videoParticipants = participants.filter( - (p) => hasVideo(p) && hasAudio(p) -); -``` - -In a call with many participants, the list returned by the `useParticipants` call state hook is truncated to 250 participants. The participants who are publishing video, audio, or screen sharing have priority over the other participants in the list. This means, for example, that in a livestream with one host and many viewers, the host is guaranteed to be in the list. - -## Client state - -To observe client state you need to provide a `StreamVideoClient` instance to the `StreamVideo` context provider. -If you want to observe the connected user you can use the `useConnectedUser` hook. - -Let's see an example: - -```tsx -import { - useConnectedUser, - StreamVideo, - StreamVideoClient, -} from '@stream-io/video-react-native-sdk'; - -export default function App() { - let client: StreamVideoClient; - - return ( - - - - ); -} - -const MyHeader = () => { - const user = useConnectedUser(); - return {user ? `Logged in: ${user.name}` : 'Logged out'}; -}; -``` - -This approach makes it possible to access the client state and be notified about changes anywhere in your application without having to manually subscribe to WebSocket events. - -Here is the list of client-state hooks: - -| Name | Description | -| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `useStreamVideoClient` | The `StreamVideoClient` instance. | -| `useConnectedUser` | Returns the connected user. Holds the server-side data of the connected user. | -| `useCalls` | A list of all tracked calls. These calls can be outgoing (I have called somebody) or incoming (somebody has called me). Loaded calls (`call.get()`) are also part of this list. | - -The `connectedUser` object contains the following properties: - -| Name | Description | -| ------------ | ----------------------------------------------------- | -| `created_at` | The time the user was created. | -| `custom` | Custom user data. | -| `deleted_at` | The time the user was deleted. | -| `devices` | The registered push notification devices of the user. | -| `id` | The id of the user. | -| `image` | The profile image of the user. | -| `name` | The name of the user. | -| `role` | The role of the user. | -| `teams` | The teams the user belongs to. | -| `updated_at` | The time when the user was updated. | diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/03-calling-state-and-lifecycle.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/03-calling-state-and-lifecycle.mdx deleted file mode 100644 index da275db188..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/03-calling-state-and-lifecycle.mdx +++ /dev/null @@ -1,145 +0,0 @@ ---- -id: calling-state-and-lifecycle -title: Calling State and Lifecycle -description: Calling State machine and Call Lifecycle. ---- - -The `call` object instance manages everything related to a particular call instance, such as: - -- creating and joining a call -- performing actions (mute, unmute, send reaction, etc...) -- manage event subscriptions (`call.on('call.session_started', callback)`, etc...) -- and many more - -Every `call` instance should be created through the `client.call(type, id)` helper. - -Our `StreamVideoClient` is responsible for maintaining a WebSocket connection to our servers and also takes care about the API calls that are proxied from the `call` instance. - -As we learned in [Joining and Creating Calls](../../core/joining-and-creating-calls/) guide, a call instance is managed like this: - -```ts -import { Call, StreamVideoClient } from '@stream-io/video-react-native-sdk'; - -let client: StreamVideoClient; // ... - -const call: Call = client.call(type, id); - -// load existing call information from our servers -await call.get(); - -// Creates the call on our servers in case it doesn't exist. Otherwise, -// loads the call information from our servers. -await call.getOrCreate(); - -// join the call -await call.join(); - -// leave the call and dispose all allocated resources -await call.leave(); -``` - -Every `call` instance has a local state, exposed to integrators through: - -- `call.state.callingState` - a getter that returns the current value -- `call.state.callingState$` - an observable that an integrator can subscribe to and be notified everytime the value changes -- `useCallCallingState()` - a [call state hook](../../core/call-and-participant-state/#call-state-hooks) that makes it easy to read and update the UI based on calling state values in React components. - -## Call Instance - -The call instance is a stateful resource that you acquire with `client.call()` and must dispose of with `call.leave()`. Failure to dispose of the call properly can result in memory leaks and unexpected behavior. - -In practice, this means that: - -1. You should only create call instances in effects. -2. Effects that create a call instance should have a `call.leave()` cleanup. - -```ts -const [call, setCall] = useState(undefined); - -useEffect(() => { - const myCall = client.call(callType, callId); - myCall.join({ create: true }).then( - () => setCall(myCall), - () => console.error('Failed to join the call') - ); - - return () => { - myCall.leave().catch(() => console.error('Failed to leave the call')); - setCall(undefined); - }; -}, [callType, callId]); -``` - -To join the same call again, you can reuse the same call instance, or create a new one using `client.call(type, id)`. - -## Calling State - -Every `call` instance has its own local state managed by the SDK. - -These values are exposed through the `CallingState` enum: - -```ts -import { - CallingState, - useCallStateHooks, -} from '@stream-io/video-react-native-sdk'; - -const { useCallCallingState } = useCallStateHooks(); -const callingState = useCallCallingState(); - -switch (callingState) { - case CallingState.JOINED: - // ... - break; - default: - const exhaustiveCheck: never = callingState; - throw new Error(`Unknown calling state: ${exhaustiveCheck}`); -} -``` - -:::note -As `CallingState` is an enum that can be extended at any time by us, it would be good to make sure you -use it exhaustively. This way, if you use TypeScript, you can get a compile time error and be notified that -there are few more states that you should handle. -::: - -### Calling States - -| State | Description | -| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `CallingState.UNKNOWN` | The state is unknown. This value is set when Calling State isn't initialized properly. | -| `CallingState.IDLE` | A call instance is created on the client side but a WebRTC session isn't established yet. | -| `CallingState.RINGING` | This is an incoming (ring) call. You are the callee. | -| `CallingState.JOINING` | The call join flow is executing (typically right after `call.join()`). Our systems are preparing to accept the new call participant. | -| `CallingState.JOINED` | The join flow has finished successfully and the current participant is part of the call. The participant can receive and publish audio and video. | -| `CallingState.LEFT` | The call has been left (`call.leave()`) and all allocated resources are released. Please create a new `call` instance if you want to re-join. | -| `CallingState.RECONNECTING` | A network connection has been lost (due to various factors) and the `call` instance attempts to re-establish a connection and resume the call. | -| `CallingState.RECONNECTING_FAILED` | The SDK failed to recover the connection after a couple of consecutive attempts. You need to inform the user that he needs to go online and manually attempt to rejoin the call. | -| `CallingState.MIGRATING` | The SFU node that is hosting the current participant is shutting down or tries to rebalance the load. This `call` instance is being migrated to another SFU node. | -| `CallingState.OFFLINE` | No network connection can be detected. Once the connection restores, the SDK will automatically attempt to recover the connection (signalled with `RECONNECTING` state). | - -### Example handling - -To understand these values better, here is a hypothetical example of how these values can be mapped: - -```tsx -import { CallingState, useCallStateHooks, CallContent } from '@stream-io/video-react-native-sdk'; - -const call = useCall(); -const isCallCreatedByMe = call?.isCreatedByMe; - -const { useCallCallingState } = useCallStateHooks(); -const callingState = useCallCallingState(); - -switch (callingState) { - case CallingState.RINGING: - return isCallCreatedByMe - ? : - case CallingState.LEFT: - return - case CallingState.IDLE: - return - default: - return -} -``` diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/04-camera-and-microphone.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/04-camera-and-microphone.mdx deleted file mode 100644 index ee6a6092a7..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/04-camera-and-microphone.mdx +++ /dev/null @@ -1,280 +0,0 @@ ---- -id: camera-and-microphone -title: Camera & Microphone -description: Docs on the media manager ---- - -Handling audio and video devices in a your application means working with `MediaStream`, `MediaDeviceInfo` and other WebRTC API objects. To make this simpler, we hide all the complexity inside the SDK and export utility functions and states. In this guide we shall go over their usage. - -## Camera management - -The SDK does its best to make working with the camera easy. We expose the following objects on the call: - -### Call settings - -The default state of the camera is determined by the settings in the call object. - -```ts -import { useCallStateHooks } from '@stream-io/video-react-native-sdk'; - -const { useCallSettings } = useCallStateHooks(); -const settings = useCallSettings(); - -console.log(settings?.video.camera_default_on); -``` - -:::note -Make sure, `call.get()` is called at least once in the application, after the call is created. -::: - -### Start-Stop Camera - -We can use the functions `camera.enable()` and `camera.disable()` to control the publishing and un-publishing our video stream. - -Alternatively, you can use `camera.toggle()`. - -```ts -import { useCall, useCallStateHooks } from '@stream-io/video-react-native-sdk'; - -const call = useCall(); - -const { useCameraState } = useCallStateHooks(); -const { camera, isMute } = useCameraState(); - -console.log(`Camera is ${isMute ? 'off' : 'on'}`); -await camera.toggle(); - -// or, alternatively -await camera.enable(); -await camera.disable(); -``` - -It's always best to await calls to `enable()`, `disable()`, and `toggle()`, however the SDK does its best to resolve potential race conditions: the last call always wins, so it's safe to make these calls in an event handler. - -Status is updated once the camera is actually enabled or disabled. Use `optimisticIsMute` for the "optimistic" status that is updated immediately after toggling the camera. - -### Manage Camera Facing Mode - -We can toggle the camera face from front to back and vice-versa using `camera.flip()`. - -```ts -import { useCallStateHooks } from '@stream-io/video-react-native-sdk'; - -const { useCameraState } = useCallStateHooks(); -const { camera } = useCameraState(); - -console.log(direction); // direction returns 'front' or 'back'. -camera.flip(); -``` - -We can get the facing mode state of the camera by: - -```ts -import { useCallStateHooks } from '@stream-io/video-react-native-sdk'; - -const { useCameraState } = useCallStateHooks(); -const { direction } = useCameraState(); // direction returns 'front' or 'back'. -``` - -### Video mute status - -We can get the mute state of our video stream by checking the `status` value returned from the `useCameraState` hook: - -```ts -import { useCallStateHooks } from '@stream-io/video-react-native-sdk'; - -const { useCameraState } = useCallStateHooks(); -const { status } = useCameraState(); // status returns enabled, disabled or undefined -``` - -### Show Video Preview - -We can get the video stream from the camera using the media stream from the `call.camera` object and show it using the `RTCView` component from `@stream-io/react-native-webrtc` library: - -```tsx -import { useCallStateHooks } from '@stream-io/video-react-native-sdk'; -import { RTCView } from '@stream-io/react-native-webrtc'; -const { useCameraState } = useCallStateHooks(); -const { camera } = useCameraState(); - -const localVideoStream = camera.state.mediaStream; - -return ; -``` - -### Access to the Camera's MediaStream - -Our SDK exposes the current `mediaStream` instance that you can use for your needs (for example, local recording, etc...): - -```typescript -import { useCallStateHooks } from '@stream-io/video-react-native-sdk'; - -const { useCameraState } = useCallStateHooks(); -const { mediaStream } = useCameraState(); - -const [videoTrack] = mediaStream.getVideoTracks(); -console.log('Video track', videoTrack); -``` - -## Microphone management - -The SDK does its best to make working with the microphone easy. We expose the following objects on the call: - -### Call settings - -The default state of the microphone is determined by the settings in the call object. - -```ts -import { useCallStateHooks } from '@stream-io/video-react-native-sdk'; - -const { useCallSettings } = useCallStateHooks(); -const settings = useCallSettings(); - -console.log(settings?.audio.mic_default_on); -``` - -:::note -Make sure, `call.get()` is called at least once in the application, after the call is created. -::: - -### Start-Stop Microphone - -We can use the functions `microphone.enable()` and `microphone.disable()` to control the publishing and un-publishing our audio stream: - -Alternatively, you can use `microphone.toggle()`. - -```ts -import { useCallStateHooks } from '@stream-io/video-react-native-sdk'; - -const { useMicrophoneState } = useCallStateHooks(); -const { microphone, isMute } = useMicrophoneState(); - -console.log(`Microphone is ${isMute ? 'off' : 'on'}`); -await microphone.toggle(); - -// or, alternatively -await microphone.enable(); -await microphone.disable(); -``` - -It's always best to await calls to `enable()`, `disable()`, and `toggle()`, however the SDK does its best to resolve potential race conditions: the last call always wins, so it's safe to make these calls in an event handler. - -Status is updated once the microphone is actually enabled or disabled. Use `optimisticIsMute` for the "optimistic" status that is updated immediately after toggling the microphone. - -### Audio mute status - -We can get the mute state of our audio stream by checking the `status` value returned from the `useMicrophoneState` hook: - -```ts -import { useCallStateHooks } from '@stream-io/video-react-native-sdk'; - -const { useMicrophoneState } = useCallStateHooks(); -const { status } = useMicrophoneState(); // status returns enabled, disabled or undefined -``` - -### Speaking while muted detection - -Our SDK provides a mechanism that can detect whether the user started to speak while being muted. -Through this mechanism, you can display a notification to the user, or apply any custom logic. - -This feature is enabled by default unless the user doesn't have the permission to send audio or explicitly disabled. - -```tsx -import { useCallStateHooks } from '@stream-io/video-react-native-sdk'; - -const { useMicrophoneState } = useCallStateHooks(); -const { isSpeakingWhileMuted, microphone } = useMicrophoneState(); - -if (isSpeakingWhileMuted) { - // your custom logic comes here - console.log('You are speaking while muted!'); -} - -// to disable this feature completely: -await microphone.disableSpeakingWhileMutedNotification(); - -// to enable it back: -await microphone.enableSpeakingWhileMutedNotification(); -``` - -### Access to the Microphone's MediaStream - -Our SDK exposes the current `mediaStream` instance that you can use for your needs (for example, local recording, etc...): - -```typescript -import { useCallStateHooks } from '@stream-io/video-react-native-sdk'; - -const { useMicrophoneState } = useCallStateHooks(); -const { mediaStream } = useMicrophoneState(); - -const [audioTrack] = mediaStream.getAudioTracks(); -console.log('Audio track', audioTrack); -``` - -## Speaker management - -:::warning -We do not support using hooks to change audio output source for React Native SDK as React Native WebRTC doesn't support device switching in RN. This means the following would not work with React Native SDK. - -```tsx -import { useCallStateHooks } from '@stream-io/video-react-native-sdk'; - -const { useMicrophoneState, useSpeakerState } = useCallStateHooks(); - -// This will give error on React Native. -const { selectedDevice } = useMicrophoneState(); -// Also, This will give error on React Native. -const { speaker, selectedDevice, devices } = useSpeakerState(); -``` - -::: - -We primarily utilize [react-native-incall-manager](https://github.com/react-native-webrtc/react-native-incall-manager) for managing speaker audio output in our SDK. - -This functionality is seamlessly integrated into our video calling and livestream components. - -In our SDK, we set the `media` type to `video` for video calls and livestream use-case, and `audio` for audio-only calls, in the [start](https://github.com/react-native-webrtc/react-native-incall-manager?tab=readme-ov-file#usage) method of the InCallManager. - -Passing `video` to `media`, routes the audio to device speaker by default unless an external device is connected. - -Passing `audio` to `media`, enables the proximity sensor, routes the audio through earpiece by default unless an external device is connected. - -When an external device is connected in between or before the call, the audio is always routed through it. - -For Audio Rooms, you have to control the behaviour of InCallManager. It allows you to start/stop the audio output on the device speaker through: - -```tsx -import InCallManager from 'react-native-incall-manager'; - -// Called on call join -InCallManager.start({ media: 'video' }); // `media` values - audio/video, default: audio - -// Called when call is left -InCallManager.stop(); -``` - -To force route the audio through speaker, use the following method: - -```tsx -import InCallManager from 'react-native-incall-manager'; - -InCallManager.setForceSpeakerphoneOn(true); // Pass true for speaker on, and false for off. Once off audio is always routed through earpiece. -``` - -For more information and customization please visit the official docs of [InCallManager](https://github.com/react-native-webrtc/react-native-incall-manager). - -## Client-side settings - -Before joining a call, user may need to preview their streams and decide their mute status. - -You can set the same on the dashboard for your app. This can be done under the `Call Types` and then selecting your call type. - -![Dashboard settings](../assets/03-core/04-camera-and-microphone/camera-and-microphone.png). - -We use the `useApplyDefaultMediaStreamSettings` hook to apply the backend settings. You can use the same, if needed. - -:::note -Generally, the backend settings are applied by default when you join the call(`call.join()`). To apply the backend settings somewhere else like custom Lobby, OutgoingCall components, etc., you can use the hook `useApplyDefaultMediaStreamSettings` above. -::: - -If you have use-cases, where you want to voluntarily have a different behaviour, you can design your own hook and apply your logic on top of the default one. diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/05-call-types.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/05-call-types.mdx deleted file mode 100644 index 418c87adfd..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/05-call-types.mdx +++ /dev/null @@ -1,20 +0,0 @@ ---- -id: configuring-call-types -title: Call Types ---- - -import CallTypesPage from '../../../shared/video/_call-types.mdx'; -import WithExternalLinks from '../../../shared/video/_withExternalLinks'; - - - - diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/06-querying-calls.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/06-querying-calls.mdx deleted file mode 100644 index e0603d9234..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/06-querying-calls.mdx +++ /dev/null @@ -1,203 +0,0 @@ ---- -id: querying-calls -title: Querying Calls -description: How to query calls ---- - -The Stream Video SDK allows you to query calls and watch them. -This allows you to build apps that display feeds of calls with real-time updates (without joining them). - -You can query calls based on built-in fields as well as any custom field you add to the calls. -Multiple filters can be combined using AND, OR logical operators, -each filter can use its comparison (equality, inequality, greater than, greater or equal, etc.). - -You can use the `StreamVideoClient` to query for: - -- Upcoming calls -- Calls that are currently live -- Popular live streams / audio rooms with a link to the recording - -## Client API - -You can query calls by using the client directly by using the following API: - -```ts -const { calls } = await client.queryCalls({ - filter_conditions: { ...filters }, - sort: [...sortOptions], - limit: 25, - watch: true, -}); -``` - -## Filters - -Filter expressions support multiple match criteria, and it's also possible to combine filters. -You can filter on the following fields: - -| Field | Description | -| -------------------- | ------------------------------------------------------------- | -| `id` | The id for this call | -| `cid` | The cid for this call. IE: `default:123` | -| `team` | The team id for the call. | -| `type` | The call type. Typically `default`, `livestream` etc... | -| `created_by_user_id` | The user id who created the call | -| `created_at` | When the call was created | -| `updated_at` | When the call was updated | -| `ended_at` | When the call ended | -| `starts_at` | When the call starts at | -| `backstage` | If the call is in backstage mode or not | -| `members` | Check if the call has these members listed | -| `ongoing` | Check if the call is ongoing or not | -| `custom` | You can query custom data using the `"custom.myfield"` syntax | - -For more information, visit the [filter operators guide](https://getstream.io/chat/docs/react/query_syntax_operators/?language=javascript). Or check the examples: - -### Calls that are about to start - -In this snippet, you can see how you can query for calls that have `livestream` type and are about to start 30 minutes from now: - -```typescript -import { StreamVideoClient } from '@stream-io/video-react-native-sdk'; - -let client: StreamVideoClient; - -const inNext30mins = new Date(Date.now() + 1000 * 60 * 60 * 30); - -const { calls } = await client.queryCalls({ - filter_conditions: { - type: { $eq: 'livestream' }, - starts_at: { $gt: inNext30mins.toISOString() }, - }, - sort: [{ field: 'starts_at', direction: -1 }], - limit: 10, - watch: true, -}); -``` - -### Call filters on a custom property - -```typescript -import { StreamVideoClient } from '@stream-io/video-react-native-sdk'; - -let client: StreamVideoClient; - -const { calls } = await client.queryCalls({ - filter_conditions: { 'custom.color': 'red' }, - limit: 10, - watch: true, -}); -``` - -### Calls that are ongoing / currently have participants - -```typescript -import { StreamVideoClient } from '@stream-io/video-react-native-sdk'; - -let client: StreamVideoClient; - -const { calls } = await client.queryCalls({ - filter_conditions: { ongoing: true }, -}); -``` - -### Calls the user has created or is a member of - -```typescript -import { StreamVideoClient } from '@stream-io/video-react-native-sdk'; - -let client: StreamVideoClient; - -const { calls } = await client.queryCalls({ - filter_conditions: { - $or: [ - { created_by_user_id: '' }, - { members: { $in: [''] } }, - ], - }, - limit: 10, - watch: true, -}); -``` - -## Sorting - -The `SortParamRequest` model contains two properties: `field` and `direction`. - -The `direction` can be `1` for ascending and `-1` for descending, while the field can be one of the following values: - -| Field | Description | -| ------------ | ------------------------------------------------------- | -| `starts_at` | When the call starts at | -| `created_at` | When the call was created | -| `updated_at` | When the call was updated | -| `ended_at` | When the call ended | -| `type` | The call type. Typically `default`, `livestream` etc... | -| `id` | The id for this call | -| `cid` | The cid for this call. IE: `default:123` | - -```typescript -import { StreamVideoClient } from '@stream-io/video-react-native-sdk'; - -let client: StreamVideoClient; - -const { calls } = await client.queryCalls({ - sort: [{ field: 'starts_at', direction: -1 }], - limit: 10, - watch: true, -}); -``` - -It's possible to provide multiple sort parameters: - -```typescript -import { StreamVideoClient } from '@stream-io/video-react-native-sdk'; - -let client: StreamVideoClient; - -const { calls } = await client.queryCalls({ - sort: [ - { field: 'starts_at', direction: -1 }, - { field: 'created_at', direction: 1 }, - ], - limit: 10, - watch: true, -}); -``` - -## Watching calls - -If you specify `watch: true` as an option, the SDK will create a subscription to the call data on the server and you'll be able to receive updates in real-time. - -The server will send updates to the client when the call data changes -(for example, members are updated, a call session has started, etc...). -This is useful for showing a live preview of who is in the call or building a call dashboard. - -## Pagination - -You can specify the page size using the `limit` option. The API response will include links to the previous/next pages. The following code example shows how pagination works: - -```typescript -import { StreamVideoClient } from '@stream-io/video-react-native-sdk'; - -let client: StreamVideoClient; - -const inNext30mins = new Date(Date.now() + 1000 * 60 * 60 * 30); -const callQuery = { - filter_conditions: { - type: { $eq: 'livestream' }, - starts_at: { $gt: inNext30mins.toISOString() }, - }, - sort: [{ field: 'starts_at', direction: -1 }], - limit: 10, - watch: true, -}; - -let { calls, prev, next } = await client.queryCalls(callQuery); - -// Go to the next page -({ calls, prev, next } = await client.queryCalls({ ...callQuery, next })); - -// Go to the previous page -({ calls, prev, next } = await client.queryCalls({ ...callQuery, prev })); -``` diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/07-query-call-members.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/07-query-call-members.mdx deleted file mode 100644 index 8ff806876d..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/07-query-call-members.mdx +++ /dev/null @@ -1,58 +0,0 @@ ---- -id: querying-call-members -title: Querying Call Members -description: How to query call members ---- - -import FilterConditions from '../../../shared/_filter-operators.mdx'; -import CallMemberFilters from '../../../shared/video/_call-member-filters.mdx'; -import CallMemberSort from '../../../shared/video/_call-member-sort-fields.mdx'; - -When you create or join a call you get a list of call members, however this can return at most 100 members: - -```typescript -// The maximum limit is 100 -// The default limit is 25 -await call.getOrCreate({ members_limit: 100 }); - -// or -await call.join({ members_limit: 100 }); -``` - -To get the complete list of call members the Stream API allows you to query, filter and sort members of a call using a paginated list. - -## Examples - -Below are a few examples of how to use this API: - -```typescript -const result = await call.queryMembers(); - -// sorting and pagination -const queryMembersReq = { - sort: [{ field: 'user_id', direction: 1 }], - limit: 2, -}; -const result = await call.queryMembers(queryMembersReq); - -// loading the next page -const result = await call.queryMembers({ - ...queryMembersReq, - next: result.next, -}); - -// filtering -const result = await call.queryMembers({ - filter_conditions: { role: { $eq: 'admin' } }, -}); -``` - -## Sort options - - - -## Filter options - - - - diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/08-keeping-call-alive.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/08-keeping-call-alive.mdx deleted file mode 100644 index d11e5c3d1b..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/08-keeping-call-alive.mdx +++ /dev/null @@ -1,69 +0,0 @@ ---- -id: keeping-call-alive -title: Keeping The Call Alive In Background ---- -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -One of the crucial functionalities of a video or audio calling application is to keep the call alive in the background. On this page, we focus on what must be added to your app to support this. After enabling, the user of your app will notice that the call is kept alive even if the app goes to the background as they will still hear the remote audio streams while the app is kept in the background. - -## Android Setup - -### Android 8 and above - -Starting in Android 8.0 (API level 26), Android allows activities to launch in picture-in-picture (PiP) mode. This means that we can keep the call alive in the background by opening PiP mode. - -Head over to the documentation [here](../../advanced/pip/) on how to picture-in-picture (PiP) mode for Android. - -### Android 7 - -There is no support for Picture-in-picture (PiP) mode below Android 8. Hence in those platforms, we use a [foreground service](https://developer.android.com/guide/components/foreground-services) to keep the call alive. The SDK will automatically create and manage the foreground service. The only requirement is to install the `Notifee` library so that SDK can handle a foreground service. To install the [`Notifee`](https://github.com/invertase/notifee) library, run the following command in your terminal of choice: - - - - -```bash title=Terminal -npx expo install @notifee/react-native -``` - - - - -```bash title=Terminal -yarn add @notifee/react-native -npx pod-install -``` - - - - -#### Optional: override the default configuration of the foreground service notifications - -You can also optionally override the default configuration of the notification used by the SDK. Below we give an example of that: - -```ts -import { StreamVideoRN } from '@stream-io/video-react-native-sdk'; - -StreamVideoRN.updateConfig({ - foregroundService: { - android: { - // you can edit the title and body of the notification here - notificationTexts: { - title: 'Video call is in progress', - body: 'Tap to return to the call', - }, - }, - }, -}); -``` - -## iOS Setup - -The way to keep audio alive in the background is to enable the `audio` background mode. When you enable this capability, your app's audio playback will continue to play when users lock their iOS device or switch to another app. In Xcode: Open the `Info.plist` file and add `audio` in `UIBackgroundModes`. By editing this file with a text editor, you should see: - -```xml -UIBackgroundModes - - audio - -``` diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/08-native-permissions.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/08-native-permissions.mdx deleted file mode 100644 index 368230cc80..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/08-native-permissions.mdx +++ /dev/null @@ -1,74 +0,0 @@ ---- -id: native-permissions -title: Manage Native Permissions ---- - -In this guide, we will create a function to request the native permissions required for the app. - -Once the function is called, we should see permissions being requested like below: - -![Preview of the final result](../assets/03-core/08-native-permissions/permissions.png) - -## Setup - -Ensure that relevant permissions are declared in your `AndroidManifest.xml` and `Info.plist` as mentioned in the installation([Native CLI](../../setup/installation/react-native) or [Expo](../../setup/installation/expo)) guide. - -Additionally, to easily request permissions on both platforms, we will use the [`react-native-permissions`](https://github.com/zoontek/react-native-permissions) library. You can run the following command to install it: - -```bash title=Terminal -yarn add react-native-permissions -``` - -:::note -Do not forget to perform the additional setup steps for iOS mentioned in the [`react-native-permissions` library documentation](https://github.com/zoontek/react-native-permissions#ios) -::: - -## Step 1 - Add a function to request permissions in the app - -In this step, we create a function called `requestAndUpdatePermissions`. This function will be responsible for requesting permissions. - -```ts title=src/utils/requestAndUpdatePermissions.ts -import { Platform } from 'react-native'; -import { PERMISSIONS, requestMultiple } from 'react-native-permissions'; - -export const requestAndUpdatePermissions = async () => { - if (Platform.OS === 'ios') { - // Request camera and mic permissions on iOS - const results = await requestMultiple([ - PERMISSIONS.IOS.CAMERA, - PERMISSIONS.IOS.MICROPHONE, - ]); - } else if (Platform.OS === 'android') { - // Request camera, mic, bluetooth and notification permissions on Android - const results = await requestMultiple([ - PERMISSIONS.ANDROID.CAMERA, - PERMISSIONS.ANDROID.RECORD_AUDIO, - PERMISSIONS.ANDROID.BLUETOOTH_CONNECT, - PERMISSIONS.ANDROID.POST_NOTIFICATIONS, - ]); - } -}; -``` - -## Step 2 - Use the function on your desired screen - -In this final step, we use the `requestAndUpdatePermissions` function in the screen of our choice. As an example below, we use it in the screen where we pass the `call` object to the SDK. - -```tsx -import { useEffect } from 'react'; -import { requestAndUpdatePermissions } from 'src/utils/requestAndUpdatePermissions'; -import { StreamVideo, StreamCall } from '@stream-io/video-react-native-sdk'; - -const MyApp = () => { - // request permissions on mount - useEffect(() => { - requestAndUpdatePermissions(); - }, []); - - return ( - - {/* You UI */} - - ); -}; -``` diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/09-permissions-and-moderation.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/09-permissions-and-moderation.mdx deleted file mode 100644 index cedaf0560a..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/09-permissions-and-moderation.mdx +++ /dev/null @@ -1,183 +0,0 @@ ---- -title: Permissions & Moderation -description: Explanation of call permissions and moderation features ---- - -In many types of calls, there is a requirement for providing different users with certain permissions and capabilities. -A typical example is a webinar where the host wants to control who can speak or who can share their video or screen. - -The Stream Video SDK provides a certain set of permissions and capabilities -that can be used to control the behavior of participants in a call. - -## Conceptual overview - -### Roles - -The Stream Video API allows assigning roles to users. Each user has a global role, and they will also have a call-level role for each call they join. The Stream Video API provides a set of predefined roles, but it's also possible to create your own roles. - -### Call types - -[Call types](../../core/configuring-call-types/) also allow for a more granular control system: - -- you can enable/disable certain features on a call type level -- you can configure how each call-level role works on a call type level - -### Capabilities - -Based on a user's roles and the call type settings, we can determine which actions are allowed for a user joined to a specific call. - -## Permissions - -As soon as you join a call, the `Call` instance would allow you to check the permissions of the local user -or perform some permission-related actions: - -### Check permissions - -```ts -import { OwnCapability } from '@stream-io/video-react-native-sdk'; - -const call = streamVideoClient.call(type, id); -const canSendAudio = call.permissionsContext.hasPermission( - OwnCapability.SEND_AUDIO, -); -``` - -In our React Native Video SDK, you can use the `useHasPermissions` hook to check for permissions. - -```tsx -import { - useCallStateHooks, - OwnCapability, -} from '@stream-io/video-react-native-sdk'; - -const { useHasPermissions } = useCallStateHooks(); -const canSendAudio = useHasPermissions(OwnCapability.SEND_AUDIO); -``` - -### Request permissions - -Every user may request permission to perform certain actions depending on the call type and call settings. -For example, in an audio-room call type, only the hosts have `send-audio` permission by default. -Other users should request this permission before they can start sending audio if the call settings allow it. - -```ts -import { OwnCapability } from '@stream-io/video-react-native-sdk'; - -const call = streamVideoClient.call(type, id); -if (!call.permissionsContext.canRequest(OwnCapability.SEND_AUDIO)) { - console.log('The host has disabled the ability to request this permission'); - return; -} -await call.requestPermissions({ - permissions: [OwnCapability.SEND_AUDIO], -}); -``` - -### Approving permission requests - -Call hosts and moderators can approve permission requests from other users. -Whenever a user requests a certain permission, a `call.permission_request` event will be emitted on the `Call` instance. -You can listen to this event and approve the request. - -```ts -import { - PermissionRequestEvent, - StreamCallEvent, -} from '@stream-io/video-react-native-sdk'; - -const call = streamVideoClient.call(type, id); -call.on('call.permission_request', async (event: StreamCallEvent) => { - const request = event as PermissionRequestEvent; - if (shouldApproveRequest(request)) { - await call.grantPermissions(request.user.id, request.permissions); - } -}); -``` - -### Moderation - -At any time, a moderator or host can decide to either grant or revoke certain permission to any participant. - -```ts -import { OwnCapability } from '@stream-io/video-react-native-sdk'; - -const call = streamVideoClient.call(type, id); -await call.updateUserPermissions({ - user_id: 'demo-user', - grant_permission: [OwnCapability.SEND_AUDIO, OwnCapability.SEND_VIDEO], - revoke_permissions: [OwnCapability.SCREENSHARE], -}); - -// alternate API for granting user permissions: -await call.grantPermissions('demo-user', [ - OwnCapability.SEND_AUDIO, - OwnCapability.SEND_VIDEO, -]); - -// alternate API for revoking user permissions: -await call.revokePermissions('demo-user', [OwnCapability.SCREENSHARE]); -``` - -The end user would get notified via a WebSocket event with a type: `call.permissions_updated`. -In the case of revoked permissions, the SDK would automatically stop publishing the appropriate tracks. - -### Muting participants - -In addition to granting or revoking permissions, a moderator or host can also mute a participant. -This is a common scenario as quite often, participants may be a source of unwanted noise or distraction. - -```ts -const call = streamVideoClient.call(type, id); -await call.muteUser('demo-user-id', 'audio'); -await call.muteUser('demo-user-id', 'video'); -await call.muteUser('demo-user-id', 'screenshare'); - -// or, mute in bulk -await call.muteUser(['demo-user-id', 'demo-user-id-2'], 'audio'); - -// or, muting self -await call.muteSelf('audio'); - -// or, muting others -await call.muteOthers('audio'); - -// or, mute all, including self. -await call.muteAllUsers('audio'); -``` - -This operation doesn't revoke any permission, and the user would still be able to unmute itself. - -### Ending call for everyone - -In some cases, a moderator or host may want to end the call for everyone. - -```ts -const call = streamVideoClient.call(type, id); -await call.endCall(); -``` - -This operation will emit `call.ended` event to every participant in the call. -The SDK would automatically stop publishing any tracks and leave the call. - -Ended calls can't be re-joined. - -## Capabilities - -Every user connecting to a Stream Call has a set of Capabilities. -The capabilities of the local user live in the metadata of the `Call` instance. - -```tsx -const call = streamVideoClient.call(type, id); -const { own_capabilities } = call.state.metadata; -``` - -In our React Native Video SDK, you can use the `useOwnCapabilities` hook. - -```tsx -import { useCallStateHooks } from '@stream-io/video-react-bindings'; - -const { useOwnCapabilities } = useCallStateHooks(); -const ownCapabilities = useOwnCapabilities(); -``` - -Capabilities will have the type [`OwnCapability`](https://github.com/GetStream/stream-video-js/blob/main/packages/client/src/gen/coordinator/index.ts). diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/10-reactions.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/10-reactions.mdx deleted file mode 100644 index 7980b30c1e..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/10-reactions.mdx +++ /dev/null @@ -1,71 +0,0 @@ ---- -title: Reactions -description: How reactions work ---- - -Reactions allow call participants to send emojis in real-time. - -Custom events let participants send and receive arbitrary WebSocket messages. For example, if you want to implement a drawing feature in your call, you can use custom events for synchronizing the drawing board between participants. - -## Reactions - -:::tip -[`CallControls`](../../ui-components/call/call-controls/) optionally utilizes [`ReactionsButton`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Call/CallControls/ReactionsButton.tsx) component that support reactions out-of-the-box, but for advanced use-cases you can also build your own reaction system. -::: - -### Sending reactions - -You can send a reaction using the `sendReaction` method of a `Call` instance. - -```typescript -const call: Call; - -await call.sendReaction({ type: 'raised-hand' }); -``` - -The value of the `type` attribute can be any string. - -It's also possible to provide additional data for the reaction: - -```typescript -const call: Call; - -await call.sendReaction({ - type: 'raised-hand', - emoji_code: ':raise-hand:', - custom: { clearAfterTimeout: true }, -}); -``` - -The `emoji_code` attribute is used by the SDK components to decide which emoji to display on the UI. - -The `custom` property can contain any data. - -### Receiving reactions - -Reactions are only delivered to clients that are [watching the call](../../advanced/events/#call-events). - -The [participant state](../../core/call-and-participant-state/#observe-participant-state) will contain the latest reaction of each participant: - -```typescript -const { useParticipants } = useCallStateHooks(); -const participants = useParticipants(); - -const reactions = participants.map((p) => p.reaction); -``` - -You can also subscribe to the `call.reaction_new` WebSocket event to receive reactions. For more information, check out our [Events guide](../../advanced/events). - -### Clearing reactions - -If you're using the [participant state](../../core/call-and-participant-state/#observe-participant-state) for receiving reactions, you can also clear the latest reaction using the `resetReaction` method: - -```typescript -const call: Call; -const { useParticipants } = useCallStateHooks(); -const participants = useParticipants(); - -call.resetReaction(participants[0].sessionId); -``` - -This is a local action, it won't send any WebSocket messages. It's helpful if you only want to display reactions for a set period of time. diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/11-custom-events.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/11-custom-events.mdx deleted file mode 100644 index 99d6354c14..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/11-custom-events.mdx +++ /dev/null @@ -1,41 +0,0 @@ ---- -title: Custom Events -description: How custom events work ---- - -You can use custom events to send data among the participants in the call. -This is a realtime layer that you can use to broadcast your own events to. - -## Sending custom events - -For example, if you are building a collaborative drawing app, you can send the coordinates to the other participants with the following code: - -```typescript -await call.sendCustomEvent({ - type: 'draw', - x: 10, - y: 30, -}); -``` - -Please note that the total payload for these events is limited to 5KB in size. - -## Receiving custom events - -Custom events are only delivered to clients that are [watching the call](../../advanced/events/#call-events). - -To receive custom events, you need to subscribe to the `custom` event on the call instance: - -```typescript -const unsubscribe = call.on('custom', (event: CustomVideoEvent) => { - const payload = event.custom; - if (payload.type === 'draw') { - console.log(`Received draw event: x=${payload.x}, y=${payload.y}`); - } -}); - -// Unsubscribe when you no longer need to listen to custom events -unsubscribe(); -``` - -For more information, check out our [Events guide](../../advanced/events). diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/11-sorting-api.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/11-sorting-api.mdx deleted file mode 100644 index af6dc53351..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/11-sorting-api.mdx +++ /dev/null @@ -1,283 +0,0 @@ ---- -title: Participant sorting -description: Overview of the Sorting API used to sort call participants. ---- - -The Participant Sorting API is a powerful tool built on top of the internal `Comparator` API, -providing developers with the ability to sort participants in various scenarios. This API offers common comparators and built-in presets that can be easily customized or used out-of-the-box, making participant sorting a seamless experience. - -When dealing with real-time communication applications, it is often necessary to sort participants based on specific criteria. -Whether you need to adjust the sorting in existing view layouts or define new sorting presets, the **Participant Sorting API** is here to simplify the process. - -By utilizing the `Comparator` API and the provided built-in comparators and presets, developers can effortlessly sort participants according to their requirements. - -## `Comparator` API overview - -The `Comparator` API is the foundation upon which the Participant Sorting API is built. -It defines a function type `Comparator` that takes two arguments `a` and `b` of type `T` and returns `-1`, `0`, or `1` based on the comparison between the two items. -This API allows developers to create custom comparators tailored to their specific needs. - -Ultimately, this API can be used in conjunction with the `Array.sort` method to sort any type of data. - -```ts -import { - Comparator, - combineComparators, - conditional, - descending, -} from '@stream-io/video-react-native-sdk'; - -type Participant = { - id: number; - name: string; -}; - -// comparator that sorts by name in ascending order -const byName: Comparator = (a, b) => { - if (a.name < b.name) return -1; - if (a.name > b.name) return 1; - return 0; -}; - -// comparator that sorts by id in ascending order -const byId: Comparator = (a, b) => { - if (a.id < b.id) return -1; - if (a.id > b.id) return 1; - return 0; -}; - -// comparator that sorts by age in ascending order -const byAge: Comparator = (a, b) => { - if (a.age < b.age) return -1; - if (a.age > b.age) return 1; - return 0; -}; - -// creates a new comparator that sorts by name in descending order -const byNameDescending: Comparator = descending(byName); - -// `conditional` creates a new comparator that applies the provided comparator only -// if the provided predicate returns `true`. The `predicate` itself, takes the two arguments -// and returns a boolean value. -const byAgeIfEnabled: Comparator = conditional( - (a, b) => opts.isSortByAgeEnabled, -)(descending(byAge)); - -// combineComparator creates a new Comparator that combines the provided comparators in one. -// this comparator will sort by name in descending order, by age if enabled, -// and then by id in ascending order -const sortingCriteria = combineComparators( - byNameDescending, - byAgeIfEnabled, - byId, -); - -// participants array -const sorted = [p1, p2, p3].sort(sortingCriteria); -``` - -:::tip -The `Comparator` API is quite generic and can be used to sort any type of data. -Works great in pair with the [`Array.sort`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) API. -::: - -## Built-in common comparators - -The Participant Sorting API provides a set of common comparators that cover common sorting scenarios. -These comparators are specifically designed for participant sorting and offer convenience when defining sorting criteria. - -The built-in common comparators include: - -- `dominantSpeaker`: Sorts participants based on their dominance in the call. -- `speaking`: Sorts participants based on whether they are currently speaking. -- `screenSharing`: Sorts participants based on whether they are currently screen sharing. -- `publishingVideo`: Sorts participants based on whether they are currently publishing video. -- `publishingAudio`: Sorts participants based on whether they are currently publishing audio. -- `pinned`: Sorts participants based on whether they are pinned in the user interface. -- `reactionType(type)`: Sorts participants based on the type of reaction they have. -- `role(...roles)`: Sorts participants based on their assigned role. -- `name`: Sorts participants based on their names. - -All of these comparators are available in the `@stream-io/video-react-native-sdk` package and can be imported as follows: - -```ts -import { - dominantSpeaker, - speaking, - screenSharing, - publishingVideo, - publishingAudio, - pinned, - reactionType, - role, - name, -} from '@stream-io/video-react-native-sdk'; - -// ... -``` - -These built-in comparators serve as a starting point for sorting participants -and can be used individually or combined to create more complex sorting criteria. - -## Sorting customization on the call level - -The Participant Sorting API allows dynamic sorting customization during runtime. -Developers can utilize the `call.setSortParticipantsBy(comparator)` API to change the sorting criteria based on user interactions or application logic. -This flexibility empowers developers to provide sorting controls within their application, giving users the ability to customize participant sorting according to their preferences. - -Lets take a look at an example: - -```ts -import { - useCall, - combineComparators, - dominantSpeaker, - publishingVideo, - publishingAudio, - screensharing, - speaking, - reactionType, - pinnned, -} from '@stream-io/video-react-native-sdk'; - -// ... boilerplate code - -// we take the existing call instance -const call = useCall(); - -// we create a new comparator that combines the built-in comparators -// and sorts participants by the following criteria: -const comparator = combineComparators( - pinned, // 1. pinned participants first - screenSharing, // 2. participants who are screensharing - dominantSpeaker, // 3. dominant speaker - reactionType('raised-hand'), // 4. participants with raised hand - speaking, // 5. participants currently speaking - publishingVideo, // 6. participants publishing video - publishingAudio, // 7. participants publishing audio - // 8. everyone else -); - -// will apply the new sorting criteria immediately -call.setSortParticipantsBy(comparator); -``` - -:::note -In some scenarios, we might want to have special sorting criteria for a specific component in our app. -For example, in the participant list component, we might want to sort participants by name. -::: - -For this purpose, we have extended the built-in `useParticipants` hook with a `sortBy: Comparator` option parameter. - -```ts -import { - combineComparators, - name, - useCallStateHooks, -} from '@stream-io/video-react-native-sdk'; - -const { useParticipants } = useCallStateHooks(); -// this will override the call's default sorting criteria -// and will return a list of participants sorted by name -const participants = useParticipants({ sortBy: name }); - -// you can also provide your custom comparator -const myComparator = combineComparators(/* ... */); -const participants = useParticipants({ sortBy: myComparator }); -``` - -:::caution -When using custom comparator in combination with the `useParticipants` hook, please make sure to provide a stable reference to the comparator. -Otherwise, you might end up with unexpected behavior (unexpected re-renders, etc.). - -Our proposal is to use stateless comparators defined outside of the component's scope. -In case you need to use a stateful comparator, please make sure to memoize it using [`React.useMemo`](https://react.dev/reference/react/useMemo) or [`React.useCallback`](https://react.dev/reference/react/useCallback) hooks. - -```ts -// stateless comparator -const myStatelessComparator = combineComparators(/* ... */); - -export const MyComponent = () => { - const { useParticipants } = useCallStateHooks(); - // component scope - const participants1 = useParticipants({ sortBy: myStatelessComparator }); - - // memoized comparator - const myStatefulComparator = React.useMemo( - () => combineComparators(/* ... */), - [dependency1, dependency2], - ); - const participants2 = useParticipants({ sortBy: myStatefulComparator }); - - // ... -}; -``` - -::: - -## Built-in sorting presets - -To further simplify participant sorting, the Participant Sorting API offers built-in presets. -These presets are pre-configured sorting criteria linked to specific call types, reducing the effort required to define sorting rules. - -The following presets are available: - -- `defaultSortPreset`: The default sorting preset applicable to general call scenarios. -- `speakerLayoutSortPreset`: A preset specifically designed for the [`'default'` call type](../configuring-call-types/#default), optimizing participant sorting for speaker layout view. -- `livestreamOrAudioRoomSortPreset`: A preset tailored for the [`'livestream'`](../configuring-call-types#livestream) and [`'audio_room'`](../configuring-call-types#audio-room) call types, ensuring optimal participant sorting in livestream or audio room scenarios. - -These presets are directly applied to the call's sorting mechanism. -For custom call types, unless specified otherwise, our SDK would use the `defaultSortPreset` preset. - -All of these presets are available in the `@stream-io/video-react-native-sdk` package and can be imported as follows: - -```ts -import { - defaultSortPreset, - speakerLayoutSortPreset, - livestreamOrAudioRoomSortPreset, -} from '@stream-io/video-react-native-sdk'; -``` - -:::tip - -For your custom call types, you can define your participant sorting presets and register them generally in the SDK. - -Check the next section to learn how. - -::: - -## Sorting customization on the call type - -Sometimes, you want to keep your UI components free from sorting logic and instead, define sorting criteria per call type. -To do so, you can register your sorting presets for your custom call types or override the existing ones -by using our SDKs `CallTypes` registry. - -```ts -import { combineComparators, CallTypes, CallType } from '@stream-io/video-react-native-sdk'; - -// setup your custom sorting preset -const myCustomSortPreset = combineComparators(/* ... */); - -// update existing type -CallTypes.get('default').options.sortParticipantsBy = myCustomSortPreset; - -// register new type -CallTypes.register(new CallType('my-custom-type', { - options: { - sortParticipantsBy: myCustomSortPreset, - }, -}); -``` - -## Disabling participant sorting - -In some cases, you may want to disable participant sorting altogether. -This can be achieved by setting our special `noopComparator` as the sorting criteria of the Call or the CallType. - -```ts -import { noopComparator, useCall } from '@stream-io/video-react-native-sdk'; - -const call = useCall(); -call.setSortParticipantsBy(noopComparator()); -``` diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/_category_.json b/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/_category_.json deleted file mode 100644 index a2907d8ee4..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/03-core/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Core concepts", - "position": 3 -} diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/01-overview.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/01-overview.mdx deleted file mode 100644 index 2985b88eaf..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/01-overview.mdx +++ /dev/null @@ -1,118 +0,0 @@ ---- -title: Overview -description: Overview of the UI components ---- - -import ImageShowcase from '@site/src/components/ImageShowcase'; -import ParticipantCameraOn from '../assets/04-ui-components/participants/participant-view/participant-camera-on.png'; -import ParticipantCameraOff from '../assets/04-ui-components/participants/participant-view/participant-camera-off.png'; -import IncomingCall from '../assets/04-ui-components/call/ringing-call-content/incoming-call.png'; -import OutgoingCall from '../assets/04-ui-components/call/ringing-call-content/outgoing-call.png'; - -Stream SDK aims to make it as easy as possible to build your own video calling, audio rooms, and live streams. We support a low-level client, guides on building your own UI, and several pre-built UI components. If you quickly want to add calling to your app, you can do that just in an hour with these UI components. - -### Rendering Participant - -If you want to render a participant's video together with: - -- A label/name for the participant -- Network quality indicator -- Mute/unmute indicator -- Fallback for when video is muted -- Reactions - -We can use [ParticipantView](../participants/participant-view): - -```tsx - -``` - -You will see the result as below: - - - -### Video Call UI - -You can use the [`CallContent`](../call/call-content): - -- Header: Content is shown that calls information or additional actions like back button, participant info. -- Call Participants Layout: A call video that renders the full participants of the call. -- Controls: Content is shown that allows users to trigger different actions to control a joined call. - -```tsx -const App = () => { - return ( - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, -}); -``` - -![Preview of Video Call UI](../assets/04-ui-components/call/call-content/call-content-grid-3-participants.png) - -### Ringing (Incoming/Outgoing calls) - -You can implement incoming/outgoing screens using our [`RingingCallContent`](../call/ringing-call-content) component: - -- It displays the [`IncomingCall`](../call/incoming-call)/[`OutgoingCall`](../call/outgoing-call) components depending upon the call states. -- After the call is accepted its displays the [`CallContent`](../call/call-content) component. -- While the call is in joining state it shows `JoiningCallIndicator` component. - - - -```tsx -const App = () => { - return ( - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, -}); -``` - -### UI Component Customization - -Stream SDK provides highly customizable UI components. Therefore, you can adjust each style or implement your own UI for each part of the components. This list describes what you can do with Stream SDK's UI components: - -- You can also build your UI components from scratch with our low-level UI component using our [UI Cookbook](../../ui-cookbook/overview). -- Use our library of built-in components. -- Mix & Match between your own and built-in components. diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/02-theme.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/02-theme.mdx deleted file mode 100644 index d8709fb00b..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/02-theme.mdx +++ /dev/null @@ -1,51 +0,0 @@ ---- -id: theme -title: Theme ---- - -The React Native Video SDK ships with a default UI theme that is included in your application. In this chapter, we'll go through the details on how you can utilize and customize the default theme. - -### Usage - -To accurately create a theme we suggest utilizing our exported types to create your own theme. This will allow you to ensure the keys you are using in your theme object are correct. - -When you provide a theme as a prop a deep merge of the theme and default theme is performed so only styles designated in the custom theme overwrite the defaults. We provide a helper type DeepPartial that makes all of the keys at every depth optional, this is to account for the deep merge that is performed. - -You can find the default theme object in [theme.ts](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/theme/theme.ts). - -```tsx -import type { DeepPartial, Theme } from '@stream-io/video-react-native-sdk'; - -const theme: DeepPartial = { - callControls: { - container: { - backgroundColor: 'red', - }, - }, -}; -``` - -Now you can provide these theme to the `style` prop of [`StreamVideo`](../core/stream-video/#style) component. - -```tsx -import { - StreamVideo, - StreamVideoClient, -} from '@stream-io/video-react-native-sdk'; - -export const App = () => { - const client = new StreamVideoClient(/* ... */); - - return ( - - - - ); -}; -``` - -![Preview of the added Call Controls theme](../assets/04-ui-components/theme-preview.png) - -You can change the default button, icon and avatar variants using the `buttonSizes`, `iconSizes` and the `avatarSizes` under the `variants` property. - -The font style and the colors can be changed as well for different font and color usages within the SDK using the `typefaces` and the `colors` property. diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/_category_.json b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/_category_.json deleted file mode 100644 index 7daf546d2a..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "Video UI Components" -} diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/_category_.json b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/_category_.json deleted file mode 100644 index 13d61e708e..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Call", - "position": 4 -} diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/call-content.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/call-content.mdx deleted file mode 100644 index 63086107a8..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/call-content.mdx +++ /dev/null @@ -1,171 +0,0 @@ ---- -id: call-content -title: CallContent ---- - -import ImageShowcase from '@site/src/components/ImageShowcase'; -import CallContentGrid from '../../assets/04-ui-components/call/call-content/call-content-grid.png'; -import CallContentGrid3Participants from '../../assets/04-ui-components/call/call-content/call-content-grid-3-participants.png'; -import CallContentSpotlight from '../../assets/04-ui-components/call/call-content/call-content-spotlight.png'; - -import CallTopView from '../../common-content/ui-components/call/call-content/call-top-view.mdx'; -import CallControls from '../../common-content/ui-components/call/call-content/call-controls.mdx'; -import Landscape from '../../common-content/ui-components/call/call-content/landscape.mdx'; -import OnBackPressed from '../../common-content/ui-components/call/call-content/on-back-pressed.mdx'; -import OnParticipantInfoPress from '../../common-content/ui-components/call/call-content/on-participant-info-press.mdx'; -import SupportedReactions from '../../common-content/ui-components/call/call-content/supported-reactions.mdx'; -import ParticipantLabel from '../../common-content/ui-components/call/call-content/participant-label.mdx'; -import ParticipantReaction from '../../common-content/ui-components/call/call-content/participant-reaction.mdx'; -import ParticipantNetworkQualityIndicator from '../../common-content/ui-components/call/call-content/participant-network-quality-indicator.mdx'; -import ParticipantVideoFallback from '../../common-content/ui-components/call/call-content/participant-video-fallback.mdx'; -import VideoRenderer from '../../common-content/ui-components/call/call-content/video-renderer.mdx'; -import OnHangupCallHandler from '../../common-content/ui-components/call/call-content/on-hangup-call-handler.mdx'; -import ParticipantView from '../../common-content/ui-components/call/call-content/participant-view.mdx'; -import FloatingParticipantView from '../../common-content/ui-components/call/call-content/floating-participant-view.mdx'; -import CallParticipantsList from '../../common-content/ui-components/call/call-content/call-participants-list.mdx'; - -The `CallContent` is the highest-level UI component that allows you to build your own call screen with full UI elements. So you don't need to take care much about each feature that you need to build a video call screen with this component. - -Basically what you can do with the `CallContent` is: - -- A full call screen with multiple UI elements, such as the top view, participants grid, and control action buttons. -- Handles the state and actions of enabling and disabling camera, microphone, and speakerphone. -- Renders screen sharing. -- Display participant information, such as network quality, name, microphone states, and reactions. -- Renders video of the participant. -- Allows changing layout from `grid` to `spotlight`. - -Based on the call state, the CallContent provides a list or grid of participants, with their avatars and names, or a video if they're publishing, with more information for each participant, like their connection quality, etc. - - - -## General usage - -Let's see how to show the `CallContent` UI: - -```tsx {13} -import { - Call, - CallContent, - StreamCall, -} from '@stream-io/video-react-native-sdk'; - -const VideoCallUI = () => { - let call: Call; - // your logic to create a new call or get an existing call - - return ( - - - - ); -}; -``` - -## Props - -### `layout` - -This switches the list between the grid and the spotlight mode. - -| Type | Default Value | -| --------------------- | ------------- | -| `grid` \| `spotlight` | `grid` | - -:::note -When a screen is shared, the layout automatically changes to `spotlight` mode. -::: - -### `landscape` - - - -### [`onBackPressed`](../call-top-view/#onbackpressed) - - - -### [`onParticipantInfoPress`](../call-top-view/#onparticipantinfopress) - - - -### [`onHangupCallHandler`](../call-controls/#onhangupcallhandler) - - - -### `supportedReactions` - - - -### `CallTopView` - - - -### `CallControls` - - - -### `ParticipantLabel` - - - -### `ParticipantReaction` - - - -### `ParticipantVideoFallback` - - - -### `ParticipantNetworkQualityIndicator` - - - -### `VideoRenderer` - - - -### `ParticipantView` - - - -### `FloatingParticipantView` - - - -### `CallParticipantsList` - - - -### `ScreenShareOverlay` - -Component to customize the screen share overlay, when the screen is shared by a user. - -| Type | Default Value | -| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `ComponentType`\| `undefined` | [`ScreenShareOverlay`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/utility/ScreenShareOverlay.tsx) | - -## Customization - -If you're looking for guides on how to override and customize this UI, we have various [UI Cookbook](../../../ui-cookbook/overview) recipes for you: - -- [Call Top View](../../../ui-cookbook/replacing-call-top-view) and [Call Controls](../../../ui-cookbook/replacing-call-controls). -- [`ParticipantView`](../../participants/participant-view) can be customized using the following [customization guide](../../participants/participant-view/#customization). -- You can pass your own component to [`LocalParticipantView`](#localparticipantview) prop to customize it. diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/call-controls.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/call-controls.mdx deleted file mode 100644 index 9b4d418ad0..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/call-controls.mdx +++ /dev/null @@ -1,145 +0,0 @@ ---- -id: call-controls -title: CallControls ---- - -import OnHangupCallHandler from '../../common-content/ui-components/call/call-content/on-hangup-call-handler.mdx'; -import Landscape from '../../common-content/ui-components/call/call-content/landscape.mdx'; - -CallControls allows users to execute actions during the call(for example, mute/unmute audio/video, reactions, hang-up calls, etc.). -We provide a built-in `CallControls` component that displays all relevant call controls during a call. - -![Preview of the CallControls component.](../../assets/04-ui-components/call/call-controls/call-controls.png) - -## General Usage - -The `CallControls` component displays the available controls for the call. - -```tsx {13} -import { - Call, - CallContent, - CallControls, - StreamCall, -} from '@stream-io/video-react-native-sdk'; - -const VideoCallUI = () => { - let call: Call; - - return ( - - - - ); -}; -``` - -## Props - -### `onHangupCallHandler` - - - -### `landscape` - - - -## Built-in call controls - -Each call control is available as a separate UI component. - -### [`AcceptCallButton`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Call/CallControls/AcceptCallButton.tsx) - -This component is used in the [Incoming Call](../incoming-call) component to accept an incoming call. - -| Type | Type | Description | -| --------------------- | --------------------------- | ---------------------------------------------------------------------------------------------------- | -| `onPressHandler` | `() => void` \| `undefined` | Handler to be called when the accept call button is pressed. Used to override the default behaviour. | -| `onAcceptCallHandler` | `() => void` \| `undefined` | Handler to be called after the incoming call is accepted. | - -### [`HangupCallButton`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Call/CallControls/HangupCallButton.tsx) - -This component is used to hangup/leave an active call/outgoing call. - -| Type | Type | Description | -| --------------------- | --------------------------- | ----------------------------------------------------------------------------------------------------- | -| `onPressHandler` | `() => void` \| `undefined` | Handler to be called when the hang up call button is pressed. Used to override the default behaviour. | -| `onHangupCallHandler` | `() => void` \| `undefined` | Handler to be called after the call is hanged up. | - -### [`RejectCallButton`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Call/CallControls/RejectCallButton.tsx) - -This component is used in the [Incoming Call](../incoming-call) component to reject an incoming call. - -| Type | Type | Description | -| --------------------- | --------------------------- | ---------------------------------------------------------------------------------------------------- | -| `onPressHandler` | `() => void` \| `undefined` | Handler to be called when the reject call button is pressed. Used to override the default behaviour. | -| `onRejectCallHandler` | `() => void` \| `undefined` | Handler to be called after the incoming call is rejected. | - -### [`ToggleAudioPreviewButton`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Call/CallControls/ToggleAudioPreviewButton.tsx) - -This component is used to toggle audio mute/unmute status before joining the call. - -| Type | Type | Description | -| ---------------- | --------------------------- | ------------------------------------------------------------------------------------------------------ | -| `onPressHandler` | `() => void` \| `undefined` | Handler to be called when the audio preview button is pressed. Used to override the default behaviour. | - -### [`ToggleVideoPreviewButton`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Call/CallControls/ToggleVideoPreviewButton.tsx) - -This component is used to toggle video mute/unmute status before joining the call. - -| Type | Type | Description | -| ---------------- | --------------------------- | ------------------------------------------------------------------------------------------------------ | -| `onPressHandler` | `() => void` \| `undefined` | Handler to be called when the video preview button is pressed. Used to override the default behaviour. | - -### [`ToggleAudioPublishingButton`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Call/CallControls/ToggleAudioPublishingButton.tsx) - -This component is used to toggle audio mute/unmute status while in the call. - -| Type | Type | Description | -| ---------------- | --------------------------- | -------------------------------------------------------------------------- | -| `onPressHandler` | `() => void` \| `undefined` | Handler to override the default behaviour of the audio publishing button.. | - -### [`ToggleVideoPublishingButton`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Call/CallControls/ToggleVideoPublishingButton.tsx) - -This component is used to toggle video mute/unmute status while in the call. - -| Type | Type | Description | -| ---------------- | --------------------------- | ------------------------------------------------------------------------- | -| `onPressHandler` | `() => void` \| `undefined` | Handler to override the default behaviour of the video publishing button. | - -### [`ToggleCameraFaceButton`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Call/CallControls/ToggleCameraFaceButton.tsx) - -This component is used to toggle camera face(front/back) when in the call. - -| Type | Type | Description | -| ---------------- | --------------------------- | --------------------------------------------------------------------------- | -| `onPressHandler` | `() => void` \| `undefined` | Handler to override the default behaviour of the toggle camera face button. | - -### [`ReactionsButton`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Call/CallControls/ReactionsButton.tsx) - -This component is used to display the list of Reactions supported in the call. It can also be used to send reactions. - -The following reactions are supported by default: - -- like 👍 -- raise hand ✋ -- fireworks 🎉 - -| Type | Type | Description | -| ---------------- | --------------------------- | ------------------------------------------------------------------------------- | -| `onPressHandler` | `() => void` \| `undefined` | Handler to override the default behaviour when the reactions button is pressed. | - -### [`ChatButton`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Call/CallControls/ChatButton.tsx) - -This component is used to display/open the chat window while in the call. - -| Type | Type | Description | -| ------------------ | --------------------------- | --------------------------------------------------------------------------------- | -| `onPressHandler` | `() => void` \| `undefined` | Handler to override the default behaviour when the chat button is pressed. | -| `unreadBadgeCount` | `number` \| `undefined` | The count of the current unread message to be displayed above on the Chat button. | - -## Customization - -You can create your own custom component using the [built-in call controls](#built-in-call-controls) as building blocks. - -If you want to create custom call controls, follow the [Call Controls UI Cookbook guide](../../../ui-cookbook/replacing-call-controls/) for more information. diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/call-participants-list.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/call-participants-list.mdx deleted file mode 100644 index 13814198c0..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/call-participants-list.mdx +++ /dev/null @@ -1,118 +0,0 @@ ---- -id: call-participants-list -title: CallParticipantsList ---- - -import ImageShowcase from '@site/src/components/ImageShowcase'; -import CallParticipantsListVertical from '../../assets/04-ui-components/call/call-participants-list/vertical.png'; -import CallParticipantsListHorizontal from '../../assets/04-ui-components/call/call-participants-list/horizontal.png'; -import Landscape from '../../common-content/ui-components/call/call-content/landscape.mdx'; - -import ParticipantLabel from '../../common-content/ui-components/call/call-content/participant-label.mdx'; -import ParticipantReaction from '../../common-content/ui-components/call/call-content/participant-reaction.mdx'; -import ParticipantNetworkQualityIndicator from '../../common-content/ui-components/call/call-content/participant-network-quality-indicator.mdx'; -import ParticipantVideoFallback from '../../common-content/ui-components/call/call-content/participant-video-fallback.mdx'; -import VideoRenderer from '../../common-content/ui-components/call/call-content/video-renderer.mdx'; -import ParticipantView from '../../common-content/ui-components/call/call-content/participant-view.mdx'; - -This component displays a list of participants in a [FlatList](https://reactnative.dev/docs/flatlist). You can use this component to display participants either in a vertical or horizontal scrolling mode. - -:::note -This component depends on a flex container to calculate the width and height of the participant view, hence it should be used only in a flex parent container -::: - -Our [CallContent](./call-content.mdx) component internally uses `CallParticipantsList` inside [`CallParticipantsGrid`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Call/CallLayout/CallParticipantsGrid.tsx) and [`CallParticipantsSpotlight`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Call/CallLayout/CallParticipantsSpotlight.tsx) components. - - - -## General Usage - -The `CallParticipantsList` can be used to display the all the participants in the form of FlatList. By default it shows the participants vertically with 2 participants in each row. This is customizable using the props. - -```tsx -import { - Call, - CallParticipantsList, - StreamCall, - useParticipants, -} from '@stream-io/video-react-native-sdk'; - -const VideoCallUI = () => { - let call: Call; - const allParticipants = useParticipants(); - - return ( - - // highlight-next-line - - - ); -}; -``` - -## Props - -### `participants` - -| Type | -| ----------------------------------------------------------------------------------------------------------------- | -| [`StreamVideoParticipant`](https://github.com/GetStream/stream-video-js/blob/main/packages/client/src/types.ts)[] | - -The list of participants to list in the view. - -### `numberOfColumns` - -| Type | Default Value | -| ----------------------- | ------------- | -| `number` \| `undefined` | 2 | - -The number of participants to be displayed in a single row. This property is only used when there are more than 2 participants. - -### `horizontal` - -| Type | Default Value | -| ------------------------ | ------------- | -| `boolean` \| `undefined` | false | - -This decides whether the participants should be listed vertically or horizontally. - -### `landscape` - - - -### `ParticipantLabel` - - - -### `ParticipantReaction` - - - -### `ParticipantVideoFallback` - - - -### `ParticipantNetworkQualityIndicator` - - - -### `VideoRenderer` - - - -### `ParticipantView` - - diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/call-top-view.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/call-top-view.mdx deleted file mode 100644 index df2a24a996..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/call-top-view.mdx +++ /dev/null @@ -1,64 +0,0 @@ ---- -id: call-top-view -title: CallTopView ---- - -import OnBackPressed from '../../common-content/ui-components/call/call-content/on-back-pressed.mdx'; -import OnParticipantInfoPress from '../../common-content/ui-components/call/call-content/on-participant-info-press.mdx'; - -The `CallTopView` represents the header component that gives the user more information when in a call, while adding a few actions they can trigger while the call is active. The header is useful for showing the call name or title, as well as the state, such as if the user is fully connected to the call or not. -It has a back button by default and a participant info badge. - -![Preview of the CallTopView component](../../assets/04-ui-components/call/call-top-view/call-top-view.png) - -## General Usage - -To use the component, you can simple use it as: - -```tsx {12} -import { - Call, - CallTopView, - StreamCall, -} from '@stream-io/video-react-native-sdk'; - -const VideoCallUI = () => { - let call: Call; - - return ( - - - - ); -}; -``` - -## Props - -### `onBackPressed` - - - -### `onParticipantInfoPress` - - - -### `title` - -Title to be rendered at the center of the Header. - -| Type | -| ----------------------- | -| `string` \| `undefined` | - -### `style` - -Style to override the container of the `CallTopView`. - -| Type | -| ---------------------------------------------------------- | -| [ViewStyle](https://reactnative.dev/docs/view-style-props) | - -## Customization - -You can create your own custom `CallTopView` using the [Call Top View UI Cookbook guide](../../../ui-cookbook/replacing-call-top-view/). diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/incoming-call.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/incoming-call.mdx deleted file mode 100644 index 0bb157e7ad..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/incoming-call.mdx +++ /dev/null @@ -1,84 +0,0 @@ ---- -id: incoming-call -title: IncomingCall ---- - -import CallTopView from '../../common-content/ui-components/call/call-content/call-top-view.mdx'; -import Landscape from '../../common-content/ui-components/call/call-content/landscape.mdx'; - -IncomingCall represents the incoming call state and the UI when a user receives a call from someone else. - -It represents the details of the user calling you, allows accepting or rejecting the call, etc. - -It is customizable using our UI cookbook guide on [Custom Incoming/Outgoing Call components](../../05-ui-cookbook/05-incoming-and-outcoming-call.mdx). - -![Preview of the IncomingCall component.](../../assets/04-ui-components/call/incoming-call/incoming-call.png) - -## General Usage - -```tsx -import { - CallingState, - IncomingCall, - useCall, - useCalls, - useCallStateHooks, -} from '@stream-io/video-react-native-sdk'; - -const CallPanel = () => { - const call = useCall(); - const isCallCreatedByMe = call?.data?.created_by.id === call?.currentUserId; - const { useCallCallingState } = useCallStateHooks(); - - const callingState = useCallCallingState(); - - // Display the incoming call if the call state is RINGING and the call is not created by me, i.e., recieved from others. - if (callingState === CallingState.RINGING && !isCallCreatedByMe) { - return ; - } -}; - -const Call = () => { - const calls = useCalls(); - - return ( - - - - ); -}; -``` - -## Props - -### `landscape` - - - -### `onAcceptCallHandler` - -| Type | -| --------------------------- | -| `() => void` \| `undefined` | - -Handler to be executed when an incoming call is accepted. - -### `onRejectCallHandler` - -| Type | -| --------------------------- | -| `() => void` \| `undefined` | - -Handler to be executed when an incoming call is rejected. - -### `CallTopView` - - - -### `IncomingCallControls` - -Prop to customize the Incoming call controls in the `IncomingCall` component. - -| Type | Default Value | -| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `ComponentType`\| `undefined` | [`IncomingCallControls`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Call/CallControls/IncomingCallControls.tsx) | diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/lobby.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/lobby.mdx deleted file mode 100644 index 4a43883713..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/lobby.mdx +++ /dev/null @@ -1,68 +0,0 @@ ---- -id: lobby -title: Lobby ---- - -import Landscape from '../../common-content/ui-components/call/call-content/landscape.mdx'; - -Lobby is a component that is designed to make your entrance and invite experience to a meeting call user-friendly. The purpose is to allow users to test their setup before entering the meeting call. This component mainly contains all the necessary information about the meeting, including: - -- The call details such as its id, etc. -- Participants who are already in the meeting. -- Controlling the audio/video mute status before joining the meeting. -- Exposes a handler for the join meeting button. - -![Preview of the Lobby component.](../../assets/04-ui-components/call/lobby/lobby.png) - -## General usage - -```tsx {9} -import { useCallback } from 'react'; -import { Lobby, useCall } from '@stream-io/video-react-native-sdk'; - -const LobbyComponent = () => { - const onJoinCallHandler = () => { - // Handle what should happen after the call is joined. Eg: navigation, etc. - }; - - return ; -}; -``` - -## Props - -### `landscape` - - - -### `onJoinCallHandler` - -Handler to be called when the call is joined using the join button in the Lobby. - -| Type | -| --------------------------- | -| `() => void` \| `undefined` | - -### `LobbyControls` - -Prop to customize the media controls in the Lobby component entirely. - -| Type | Default Value | -| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `ComponentType`\| `undefined` | [`LobbyControls`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Call/CallControls/LobbyControls.tsx) | - -### `JoinCallButton` - -Prop to customize the Join call button in the Lobby component. - -| Type | Default Value | -| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | -| `ComponentType`\| `undefined` | [`JoinCallButton`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Call/Lobby/JoinCallButton.tsx) | - -### `LobbyFooter` - -Prop to customize the Lobby Footer in the Lobby component. - -| Type | Default Value | -| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | -| `ComponentType`\| `undefined` | [`LobbyFooter`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Call/Lobby/LobbyFooter.tsx) | diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/outgoing-call.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/outgoing-call.mdx deleted file mode 100644 index 9182070a87..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/outgoing-call.mdx +++ /dev/null @@ -1,92 +0,0 @@ ---- -id: outgoing-call -title: OutgoingCall ---- - -import ImageShowcase from '@site/src/components/ImageShowcase'; -import CameraEnabled from '../../assets/04-ui-components/call/outgoing-call/outgoing-call-camera-enabled.png'; -import CameraDisabled from '../../assets/04-ui-components/call/outgoing-call/outgoing-call-camera-disabled.png'; -import Landscape from '../../common-content/ui-components/call/call-content/landscape.mdx'; -import CallTopView from '../../common-content/ui-components/call/call-content/call-top-view.mdx'; - -OutgoingCall represents the outgoing call state and the UI when a user calls someone else. The view is displayed until someone accepts the call. - -It represents the details of the user who is being called. It also allows controlling the pre-join audio/video mute status of the call etc. - -It is customizable using our UI cookbook guide on [Custom Incoming/Outgoing Call components](../../05-ui-cookbook/05-incoming-and-outcoming-call.mdx). - - - -## General Usage - -```tsx -import { - CallingState, - OutgoingCall, - useCall, - useCalls, - useCallStateHooks, -} from '@stream-io/video-react-native-sdk'; - -const CallPanel = () => { - const call = useCall(); - const isCallCreatedByMe = call?.data?.created_by.id === call?.currentUserId; - const { useCallCallingState } = useCallStateHooks(); - - const callingState = useCallCallingState(); - - // Display the outgoing call if the call state is RINGING and the call is created by me. - if (callingState === CallingState.RINGING && isCallCreatedByMe) { - return ; - } -}; - -const Call = () => { - const calls = useCalls(); - - return ( - - - - ); -}; -``` - -## Props - -### `landscape` - - - -### `onHangupCallHandler` - -| Type | -| --------------------------- | -| `() => void` \| `undefined` | - -Handler to be executed when the outgoing call is cancelled or hanged up. - -### `CallTopView` - - - -### `OutgoingCallControls` - -Prop to customize the Outgoing call controls in the `OutgoingCall` component. - -| Type | Default Value | -| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `ComponentType`\| `undefined` | [`OutgoingCallControls`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Call/CallControls/OutgoingCallControls.tsx) | diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/ringing-call-content.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/ringing-call-content.mdx deleted file mode 100644 index fcb63b1c18..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/call/ringing-call-content.mdx +++ /dev/null @@ -1,113 +0,0 @@ ---- -id: ringing-call-content -title: RingingCallContent ---- - -import ImageShowcase from '@site/src/components/ImageShowcase'; -import IncomingCall from '../../assets/04-ui-components/call/ringing-call-content/incoming-call.png'; -import OutgoingCall from '../../assets/04-ui-components/call/ringing-call-content/outgoing-call.png'; -import JoiningCallIndicator from '../../assets/04-ui-components/call/ringing-call-content/joining-call-indicator.png'; -import CallContent from '../../assets/04-ui-components/call/ringing-call-content/call-content.png'; - -import CallTopView from '../../common-content/ui-components/call/call-content/call-top-view.mdx'; -import Landscape from '../../common-content/ui-components/call/call-content/landscape.mdx'; - -The `RingingCallContent` lets you easily build UI when you're calling or ringing other people in an app. It's used to show more information about the participants you're calling, as well as give you the option to cancel the call before anyone accepts. - -Based on the call's ringing state and a call type, the RingingCallContent provides a list of participants, with their avatars and names, or a background with the avatar of the person you're calling, if it's a 1:1 conversation. - -Let's see how to show the RingingCallContent UI. - - - -## Usage - -To use the `RingingCallContent` you can do the following: - -```tsx {11} -import { - StreamCall, - useCalls, - RingingCallContent, -} from '@stream-io/video-react-native-sdk'; - -const Call = () => { - // filter for ringing calls - const calls = useCalls().filter( - (c) => c.state.callingState === CallingState.RINGING, - ); - const call = calls[0]; - if (!call) return null; - - return ( - - - - ); -}; -``` - -## Props - -### `landscape` - - - -### `IncomingCall` - -Prop to customize the `IncomingCall` component. This component is rendered when an incoming call is received. - -| Type | Default Value | -| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `ComponentType`\| `undefined` | [`IncomingCall`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Call/RingingCallContent/IncomingCall.tsx) | - -### `OutgoingCall` - -Prop to customize the `OutgoingCall` component. This component is rendered when someone is called. - -| Type | Default Value | -| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `ComponentType`\| `undefined` | [`OutgoingCall`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Call/RingingCallContent/OutgoingCall.tsx) | - -### `CallTopView` - - - -### `CallContent` - -Prop to customize the accepted CallContent component in the RingingCallContent. This is shown after the call is accepted. By default it renders the [`CallContent`](../call-content) component. - -| Type | Default Value | -| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | -| `ComponentType`\| `undefined` | [`CallContent`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Call/CallContent/CallContent.tsx) | - -### `JoiningCallIndicator` - -Prop to customize the `JoiningCallIndicator` component in the RingingCallContent. It is shown when the call is accepted and is waiting to be joined. - -| Type | Default Value | -| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `ComponentType`\| `undefined` | [`JoiningCallIndicator`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Call/RingingCallContent/JoiningCallIndicator.tsx) | diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/core/_category_.json b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/core/_category_.json deleted file mode 100644 index 4b7aa812e4..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/core/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Core", - "position": 3 -} diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/core/stream-call.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/core/stream-call.mdx deleted file mode 100644 index 10dae80e33..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/core/stream-call.mdx +++ /dev/null @@ -1,34 +0,0 @@ ---- -id: stream-call -title: StreamCall ---- - -The `` component is a declarative component wrapper around `Call` objects. It utilizes the `StreamCallProvider` to make the [call and its state](../../../core/call-and-participant-state/) available to all child components. - -## General usage - -```tsx {9,11} -import { - StreamCall, -} from '@stream-io/video-react-native-sdk'; - -export const App = () => { - const call = /* ... */; - - return ( - - - - ); -}; -``` - -## Props - -### `call` - -Stream's `Call` instance propagated to the component's children as a part of `StreamCallContext`. Children can access it with `useCall()` hook. - -| Type | -| ------ | -| `Call` | diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/core/stream-video.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/core/stream-video.mdx deleted file mode 100644 index 5f58180a37..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/core/stream-video.mdx +++ /dev/null @@ -1,67 +0,0 @@ ---- -id: stream-video -title: StreamVideo ---- - -The `` provider makes the [client and its state](../../../core/call-and-participant-state) available to all child components and initializes [internationalization](../../../advanced/i18n/) - -## General usage - -```tsx {10,12} -import { - StreamVideo, - StreamVideoClient, -} from '@stream-io/video-react-native-sdk'; - -export const App = () => { - const client = new StreamVideoClient(/* ... */); - - return ( - - - - ); -}; -``` - -## Props - -### `client` - -`StreamVideoClient` instance propagated to the component's children as a part of `StreamVideoContext`. Children can access it with `useStreamVideoClient()` hook. - -| Type | -| ------------------- | -| `StreamVideoClient` | - -### `i18nInstance` - -The `StreamI18n` instance to use, if `undefined` is provided, a new instance will be created. For more information, see our [Internationalization guide](../../../advanced/i18n/). - -| Type | -| --------------------------- | -| `StreamI18n` \| `undefined` | - -### `language` - -The language to translate UI labels. For more information, see our [Internationalization guide](../../../advanced/i18n/). - -| Type | Default | -| ----------------------- | ------- | -| `string` \| `undefined` | en | - -### `translationsOverrides` - -Custom translations that will be merged with the defaults provided by the library. For more information, see our [Internationalization guide](../../../advanced/i18n/). - -| Type | -| -------------------------------- | -| `TranslationsMap` \| `undefined` | - -### `style` - -Prop to apply [styles/theme](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/theme/theme.ts) to all of the inner components. - -| Type | -| ----------------------------------------------------------------------------------------------------------------------------- | -| [`Theme`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/theme/theme.ts) \| `undefined` | diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/livestream/01-host-livestream.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/livestream/01-host-livestream.mdx deleted file mode 100644 index 305e5dd318..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/livestream/01-host-livestream.mdx +++ /dev/null @@ -1,125 +0,0 @@ ---- -id: host-livestream -title: HostLivestream ---- - -import ImageShowcase from '@site/src/components/ImageShowcase'; -import HostLivestreamStart from '../../assets/04-ui-components/livestream/host-livestream-start.png'; -import HostLivestreamEnd from '../../assets/04-ui-components/livestream/host-livestream-end.png'; - -import HostLivestreamTopView from '../../common-content/ui-components/livestream/host-livestream/host-livestream-top-view.mdx'; -import LivestreamLayout from '../../common-content/ui-components/livestream/livestream-layout.mdx'; -import HostLivestreamControls from '../../common-content/ui-components/livestream/host-livestream/host-livestream-controls.mdx'; -import DurationBadge from '../../common-content/ui-components/livestream/duration-badge.mdx'; -import FollowerCount from '../../common-content/ui-components/livestream/follower-count.mdx'; -import LiveIndicator from '../../common-content/ui-components/livestream/live-indicator.mdx'; -import HostStartStreamButton from '../../common-content/ui-components/livestream/host-livestream/host-start-stream-button.mdx'; -import LiveStreamMediaControls from '../../common-content/ui-components/livestream/livestream-media-controls.mdx'; -import OnStartStreamHandler from '../../common-content/ui-components/livestream/host-livestream/on-start-stream-handler.mdx'; -import OnEndStreamHandler from '../../common-content/ui-components/livestream/host-livestream/on-end-stream-handler.mdx'; - -The `HostLivestream` is the UI component that allows you to show the livestream view in the app on the host's end with minimal efforts. So you don't need to take care much about each feature that you need to build a video livestream call screen with this component. - -`HostLivestream` offers following features: - -- A fully fledged livestream user interface, complete with the capability to host and stream video content. -- Real-time display of the livestream's duration, along with a live indicator. -- An indication of whether the stream is currently live, and a count of the number of participants who are actively watching the stream. -- Buttons to start and end the livestream and media control buttons to pause/resume audio/video streams. - - - -## General usage - -Let's see how to show the `HostLivestream` UI: - -```tsx -import { - Call, - StreamCall, - HostLivestream, -} from '@stream-io/video-react-native-sdk'; - -const VideoCallUI = () => { - let call: Call; - // your logic to create a new call or get an existing call - - return ( - - // highlight-next-line - - - ); -}; -``` - -## Props - -### `hls` - -Enable HTTP livestreaming - -| Type | Default | -| --------- | ------- | -| `boolean` | false | - -### `HostLivestreamTopView` - - - -### `LivestreamLayout` - - - -### `HostLivestreamControls` - - - -### `DurationBadge` - - - -### `FollowerCount` - - - -### `LiveIndicator` - - - -### `HostStartStreamButton` - - - -### `LivestreamMediaControls` - - - -### `onStartStreamHandler` - - - -### `onEndStreamHandler` - - - -### `ScreenShareOverlay` - -Component to customize the screen share overlay, when the screen is shared by a user. - -| Type | Default Value | -| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `ComponentType`\| `undefined` | [`ScreenShareOverlay`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/utility/ScreenShareOverlay.tsx) | diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/livestream/02-viewer-livestream.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/livestream/02-viewer-livestream.mdx deleted file mode 100644 index 25ccc8096e..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/livestream/02-viewer-livestream.mdx +++ /dev/null @@ -1,102 +0,0 @@ ---- -id: viewer-livestream -title: ViewerLivestream ---- - -import ImageShowcase from '@site/src/components/ImageShowcase'; -import ViewerLivestream from '../../assets/04-ui-components/livestream/viewer-livestream.png'; -import ViewerLivestreamScreenShare from '../../assets/04-ui-components/livestream/viewer-livestream-screenshare.png'; - -import ViewerLivestreamTopView from '../../common-content/ui-components/livestream/viewer-livestream/viewer-livestream-top-view.mdx'; -import LivestreamLayout from '../../common-content/ui-components/livestream/livestream-layout.mdx'; -import ViewerLivestreamControls from '../../common-content/ui-components/livestream/viewer-livestream/viewer-livestream-controls.mdx'; -import DurationBadge from '../../common-content/ui-components/livestream/duration-badge.mdx'; -import FollowerCount from '../../common-content/ui-components/livestream/follower-count.mdx'; -import LiveIndicator from '../../common-content/ui-components/livestream/live-indicator.mdx'; -import FloatingParticipantView from '../../common-content/ui-components/call/call-content/floating-participant-view.mdx'; -import ViewerLeaveStreamButton from '../../common-content/ui-components/livestream/viewer-livestream/viewer-leave-stream-button.mdx'; -import OnLeaveStreamHandler from '../../common-content/ui-components/livestream/viewer-livestream/on-leave-stream-handler.mdx'; - -The `ViewerLivestream` is the UI component that allows you to show the livestream view in the app with minimal efforts. - -`ViewerLivestream` offers following features: - -- A fully fledged livestream user interface, complete with the capability to view the stream video content. -- Real-time display of the livestream's duration, along with a live indicator. -- An indication of whether the stream is currently live, and a count of the number of participants who are actively watching the stream. - - - -## General usage - -Let's see how to show the `ViewerLivestream` UI: - -```tsx {13} -import { - Call, - StreamCall, - ViewerLivestream, -} from '@stream-io/video-react-native-sdk'; - -const VideoCallUI = () => { - let call: Call; - // your logic to create a new call or get an existing call - - return ( - - - - ); -}; -``` - -## Props - -### `ViewerLivestreamTopView` - - - -### `LivestreamLayout` - - - -### `ViewerLivestreamControls` - - - -### `DurationBadge` - - - -### `FollowerCount` - - - -### `LiveIndicator` - - - -### `ViewerLeaveStreamButton` - - - -### `FloatingParticipantView` - - - -### `onLeaveStreamHandler` - - diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/livestream/03-livestream-player.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/livestream/03-livestream-player.mdx deleted file mode 100644 index 450f6fb95e..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/livestream/03-livestream-player.mdx +++ /dev/null @@ -1,48 +0,0 @@ ---- -id: livestream-player -title: LivestreamPlayer ---- - -import ImageShowcase from '@site/src/components/ImageShowcase'; -import ViewerLivestream from '../../assets/04-ui-components/livestream/viewer-livestream.png'; - -The `LivestreamPlayer` is a UI component that plays a WebRTC livestream given the call ID and the call type. Under the hood, it uses the [`ViewerLivestream`](./02-viewer-livestream.mdx) component. - -![Preview of the LivestreamPlayer component.](../../assets/04-ui-components/livestream/viewer-livestream.png) - -## General usage - -Let's see how to show the `LivestreamPlayer` UI: - -```tsx -import { - StreamVideo, - LivestreamPlayer, -} from '@stream-io/video-react-native-sdk'; - -const LivestreamScreen() { - return ( - - - - ); -} -``` - -## Props - -### `callType` - -The call type. Usually `livestream`. - -### `callId` - -The call ID. - -### `ViewerLivestream` - -Component to override the ViewerLivestream component that is used under the hood. - -| Type | Default Value | -| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `ComponentType`\| `undefined` | [`ViewerLivestream`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Livestream/ViewerLivestream/ViewerLivestream.tsx) | diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/livestream/_category_.json b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/livestream/_category_.json deleted file mode 100644 index 9e59f1b262..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/livestream/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Livestream", - "position": 3 -} diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/participants/_category_.json b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/participants/_category_.json deleted file mode 100644 index 795fd5a5c9..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/participants/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Participants", - "position": 5 -} diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/participants/floating-participant-view.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/participants/floating-participant-view.mdx deleted file mode 100644 index 81082f4139..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/participants/floating-participant-view.mdx +++ /dev/null @@ -1,124 +0,0 @@ ---- -id: floating-participant-view -title: FloatingParticipantView ---- - -import ImageShowcase from '@site/src/components/ImageShowcase'; -import CameraEnabled from '../../assets/04-ui-components/participants/floating-participant-view/enabled.png'; -import CameraDisabled from '../../assets/04-ui-components/participants/floating-participant-view/disabled.png'; -import ParticipantLabel from '../../common-content/ui-components/call/call-content/participant-label.mdx'; -import ParticipantReaction from '../../common-content/ui-components/call/call-content/participant-reaction.mdx'; -import ParticipantNetworkQualityIndicator from '../../common-content/ui-components/call/call-content/participant-network-quality-indicator.mdx'; -import ParticipantVideoFallback from '../../common-content/ui-components/call/call-content/participant-video-fallback.mdx'; -import VideoRenderer from '../../common-content/ui-components/call/call-content/video-renderer.mdx'; -import ParticipantView from '../../common-content/ui-components/call/call-content/participant-view.mdx'; - -FloatingParticipantView displays the local video stream of the local participant. It is a floating view that can be dragged in the call area. - - - -When the video is muted, the video muted icon is shown in a disabled background. - -## General Usage - -In order to use the `FloatingParticipantView` as a standalone component, you should use the following code: - -```tsx {4} -import { - FloatingParticipantView, - useCallStateHooks, -} from '@stream-io/video-react-native-sdk'; - -const App = () => { - const { useLocalParticipant } = useCallStateHooks(); - const localParticipant = useLocalParticipant(); - return ; -}; -``` - -## Props - -### `alignment` - -Determines where the floating participant video will be placed initially. - -| Type | Default value | -| ------------------------------------------------------- | ------------- | -| `top-left` \|`top-right`\|`bottom-left`\|`bottom-right` | `top-right` | - -### `participant` - -The participant to be rendered in the `FloatingParticipantView`. - -| Type | -| ---------------------------------------------------------------------------------------------------------------------------- | -| [`StreamVideoParticipant`](https://github.com/GetStream/stream-video-js/blob/main/packages/client/src/types.ts)\|`undefined` | - -### `onPressHandler` - -Handler used to handle actions on click of the participant view in FloatingParticipantView. - -| Type | -| --------------------------- | -| `() => void` \| `undefined` | - -### `style` - -This prop is used to override the root container style of the component. - -| Type | -| ---------------------------------------------------------- | -| [ViewStyle](https://reactnative.dev/docs/view-style-props) | - -### `videoZOrder` - -The `zOrder` for the video that will be displayed. - -| Type | Default Value | -| -------- | ------------- | -| `number` | `0` | - -### `objectFit` - -Represents how the video view fits within the parent view. - -| Type | Default Value | -| -------------------------------------- | ------------- | -| `'contain'` \| `'cover'` \|`undefined` | `cover` | - -### `ParticipantLabel` - - - -### `ParticipantReaction` - - - -### `ParticipantNetworkQualityIndicator` - - - -### `ParticipantVideoFallback` - - - -### `ParticipantView` - - - -### `VideoRenderer` - - diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/participants/participant-view.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/participants/participant-view.mdx deleted file mode 100644 index 6611a2da4d..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/participants/participant-view.mdx +++ /dev/null @@ -1,143 +0,0 @@ ---- -id: participant-view -title: ParticipantView ---- - -import ImageShowcase from '@site/src/components/ImageShowcase'; -import ParticipantCameraOn from '../../assets/04-ui-components/participants/participant-view/participant-camera-on.png'; -import ParticipantCameraOff from '../../assets/04-ui-components/participants/participant-view/participant-camera-off.png'; - -import ParticipantLabel from '../../common-content/ui-components/call/call-content/participant-label.mdx'; -import ParticipantReaction from '../../common-content/ui-components/call/call-content/participant-reaction.mdx'; -import ParticipantNetworkQualityIndicator from '../../common-content/ui-components/call/call-content/participant-network-quality-indicator.mdx'; -import ParticipantVideoFallback from '../../common-content/ui-components/call/call-content/participant-video-fallback.mdx'; -import VideoRenderer from '../../common-content/ui-components/call/call-content/video-renderer.mdx'; - -The `ParticipantView` component renders a participant's video and displays related information of the participant. It can also be used to display the screen sharing view. - -It displays the participant info such as - -- Name -- Audio/video mute/unmute state -- Reaction -- Network quality indicator - -It can toggle between video and avatar based on the participant's video state. It also provides action buttons (for example, to unpin the participant). - -:::note -It is used as a building block to render individual item of participants in [CallContent](../call/call-content.mdx) and [CallParticipantsList](../call/call-participants-list.mdx). -::: - - - -## General Usage - -In order to use the `ParticipantView` as a standalone component, you should use the following code: - -```tsx -import { - ParticipantView, - useCallStateHooks, -} from '@stream-io/video-react-native-sdk'; - -const App = () => { - const { useParticipants } = useCallStateHooks(); - const participants = useParticipants(); - - // Here to show the demo, we pass only first participant. You can pass any of the participant. - return ; -}; -``` - -## Props - -### `participant` - -The participant to be rendered in the `ParticipantView`. - -| Type | -| --------------------------------------------------------------------------------------------------------------- | -| [`StreamVideoParticipant`](https://github.com/GetStream/stream-video-js/blob/main/packages/client/src/types.ts) | - -### `trackType` - -The type of the participant stream to be rendered. Eg: screen sharing or participant's video stream. - -| Type | Default Value | -| ---------------------------------- | ------------- | -| `videoTrack` \| `screenShareTrack` | `videoTrack` | - -### `style` - -This prop is used to override the root container style of the component. - -| Type | -| ---------------------------------------------------------- | -| [ViewStyle](https://reactnative.dev/docs/view-style-props) | - -### `isVisible` - -When set to false, the video stream will not be shown even if it is available. - -| Type | Default Value | -| --------- | ------------- | -| `boolean` | `true` | - -### `videoZOrder` - -The `zOrder` for the video that will be displayed. - -| Type | Default Value | -| -------- | ------------- | -| `number` | `0` | - -### `objectFit` - -Represents how the video view fits within the parent view. - -| Type | Default Value | -| -------------------------------------- | ------------- | -| `'contain'` \| `'cover'` \|`undefined` | `cover` | - -### `ParticipantLabel` - - - -### `ParticipantReaction` - - - -### `ParticipantVideoFallback` - - - -### `ParticipantNetworkQualityIndicator` - - - -### `VideoRenderer` - - - -## Customization - -Our [UI cookbook](../../../ui-cookbook/overview) tells all the important information about the component customizations. - -- [Video fallback](../../../ui-cookbook/video-fallback) -- [Network Quality Indicator](../../../ui-cookbook/network-quality-indicator) -- [Custom Label](../../../ui-cookbook/participant-label) -- [Video Renderer](../../../ui-cookbook/video-renderer) -- [Reactions](../../../ui-cookbook/reactions) diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/utility/_category_.json b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/utility/_category_.json deleted file mode 100644 index b9201f46ed..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/utility/_category_.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "label": "Utility", - "position": 6 -} diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/utility/avatar.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/utility/avatar.mdx deleted file mode 100644 index 190ba39d0c..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/04-ui-components/utility/avatar.mdx +++ /dev/null @@ -1,47 +0,0 @@ ---- -id: avatar -title: Avatar ---- - -![Preview of the Avatar component.](../../assets/04-ui-components/avatar.png) - -The `Avatar` component is generally used to show the image and/or name of participants of a video call. If no image is provided it will display the name of the user. - -Of course, it can also be used in any other circumstances that you like, for example, in user lists, profiles, and much more. - -## General usage - -The way you can use the `Avatar` element in a react component is like this: - -```tsx {5} -import { Avatar, useLocalParticipant } from '@stream-io/video-react-native-sdk'; - -export function AvatarExample() { - const localParticipant = useLocalParticipant(); - return ; -} -``` - -## Props - -### `participant` - -| Type | -| ------------------------ | -| `StreamVideoParticipant` | - -The `StreamVideoParticipant` object. - -### `size` - -| Type | Default Value | -| ----------------------- | ------------- | -| `number` \| `undefined` | 100 | - -The size of the avatar. The width and height is set to this number. - -### `style` - -| Type | -| ---------------------------------------------------------- | -| `{container:ViewStyle; image:ImageStyle; text: TextStyle}` | diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/01-overview.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/01-overview.mdx deleted file mode 100644 index 67f9bb6ce9..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/01-overview.mdx +++ /dev/null @@ -1,99 +0,0 @@ ---- -title: Overview -description: Overview of the UI cookbook ---- - -export const CookbookCard = ({ title, link, img }) => ( -
-

{title}

- -

- {title} -

-
-
-); - -import CallControls from '../assets/05-ui-cookbook/02-replacing-call-controls/call-controls.png'; -import CallTopView from '../assets/05-ui-cookbook/04-replacing-call-top-view/call-top-view-custom.png'; -import ParticipantLabel from '../assets/05-ui-cookbook/03-participant-label/participant-label-custom.png'; -import IncomingCall from '../assets/05-ui-cookbook/05-incoming-and-outgoing-call/incoming-call.png'; -import OutgoingCall from '../assets/05-ui-cookbook/05-incoming-and-outgoing-call/outgoing-call.png'; -import Lobby from '../assets/05-ui-cookbook/06-lobby-preview/lobby.png'; -import VideoFallback from '../assets/05-ui-cookbook/07-video-fallback/video-fallback-custom.png'; -import VideoRenderer from '../assets/05-ui-cookbook/08-video-renderer/video-renderer-custom.png'; -import Reaction from '../assets/05-ui-cookbook/09-reactions/reaction-custom.png'; -import NetworkQualityIndicator from '../assets/05-ui-cookbook/10-network-quality-indicator/network-quality-indicator-custom.png'; -import RuntimeLayoutSwitching from '../assets/05-ui-cookbook/11-runtime-layout-switching/runtime-layout-switching-modal.png'; - -Stream Video React Native UI components are highly customizable and allow you to fully customize/remake the components with ease according to your design requirements. - -This UI Cookbook will walk you through how to customize each component in your video call: - -
- - - - - - - - - - - -
diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/02-replacing-call-controls.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/02-replacing-call-controls.mdx deleted file mode 100644 index e8bf0e848d..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/02-replacing-call-controls.mdx +++ /dev/null @@ -1,396 +0,0 @@ ---- -title: Call Controls -description: A guide on how to add/remove or replace call controls ---- - -import ImageShowcase from '@site/src/components/ImageShowcase'; -import CallControlsMediaButtonOn from '../assets/05-ui-cookbook/02-replacing-call-controls/call-controls-button-media-on.png'; -import CallControlsMediaButtonOff from '../assets/05-ui-cookbook/02-replacing-call-controls/call-controls-button-media-off.png'; - -The Stream Video React Native SDK allows building your own Call controls view/layout. Call Controls View generally comprises several buttons that control the call. Each button controls its area of responsibility. Our task, as integrators, is to create a component that puts these buttons together as we wish. In this example, we intend to show how to do just that. - -:::note -The React Native SDK exports a lot of [Built-in Call Controls](../../ui-components/call/call-controls/#built-in-call-controls) components that we encourage to use if you don't want to entirely override the functionality. -::: - -## Building Custom Controls Buttons - -It is very easy to build custom buttons making use of the hooks provided by the SDK. In the next few sections, we will demonstrate how custom call controls buttons can be built. - -:::note -Implementing call controls buttons will often be in reality associated with handling permissions to perform the given action. To learn about permission handling, take a look at our [permissions and moderation guide](../../core/permissions-and-moderation). -::: - -### Button to accept a call - -We will need a call accept button when building an app that makes use of [ring call workflow](../../core/joining-and-creating-calls/#ring-call). To accept a call we just invoke `call.join()`. So the minimal call accept button could look like this: - -```tsx -import { Pressable, Text, StyleSheet } from 'react-native'; -import { useCall } from '@stream-io/video-react-native-sdk'; - -export const CustomAcceptCallButton = () => { - const call = useCall(); - - const onCallAcceptHandler = async () => { - await call?.join(); - }; - - return ( - - Accept Call - - ); -}; - -const styles = StyleSheet.create({ - button: { - height: 80, - width: 80, - borderRadius: 40, - justifyContent: 'center', - }, - buttonText: { - textAlign: 'center', - }, - acceptButton: { - backgroundColor: '#20E070', - }, -}); -``` - -### Button to hangup a call - -We will need a call hangup button when hanging up a call or to leave the ongoing call. To accept a call we just invoke `call.leave()`. So the minimal call hang up button could look like this: - -```tsx -import { Pressable, Text, StyleSheet } from 'react-native'; -import { useCall } from '@stream-io/video-react-native-sdk'; - -export const CustomHangupCallButton = () => { - const call = useCall(); - - const onCallHangupHandler = async () => { - await call?.leave(); - }; - - return ( - - Hangup Call - - ); -}; - -const styles = StyleSheet.create({ - button: { - height: 80, - width: 80, - borderRadius: 40, - justifyContent: 'center', - }, - buttonText: { - textAlign: 'center', - }, - hangupButton: { - backgroundColor: '#FF3742', - }, -}); -``` - -### Button to reject a call - -We will need a call reject button when building an app that makes use of [ring call workflow](../../core/joining-and-creating-calls/#ring-call). To reject a call we just invoke `call.leave()` function with an object parameter having key as `reject` and value of it being `true`. So the minimal call reject button could look like this: - -```tsx -import { Pressable, Text, StyleSheet } from 'react-native'; -import { useCall } from '@stream-io/video-react-native-sdk'; - -export const CustomRejectCallButton = () => { - const call = useCall(); - - const onCallRejectHandler = async () => { - await call?.leave({ reject: true }); - }; - - return ( - - Reject Call - - ); -}; - -const styles = StyleSheet.create({ - button: { - height: 80, - width: 80, - borderRadius: 40, - justifyContent: 'center', - }, - buttonText: { - textAlign: 'center', - }, - rejectButton: { - backgroundColor: '#FF3742', - }, -}); -``` - -### Button to toggle audio - -Toggling microphone in an active call turns around publishing audio input streams and enabling the audio state. The bare-bones button to toggle audio in an active call could look like the following: - -```tsx -import { useCall, useCallStateHooks } from '@stream-io/video-react-native-sdk'; - -export const ToggleAudioButton = () => { - const call = useCall(); - const { useMicrophoneState } = useCallStateHooks(); - const { status } = useMicrophoneState(); - - const toggleAudioMuted = async () => { - await call?.microphone.toggle(); - }; - - const audioButtonStyles = [ - styles.button, - { - backgroundColor: status === 'disabled' ? '#080707dd' : 'white', - }, - ]; - - const audioButtonTextStyles = [ - styles.mediaButtonText, - { - color: status === 'disabled' ? 'white' : '#080707dd', - }, - ]; - - return ( - - {status === 'disabled' ? ( - Audio On - ) : ( - Audio Off - )} - - ); -}; - -const styles = StyleSheet.create({ - button: { - height: 80, - width: 80, - borderRadius: 40, - justifyContent: 'center', - }, - buttonText: { - textAlign: 'center', - }, - mediaButtonText: { - textAlign: 'center', - }, -}); -``` - -### Button to toggle video - -Toggling video in an active call turns around publishing video input streams and enabling the video state. The bare-bones button to toggle video in an active call could look like the following: - -```tsx -import { useCall, useCallStateHooks } from '@stream-io/video-react-native-sdk'; - -export const ToggleVideoButton = () => { - const call = useCall(); - const { useCameraState } = useCallStateHooks(); - const { status } = useCameraState(); - - const toggleVideoMuted = async () => { - await call?.camera.toggle(); - }; - - const videoButtonStyles = [ - styles.button, - { - backgroundColor: status === 'disabled' ? '#080707dd' : 'white', - }, - ]; - - const videoButtonTextStyles = [ - styles.mediaButtonText, - { - color: status === 'disabled' ? 'white' : '#080707dd', - }, - ]; - - return ( - - {status === 'disabled' ? ( - Video On - ) : ( - Video Off - )} - - ); -}; - -const styles = StyleSheet.create({ - button: { - height: 80, - width: 80, - borderRadius: 40, - justifyContent: 'center', - }, - buttonText: { - textAlign: 'center', - }, - mediaButtonText: { - textAlign: 'center', - }, -}); -``` - -### Button to toggle Camera Face - -Toggling camera face for mobile devices is an important feature. You can do it using `call.camera.flip()` function. - -```tsx -import { useCall, useCallStateHooks } from '@stream-io/video-react-native-sdk'; - -export const ToggleCameraFaceButton = () => { - const call = useCall(); - const { useCameraState } = useCallStateHooks(); - const { direction } = useCameraState(); - - const toggleCameraFacingMode = async () => { - onPressHandler?.(); - await call?.camera.flip(); - }; - - const videoFaceButtonStyles = [ - styles.button, - { - backgroundColor: direction === 'back' ? '#080707dd' : 'white', - }, - ]; - - const videoFaceButtonTextStyles = [ - styles.mediaButtonText, - { - color: direction === 'back' ? 'white' : '#080707dd', - }, - ]; - - return ( - - {direction === 'front' ? ( - Back - ) : ( - Front - )} - - ); -}; - -const styles = StyleSheet.create({ - button: { - height: 80, - width: 80, - borderRadius: 40, - justifyContent: 'center', - }, - buttonText: { - textAlign: 'center', - }, - mediaButtonText: { - textAlign: 'center', - }, -}); -``` - -### Assembling it all together - -![Preview of Call Buttons Call Controls](../assets/05-ui-cookbook/02-replacing-call-controls/call-controls-button.png) - -#### Call Buttons - -```tsx -import { View, StyleSheet } from 'react-native'; -import { CallContent } from '@stream-io/video-react-native-sdk'; - -export const CustomCallControls = () => { - return ( - - - - - - ); -}; - -export const App = () => { - return ( - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, - buttonGroup: { - flexDirection: 'row', - justifyContent: 'space-evenly', - paddingVertical: 10, - }, -}); -``` - -#### Media Button - - - -```tsx -import { View, StyleSheet } from 'react-native'; - -export const CustomCallControls = () => { - return ( - - - - - - ); -}; - -const styles = StyleSheet.create({ - buttonGroup: { - flexDirection: 'row', - justifyContent: 'space-evenly', - paddingVertical: 10, - }, -}); -``` diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/03-participant-label.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/03-participant-label.mdx deleted file mode 100644 index a1a8eaa569..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/03-participant-label.mdx +++ /dev/null @@ -1,72 +0,0 @@ ---- -title: Participant Label -description: How to make your own custom participant label ---- - -Stream's UI components provide a participant label, which displays participant's basic information, such as name, and the status of the microphone by default like the image below: - -![Preview of the Default Participant Label component](../assets/05-ui-cookbook/03-participant-label/participant-label-default.png) - -It is expected that the default component may not meet all the requirements of your design/app. Therefore, we will look into ways, how to customize/create a participant label in this tutorial. - -## Custom Participant Label - -You can customize the participant label by implementing your own label component and passing it to the [`CallContent`](../../ui-components/call/call-content) component. - -![Preview of the Custom Participant Label component](../assets/05-ui-cookbook/03-participant-label/participant-label-custom.png) - -```tsx -import { Text, StyleSheet } from 'react-native'; -import { - StreamVideoParticipant, - ParticipantLabelProps, -} from '@stream-io/video-react-native-sdk'; - -// A custom ParticipantLabel component that shows participant's name and if its a dominant speaker -const CustomParticipantLabel = ({ participant }: ParticipantLabelProps) => { - const participantLabel = participant?.name ?? participant?.id; - - return {participantLabel}; -}; - -const styles = StyleSheet.create({ - label: { - backgroundColor: 'gray', - padding: 10, - borderRadius: 10, - color: 'white', - }, -}); -``` - -## Final Steps - -Now this can be passed to the [`ParticipantLabel`](../../ui-components/call/call-content/#participantlabel) prop of the [`CallContent`](../../ui-components/call/call-content) component, as follows: - -```tsx {13} -import { - Call, - CallContent, - StreamCall, -} from '@stream-io/video-react-native-sdk'; - -const VideoCallUI = () => { - let call: Call; - // your logic to create a new call or get an existing call - - return ( - - - - ); -}; -``` - -:::note -To get the participant data, you can use the following hooks from the `useCallStateHooks`: - -- `useParticipants` hook that provides all the necessary details of all the participants. -- `useRemoteParticipants` hook that provides all the details of the participants other than the local participant. -- `useConnectedUser` or `useLocalParticipant` provides the details of the local or connected participant. - -::: diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/04-replacing-call-top-view.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/04-replacing-call-top-view.mdx deleted file mode 100644 index 030b6fb338..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/04-replacing-call-top-view.mdx +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: Call Top View -description: A guide on how to create Call Top View ---- - -The `CallTopView` is the header component that gives the user more information when in a call, while adding a few actions they can trigger while the call is active. - -![Preview of the default CallTopView component](../assets/05-ui-cookbook/04-replacing-call-top-view/call-top-view-default.png) - -You are free to customize the `CallTopView` as you want to add new buttons or change the background as per your design requirements. - -## Custom Call Top View - -We will create a custom call top view which will only display the id of the call. This can then be passed to the [`CallContent`](../../ui-components/call/call-content) component. - -![Preview of the custom CallTopView component](../assets/05-ui-cookbook/04-replacing-call-top-view/call-top-view-custom.png) - -```tsx -import { StyleSheet, Text, View } from 'react-native'; - -const CustomCallTopView = () => { - const call = useCall(); - return ( - - {call?.id} - - ); -}; - -const styles = StyleSheet.create({ - topView: { - width: '100%', - backgroundColor: 'gray', - }, - title: { - paddingVertical: 20, - color: 'white', - textAlign: 'center', - }, -}); -``` - -## Final steps - -Now this can be passed to the [`CallTopView`](../../ui-components/call/call-content/#calltopview) prop of the [`CallContent`](../../ui-components/call/call-content) component, as follows: - -```tsx {13} -import { - Call, - CallContent, - StreamCall, -} from '@stream-io/video-react-native-sdk'; - -const VideoCallUI = () => { - let call: Call; - // your logic to create a new call or get an existing call - - return ( - - - - ); -}; -``` diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/05-incoming-and-outcoming-call.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/05-incoming-and-outcoming-call.mdx deleted file mode 100644 index ba7e96324f..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/05-incoming-and-outcoming-call.mdx +++ /dev/null @@ -1,421 +0,0 @@ ---- -id: incoming-and-outgoing-call -title: Incoming & Outgoing Call Component ---- - -import ImageShowcase from '@site/src/components/ImageShowcase'; -import MediaStreamManagementButtonOn from '../assets/05-ui-cookbook/05-incoming-and-outgoing-call/media-stream-management-button-on.png'; -import MediaStreamManagementButtonOff from '../assets/05-ui-cookbook/05-incoming-and-outgoing-call/media-stream-management-button-off.png'; -import IncomingCall from '../assets/05-ui-cookbook/05-incoming-and-outgoing-call/incoming-call.png'; -import OutgoingCall from '../assets/05-ui-cookbook/05-incoming-and-outgoing-call/outgoing-call.png'; -import IncomingCallCompleted from '../assets/05-ui-cookbook/05-incoming-and-outgoing-call/incoming-call-completed.png'; -import OutgoingCallCompleted from '../assets/05-ui-cookbook/05-incoming-and-outgoing-call/outgoing-call-completed.png'; - -The incoming and outgoing call components are responsible for showing the call preview before the call is finally joined. - -Incoming call is shown to participant who is being called, while the Outgoing call is shown to participant who is calling someone. - -While showing an Incoming call component to the callee, the following UI elements ideally be present, but not necessarily the same: - -- User info of the participant calling them. -- Accept and reject call button. - -Similarly, the Outgoing call component should show the following UI elements: - -- User info of the participants being called. -- Hang up call button. -- Optional: Audio/Video mute/unmute status button. - - - -:::note -The approach to visualise the components will differ from application to application. Therefore, in this guide, we will focus only on the principles of building components and plugging them with right data sources. -::: - -### User Info - -We can show the user info such as image, name, or additional details of the user within the Incoming/Outgoing call component. To do so we can use the `useCallMembers` hook to get the list of members for whom the call was initiated. - -![Preview of the User Info Example](../assets/05-ui-cookbook/05-incoming-and-outgoing-call/user-info.png) - -:::note -`useCallMembers` also includes the member info of the connected user. So, in most cases that needs to be extracted before showing it in the preview. -::: - -```tsx title="UserInfoComponent.tsx" -import React from 'react'; -import { - useCallStateHooks, - useConnectedUser, - UserResponse, -} from '@stream-io/video-react-native-sdk'; -import { Image, StyleSheet, Text, View } from 'react-native'; - -export const UserInfoComponent = () => { - const connectedUser = useConnectedUser(); - const { useCallMembers } = useCallStateHooks(); - const members = useCallMembers(); - - const membersToShow: UserResponse[] = (members || []) - .map(({ user }) => user) - .filter((user) => user.id !== connectedUser?.id); - - return ( - - - {membersToShow.map((memberToShow) => { - return ( - - - {memberToShow.name} - - ); - })} - - - ); -}; - -const styles = StyleSheet.create({ - userInfo: { - flexDirection: 'row', - justifyContent: 'space-evenly', - }, - title: { - fontSize: 20, - color: 'white', - marginVertical: 20, - textAlign: 'center', - }, - avatar: { - height: 100, - width: 100, - borderRadius: 50, - }, -}); -``` - -### Media Stream Management - -To control audio or video mute status in the Lobby, you can use the `useCameraState` and `useMicrophoneState` hooks from the `useCallStateHooks`, that orchestrates the local state of the device within the SDK and handles streaming of the media effectively. - - - -We can go through the usage through the following example: - -```tsx title="MediaStreamButtonGroup.tsx" -import React from 'react'; -import { Pressable, View, Text, StyleSheet } from 'react-native'; -import { useCallStateHooks } from '@stream-io/video-react-native-sdk'; - -export const MediaStreamButtonGroup = () => { - const { useMicrophoneState, useCameraState } = useCallStateHooks(); - const { isMute: microphoneMuted } = useMicrophoneState(); - const { isMute: cameraMuted } = useCameraState(); - - const audioButtonStyles = [ - styles.button, - { - backgroundColor: microphoneStatus === 'disabled' ? '#080707dd' : 'white', - }, - ]; - - const videoButtonStyles = [ - styles.button, - { - backgroundColor: cameraStatus === 'disabled' ? '#080707dd' : 'white', - }, - ]; - - const audioButtonTextStyles = [ - styles.mediaButtonText, - { - color: microphoneStatus === 'disabled' ? 'white' : '#080707dd', - }, - ]; - - const videoButtonTextStyles = [ - styles.mediaButtonText, - { - color: cameraStatus === 'disabled' ? 'white' : '#080707dd', - }, - ]; - - const toggleAudioMuted = async () => { - await call?.microphone.toggle(); - }; - - const toggleVideoMuted = async () => { - await call?.camera.toggle(); - }; - - return ( - - - {!microphoneMuted ? ( - Audio on - ) : ( - Audio off - )} - - - {!cameraMuted ? ( - Video on - ) : ( - Video off - )} - - - ); -}; - -const styles = StyleSheet.create({ - buttonGroup: { - flexDirection: 'row', - justifyContent: 'space-evenly', - }, - button: { - height: 80, - width: 80, - borderRadius: 40, - justifyContent: 'center', - }, - mediaButtonText: { - textAlign: 'center', - }, -}); -``` - -## Incoming Call Component - -### Accept and Reject Call Button - -We already talked about [Accepting](../../core/joining-and-creating-calls/#accepting-a-call) and [Rejecting](../../core/joining-and-creating-calls/#rejecting-a-call) calls on our Core Concepts [Joining & Creating Calls](../../core/joining-and-creating-calls) guide. - -To build the buttons we would primarily need `useCall` hook, that gives us the call object that has all the necessary functions to accept or reject the call. - -![Preview of the Accept and Reject Call Button of Incoming Call](../assets/05-ui-cookbook/05-incoming-and-outgoing-call/accept-reject-call-button.png) - -```tsx title="IncomingCallButtonGroup.tsx" -import React, { useCallback } from 'react'; -import { Pressable, Text, View, StyleSheet } from 'react-native'; -import { - CallingState, - useCall, - useCallStateHooks, -} from '@stream-io/video-react-native-sdk'; - -export const IncomingCallButtonGroup = () => { - const call = useCall(); - const { useCallCallingState } = useCallStateHooks(); - const callingState = useCallCallingState(); - - const acceptCallHandler = useCallback(async () => { - try { - await call?.join(); - } catch (error) { - console.log('Error accepting Call', error); - } - }, [call]); - - const rejectCallHandler = useCallback(async () => { - try { - if (callingState === CallingState.LEFT) { - return; - } - await call?.leave({ reject: true }); - } catch (error) { - console.log('Error rejecting Call', error); - } - }, [call, callingState]); - - return ( - - - Reject - - - Accept - - - ); -}; - -const styles = StyleSheet.create({ - buttonGroup: { - flexDirection: 'row', - justifyContent: 'space-evenly', - }, - button: { - height: 80, - width: 80, - borderRadius: 40, - justifyContent: 'center', - }, - acceptButton: { - backgroundColor: '#20E070', - }, - rejectButton: { - backgroundColor: '#FF3742', - }, - callButtonText: { - color: 'white', - textAlign: 'center', - }, -}); -``` - -## Outgoing Call Component - -### Hangup call button - -To hang up a call we can follow the [Leave call](../../core/joining-and-creating-calls/#leave-call-1) concept from our Core Concepts [Joining & Creating Calls](../../core/joining-and-creating-calls) guide. - -To build the buttons we would primarily need `useCall` hook, that gives us the call object that has all the necessary function to hang up the call. - -![Preview of the Hangup call button Example](../assets/05-ui-cookbook/05-incoming-and-outgoing-call/hangup-call-button.png) - -```tsx title="OutgoingCallButtonGroup.tsx" -import React, { useCallback } from 'react'; -import { Pressable, Text, View, StyleSheet } from 'react-native'; -import { - CallingState, - useCall, - useCallStateHooks, -} from '@stream-io/video-react-native-sdk'; - -export const OutgoingCallButtonGroup = () => { - const call = useCall(); - const { useCallCallingState } = useCallStateHooks(); - const callingState = useCallCallingState(); - - const hangupCallHandler = useCallback(async () => { - try { - if (callingState === CallingState.LEFT) { - return; - } - await call?.leave(); - } catch (error) { - console.log('Error rejecting Call', error); - } - }, [call, callingState]); - - return ( - - - Hang up - - - ); -}; - -const styles = StyleSheet.create({ - buttonGroup: { - flexDirection: 'row', - justifyContent: 'space-evenly', - }, - button: { - height: 80, - width: 80, - borderRadius: 40, - justifyContent: 'center', - }, - hangupButton: { - backgroundColor: '#FF3742', - }, - callButtonText: { - color: 'white', - textAlign: 'center', - }, -}); -``` - -## Assembling it all together - -You can assemble the components into a single component as below or choose to pass the components in the respective customization props of the [`IncomingCall`](../../ui-components/call/incoming-call/#props)/[`OutgoingCall`](../../ui-components/call/outgoing-call/#props) component. - - - -```tsx -import { StyleSheet, View } from 'react-native'; - -export const IncomingCallComponent = () => { - return ( - - - - - ); -}; - -export const OutgoingCallComponent = () => { - return ( - - - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: '#272A30', - justifyContent: 'space-evenly', - }, -}); -``` diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/06-lobby-preview.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/06-lobby-preview.mdx deleted file mode 100644 index 12376d80a1..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/06-lobby-preview.mdx +++ /dev/null @@ -1,441 +0,0 @@ ---- -title: Lobby Preview -description: Lobby Preview ---- - -import ImageShowcase from '@site/src/components/ImageShowcase'; -import LocalParticipantPreviewOn from '../assets/05-ui-cookbook/06-lobby-preview/local-participant-preview-on.png'; -import LocalParticipantPreviewOff from '../assets/05-ui-cookbook/06-lobby-preview/local-participant-preview-off.png'; -import MediaStreamManagementButtonOn from '../assets/05-ui-cookbook/06-lobby-preview/media-stream-management-button-on.png'; -import MediaStreamManagementButtonOff from '../assets/05-ui-cookbook/06-lobby-preview/media-stream-management-button-off.png'; -import LobbyCompletedOn from '../assets/05-ui-cookbook/06-lobby-preview/lobby-completed-on.png'; -import LobbyCompletedOff from '../assets/05-ui-cookbook/06-lobby-preview/lobby-completed-off.png'; - -In this article, we will discuss key considerations for creating a user-friendly entrance to your call. We will delve into the various elements that can enhance the user experience and ensure a smooth transition from the lobby to the video call itself. By implementing effective lobby page design principles, you can elevate the overall user satisfaction. - -We consider lobby to be place, where: - -- The user can see their own video preview before joining the call. -- The user acquires information about the call to be joined. -- The user can set their video/audio mute preferences before joining the call. -- The user can use the Lobby component to show the participants who are already in the call. - -:::note -The approach to visualise the components will differ from application to application. Therefore, in this guide, we will focus only on the principles of building components and plugging them with right data sources -::: - -## The Call data - -We would like to show some basic information about the call, when users arrive to the lobby. For example: - -- The call id, type, etc. -- Who already joined the call. - -To retrieve the basic details such as call id and type you can use the `useCall` hook. - -Then we can use the following hooks: - -- useCallMetadata - -:::note -The initial call information can be retrieved by the `get` or `getOrCreate` method of a Call instance. - -You can register an effect at the same place for the same, where you create the call(`client.call`). - -```tsx -const call = - /* Created call */ - - useEffect(() => { - const getOrCreateCall = async () => { - try { - await call?.getOrCreate(); - } catch (error) { - console.error('Failed to get or create call', error); - } - }; - - getOrCreateCall(); - }, [call]); -``` - -::: - -These hooks make sure, that the information about call metadata or call members is updated in real time. The updates are made automatically in response to [Stream's WebSocket events](../../advanced/events) arriving from the backend. - -## Video Input Preview - -We can show the local video preview in the Lobby view, before joining the call to check if everything is fine with your video. We can get the video stream from the camera using the media stream from the `call.camera` object and show it using the `RTCView` component from `@stream-io/react-native-webrtc` library. And by using the `useConnectedUser` hook we can get the user info. - -To show the Video preview, we can use the [`RTCView`](https://github.com/GetStream/react-native-webrtc/blob/master/src/RTCView.ts) component from [`@stream-io/react-native-webrtc`](https://github.com/GetStream/react-native-webrtc). - - - -We can go through the usage through the following example: - -```tsx -import { - Avatar, - StreamVideoParticipant, - useConnectedUser, - useCall, - useCallStateHooks, -} from '@stream-io/video-react-native-sdk'; -import React from 'react'; -import { StyleSheet, View, Text } from 'react-native'; -import { RTCView } from '@stream-io/react-native-webrtc'; - -export const LocalVideoRenderer = () => { - const call = useCall(); - const localVideoStream = call?.camera.state.mediaStream; - const connectedUser = useConnectedUser(); - const { useCameraState } = useCallStateHooks(); - const { status: cameraStatus } = useCameraState(); - - const connectedUserAsParticipant = { - userId: connectedUser?.id, - image: connectedUser?.image, - name: connectedUser?.name, - } as StreamVideoParticipant; - - return ( - - - {cameraStatus === 'enabled ? ( - - ) : ( - - )} - - - ); -}; - -const ParticipantStatus = () => { - const connectedUser = useConnectedUser(); - const participantLabel = connectedUser?.name ?? connectedUser?.id; - const { useMicrophoneState } = useCallStateHooks(); - const { status: microphoneStatus } = useMicrophoneState(); - - return ( - - - {participantLabel} - - {microphoneStatus === 'disabled' && ( - - (Mic off) - - )} - - ); -}; - -const styles = StyleSheet.create({ - videoView: { - backgroundColor: 'gray', - height: 280, - width: '100%', - justifyContent: 'space-between', - alignItems: 'center', - overflow: 'hidden', - marginVertical: 8, - }, - topView: {}, - status: { - alignSelf: 'flex-start', - flexDirection: 'row', - alignItems: 'center', - padding: 8, - borderRadius: 4, - backgroundColor: '#dddddd', - }, - userNameLabel: { - flexShrink: 1, - color: 'white', - }, - svgContainerStyle: { - marginLeft: 8, - }, -}); -``` - -## Media Stream Management - -To control audio or video mute status in the Lobby, you can use the `useCameraState` and `useMicrophoneState` hooks from the `useCallStateHooks`, that orchestrates the local state of the device within the SDK and handles streaming of the media effectively. - - - -:::note -The button's created will orchestrate the video preview in the [Video Input Preview](#video-input-preview) guide above. - -You can add this in the code above: - -```tsx -const { useMicrophoneState, useCameraState } = useCallStateHooks(); -const { status: cameraStatus } = useCameraState(); -const isVideoAvailable = !!localVideoStream && cameraStatus === 'enabled'; -``` - -::: - -We can go through the usage through the following example: - -```tsx title="MediaStreamButtonGroup.tsx" -import React from 'react'; -import { Pressable, View, Text, StyleSheet } from 'react-native'; -import { useCallStateHooks } from '@stream-io/video-react-native-sdk'; - -export const MediaStreamButtonGroup = () => { - const { useMicrophoneState, useCameraState } = useCallStateHooks(); - const { isMuted: microphoneMuted } = useMicrophoneState(); - const { isMuted: cameraMuted } = useCameraState(); - - const audioButtonStyles = [ - styles.button, - { - backgroundColor: microphoneStatus === 'disabled' ? '#080707dd' : 'white', - }, - ]; - - const videoButtonStyles = [ - styles.button, - { - backgroundColor: cameraStatus === 'disabled' ? '#080707dd' : 'white', - }, - ]; - - const audioButtonTextStyles = [ - styles.mediaButtonText, - { - color: microphoneStatus === 'disabled' ? 'white' : '#080707dd', - }, - ]; - - const videoButtonTextStyles = [ - styles.mediaButtonText, - { - color: cameraStatus === 'disabled' ? 'white' : '#080707dd', - }, - ]; - - const toggleAudioMuted = async () => { - await call?.microphone.toggle(); - }; - - const toggleVideoMuted = async () => { - await call?.camera.toggle(); - }; - - return ( - - - {!microphoneMuted ? ( - Audio on - ) : ( - Audio off - )} - - - {!cameraMuted ? ( - Video on - ) : ( - Video off - )} - - - ); -}; - -const styles = StyleSheet.create({ - buttonGroup: { - flexDirection: 'row', - justifyContent: 'space-evenly', - }, - button: { - height: 80, - width: 80, - borderRadius: 40, - justifyContent: 'center', - }, - mediaButtonText: { - textAlign: 'center', - }, -}); -``` - -## Participants in a call - -We can retrieve the list of members, that already joined the call (participants), by inspecting the call metadata object (`callMetadata.session.participants`). The object is provided and maintained up-to-date by `useCallMetadata` hook. - -![Preview of the already joined participants example](../assets/05-ui-cookbook/06-lobby-preview/already_joined_participants.png) - -```tsx title="LobbyParticipantsPreview.tsx" -import { Image, StyleSheet, Text, View } from 'react-native'; -import { useCallStateHooks } from '@stream-io/video-react-native-sdk'; - -export const LobbyParticipantsPreview = () => { - const { useCallSession } = useCallStateHooks(); - const session = useCallSession(); - - if (!(session?.participants && session?.participants.length)) { - return null; - } - - return ( - - - Already in call ({session.participants.length}): - - - {session.participants.map((participant) => ( - - - {participant.user.name && ( - {participant.user.name} - )} - - ))} - - - ); -}; - -const styles = StyleSheet.create({ - infoText: { - color: 'white', - textAlign: 'center', - }, - userInfo: { - flexDirection: 'row', - justifyContent: 'space-evenly', - marginTop: 10, - }, - avatar: { - height: 100, - width: 100, - borderRadius: 50, - }, - title: { - fontSize: 16, - color: 'white', - marginVertical: 10, - textAlign: 'center', - }, -}); -``` - -## Joining the call button - -Lastly, to join a call we simply invoke call.join(). Learn more about the topic in the dedicated [Joining & Creating Calls guide](../../core/joining-and-creating-calls/). - -```tsx title="JoinCallButton.tsx" -import React, { useCallback } from 'react'; -import { Pressable, StyleSheet, Text } from 'react-native'; -import { useCall } from '@stream-io/video-react-native-sdk'; - -export const JoinCallButton = () => { - const call = useCall(); - - const onCallJoinHandler = useCallback(async () => { - try { - await call?.join({ create: true }); - } catch (error) { - if (error instanceof Error) { - console.log('Error joining call:', error); - } - } - }, [call]); - - return ( - - Join Call - - ); -}; - -const styles = StyleSheet.create({ - joinButton: { - backgroundColor: 'blue', - paddingVertical: 10, - }, - joinButtonText: { - textAlign: 'center', - fontSize: 25, - color: 'white', - }, -}); -``` - -## Assembling it all together - - - -```tsx -import { StyleSheet, View } from 'react-native'; - -export const Lobby = () => { - return ( - - - - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: '#272A30', - justifyContent: 'space-evenly', - }, -}); -``` diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/07-video-fallback.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/07-video-fallback.mdx deleted file mode 100644 index 8730f499db..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/07-video-fallback.mdx +++ /dev/null @@ -1,83 +0,0 @@ ---- -title: Video Fallback -description: How to make your own custom participant video fallback ---- - -In terms of design the video fallback can be quite creative. How do you indicate that someone is speaking or their video track is unavailable? How does the background look. - -![Preview of the Default Participant Video Fallback component](../assets/05-ui-cookbook/07-video-fallback/video-fallback-default.png) - -It is expected that the default component may not meet all the requirements of your design/app. Therefore, we will look into ways, how to customize/create a participant video fallback in this tutorial. - -## Custom Participant Video Fallback - -You can customize the participant video fallback by implementing your own video fallback component and passing it to the [`CallContent`](../../ui-components/call/call-content) component. - -![Preview of the Custom Participant Video Fallback component](../assets/05-ui-cookbook/07-video-fallback/video-fallback-custom.png) - -```tsx -import { Text, StyleSheet } from 'react-native'; -import { - StreamVideoParticipant, - ParticipantVideoFallbackProps, -} from '@stream-io/video-react-native-sdk'; - -const CustomParticipantVideoFallback = ({ - participant, -}: ParticipantVideoFallbackProps) => { - return ( - - - - ); -}; - -const styles = StyleSheet.create({ - background: { - ...StyleSheet.absoluteFillObject, - alignItems: 'center', - justifyContent: 'center', - }, - avatar: { - borderRadius: 50, - height: 100, - width: 100, - }, -}); -``` - -## Final Steps - -Now this can be passed to the [`ParticipantVideoFallback`](../../ui-components/call/call-content/#participantvideofallback) prop of the [`CallContent`](../../ui-components/call/call-content) component, as follows: - -```tsx {13} -import { - Call, - CallContent, - StreamCall, -} from '@stream-io/video-react-native-sdk'; - -const VideoCallUI = () => { - let call: Call; - // your logic to create a new call or get an existing call - - return ( - - - - ); -}; -``` - -:::note -To get the participant data, you can use the following hooks from the `useCallStateHooks`: - -- `useParticipants` hook that provides all the necessary details of all the participants. -- `useRemoteParticipants` hook that provides all the details of the participants other than the local participant. -- `useConnectedUser` or `useLocalParticipant` provides the details of the local or connected participant. - -::: diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/08-video-renderer.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/08-video-renderer.mdx deleted file mode 100644 index 483f2564ce..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/08-video-renderer.mdx +++ /dev/null @@ -1,79 +0,0 @@ ---- -title: Video Renderer -description: How to make your own custom participant video renderer ---- - -The video renderer is the most essential UI component in a video call screen, which renders participant's video in real-time. - -![Preview of the Default Video Renderer component](../assets/05-ui-cookbook/08-video-renderer/video-renderer-default.png) - -It is expected that the default component may not meet all the requirements of your design/app. Therefore, we will look into ways, how to customize/create a participant label in this tutorial. - -## Custom Video Renderer - -You can customize the video renderer by implementing your own video renderer component and passing it to the [`CallContent`](../../ui-components/call/call-content) component. - -![Preview of the Custom VideoRenderer component](../assets/05-ui-cookbook/08-video-renderer/video-renderer-custom.png) - -```tsx -import { View, StyleSheet } from 'react-native'; -import { RTCView } from '@stream-io/react-native-webrtc'; - -const CustomVideoRenderer = ({ participant }: VideoRendererProps) => { - const { videoStream } = participant; - return ( - - - - ); -}; - -const styles = StyleSheet.create({ - background: { - ...StyleSheet.absoluteFillObject, - alignItems: 'center', - justifyContent: 'center', - }, - stream: { - height: 250, - width: 250, - borderRadius: 125, - }, -}); -``` - -## Final Steps - -Now this can be passed to the [`VideoRenderer`](../../ui-components/call/call-content/#videorenderer) prop of the [`CallContent`](../../ui-components/call/call-content) component, as follows: - -```tsx {13} -import { - Call, - CallContent, - StreamCall, -} from '@stream-io/video-react-native-sdk'; - -const VideoCallUI = () => { - let call: Call; - // your logic to create a new call or get an existing call - - return ( - - - - ); -}; -``` - -:::note -To get the participant data, you can use the following hooks from the `useCallStateHooks`: - -- `useParticipants` hook that provides all the necessary details of all the participants. -- `useRemoteParticipants` hook that provides all the details of the participants other than the local participant. -- `useConnectedUser` or `useLocalParticipant` provides the details of the local or connected participant. - -::: diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/09-reactions.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/09-reactions.mdx deleted file mode 100644 index ab7d85d3be..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/09-reactions.mdx +++ /dev/null @@ -1,227 +0,0 @@ ---- -title: Reactions -description: How to make your own custom participant reaction component ---- - -Reactions are a great way to communicate between users when you have limited speakers or even if the users are in mute mode. - -You can send an emoji to the call with the code below: - -```tsx -const reaction = { - type: 'reaction', - emoji_code: ':like:', - custom: {}, -}; -const call = useCall(); -call?.sendReaction(reaction); -``` - -## Reaction Mapper - -Stream Video React Native SDK provides the default reaction mapper to display proper emojis. It basically handles a couple of default emojis, but you can customize those emoji maps by building your own mapper and passing it to [`supportedReactions`](../../ui-components/call/call-content/#supportedreactions) prop of `CallContent`. - -Once you pass the reaction map here, it is automatically handled in the [`ParticipantReaction`](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Participant/ParticipantView/ParticipantReaction.tsx) component. - -```tsx -import { - StreamReactionType, - Call, - CallContent, - StreamCall, -} from '@stream-io/video-react-native-sdk'; - -const reactions: StreamReactionType[] = [ - { - type: 'reaction', - emoji_code: ':smile:', - custom: {}, - icon: '😊', - }, - { - type: 'raised-hand', - emoji_code: ':raise-hand:', - custom: {}, - icon: '✋', - }, - { - type: 'reaction', - emoji_code: ':fireworks:', - custom: {}, - icon: '🎉', - }, -]; - -const VideoCallUI = () => { - let call: Call; - // your logic to create a new call or get an existing call - - return ( - - - - ); -}; -``` - -You can also use it in the [ReactionsControls](https://github.com/GetStream/stream-video-js/blob/main/packages/react-native-sdk/src/components/Call/CallControls/ReactionsButton.tsx) in order to list the supported reactions in the call controls and use them to send it. - -```tsx -import { - StreamReactionType, - Call, - CallContent, - StreamCall, - ReactionsButton, -} from '@stream-io/video-react-native-sdk'; -import { View, StyleSheet } from 'react-native'; - -const reactions: StreamReactionType[] = [ - { - type: 'reaction', - emoji_code: ':smile:', - custom: {}, - icon: '😊', - }, - { - type: 'raised-hand', - emoji_code: ':raise-hand:', - custom: {}, - icon: '✋', - }, - { - type: 'reaction', - emoji_code: ':fireworks:', - custom: {}, - icon: '🎉', - }, -]; - -const CustomCallControls = () => { - return ( - - - {/* Other Call Controls */} - - ); -}; - -const VideoCallUI = () => { - let call: Call; - // your logic to create a new call or get an existing call - - return ( - - - - ); -}; - -const styles = StyleSheet.create({ - buttonGroup: { - flexDirection: 'row', - justifyContent: 'space-evenly', - paddingVertical: 10, - }, -}); -``` - -## Custom Participant Reaction - -![Preview of the Custom Participant reaction](../assets/05-ui-cookbook/09-reactions/reaction-custom.png) - -You can customize the participant reaction by implementing your own reaction component and passing it to the [`CallContent`](../../ui-components/call/call-content) component. - -```tsx -import { ParticipantReactionProps } from '@stream-io/video-react-native-sdk'; -import { StyleSheet, Text, View } from 'react-native'; - -const CustomParticipantReaction = ({ - participant, - supportedReactions, -}: ParticipantReactionProps) => { - const { reaction } = participant; - - const currentReaction = - reaction && - supportedReactions.find( - (supportedReaction) => - supportedReaction.emoji_code === reaction.emoji_code, - ); - - return ( - - {currentReaction?.icon} - - ); -}; - -const styles = StyleSheet.create({ - background: { - alignItems: 'center', - justifyContent: 'center', - zIndex: Z_INDEX.IN_FRONT, - }, - reaction: { - fontSize: 50, - }, -}); -``` - -## Final Steps - -Now this can be passed to the [`ParticipantReaction`](../../ui-components/call/call-content/#participantreaction) prop of the [`CallContent`](../../ui-components/call/call-content) component, as follows: - -```tsx -import { - Call, - CallContent, - StreamCall, -} from '@stream-io/video-react-native-sdk'; - -const VideoCallUI = () => { - let call: Call; - // your logic to create a new call or get an existing call - - const reactions: StreamReactionType[] = [ - { - type: 'reaction', - emoji_code: ':smile:', - custom: {}, - icon: '😊', - }, - { - type: 'raised-hand', - emoji_code: ':raise-hand:', - custom: {}, - icon: '✋', - }, - { - type: 'reaction', - emoji_code: ':fireworks:', - custom: {}, - icon: '🎉', - }, - ]; - - return ( - - - - ); -}; -``` - -:::note -To get the participant data, you can use the following hooks from the `useCallStateHooks`: - -- `useParticipants` hook that provides all the necessary details of all the participants. -- `useRemoteParticipants` hook that provides all the details of the participants other than the local participant. -- `useConnectedUser` or `useLocalParticipant` provides the details of the local or connected participant. - -::: diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/10-network-quality-indicator.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/10-network-quality-indicator.mdx deleted file mode 100644 index b0317796d1..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/10-network-quality-indicator.mdx +++ /dev/null @@ -1,89 +0,0 @@ ---- -id: network-quality-indicator -title: Network Quality Indicator ---- - -Network conditions are not always perfect so instead of playing guessing games as to why is someone's video so blurry our default [`ParticipantView`](../../ui-components/participants/participant-view/) component comes equipped with network quality indicator which provides general information about that participants network conditions in four states: - -- UNSPECIFIED -- POOR -- GOOD -- EXCELLENT - -This is the preview of the Default Network Quality Indicator: - -![Preview of the Default Network quality indicator](../assets/05-ui-cookbook/10-network-quality-indicator/network-quality-indicator-default.png) - -In this guide we'll learn how to build and implement our own primitive network quality indicator: - -## Custom Network Quality Indicator - -You'll most likely be displaying this indicator component inside each participant view ([Participant](../../ui-components/participants/participant-view)) within a call layout. To achieve the customization you can follow the snippet below: - -![Preview of the Custom Network quality indicator](../assets/05-ui-cookbook/10-network-quality-indicator/network-quality-indicator-custom.png) - -```tsx -import { Text, View } from 'react-native'; -import { - useParticipants, - ParticipantNetworkQualityIndicatorProps, -} from '@stream-io/video-react-native-sdk'; - -const CustomNetworkQualityIndicator = ({ - participant, -}: ParticipantNetworkQualityIndicatorProps) => { - return ( - - - {'⭐️'.repeat(participant.connectionQuality)} - - - ); -}; - -const styles = StyleSheet.create({ - container: { - backgroundColor: 'gray', - borderRadius: 5, - alignSelf: 'center', - padding: 5, - }, - connection: { - fontSize: 10, - }, -}); -``` - -## Final Steps - -Now this can be passed to the [`ParticipantNetworkQualityIndicator`](../../ui-components/call/call-content/#participantnetworkqualityindicator) prop of the [`CallContent`](../../ui-components/call/call-content) component, as follows: - -```tsx {13-15} -import { - Call, - CallContent, - StreamCall, -} from '@stream-io/video-react-native-sdk'; - -const VideoCallUI = () => { - let call: Call; - // your logic to create a new call or get an existing call - - return ( - - - - ); -}; -``` - -:::note -To get the participant data, you can use the following hooks from the `useCallStateHooks`: - -- `useParticipants` hook that provides all the necessary details of all the participants. -- `useRemoteParticipants` hook that provides all the details of the participants other than the local participant. -- `useConnectedUser` or `useLocalParticipant` provides the details of the local or connected participant. - -::: diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/11-runtime-layout-switching.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/11-runtime-layout-switching.mdx deleted file mode 100644 index 2f811d1887..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/11-runtime-layout-switching.mdx +++ /dev/null @@ -1,256 +0,0 @@ ---- -id: runtime-layout-switching -title: Runtime layout switching ---- - -import ImageShowcase from '@site/src/components/ImageShowcase'; -import ParticipantLayoutGrid from '../assets/05-ui-cookbook/11-runtime-layout-switching/grid.png'; -import ParticipantLayoutSpotlight from '../assets/05-ui-cookbook/11-runtime-layout-switching/spotlight.png'; - -Runtime Layout Switching is basically switching the participant's layout from the app. We currently support switching between `grid` and `spotlight` layout modes through our SDK. - - - -## Switching the layout from the App - -To switch the layout from the app we can take the help of the [`layout`](../../ui-components/call/call-content/#mode) prop of the [CallContent](../../ui-components/call/call-content) component. - -We will create a state variable in the app to track the state of the current layout and pass the state to the [`layout`](../../ui-components/call/call-content/#mode) prop of the [CallContent](../../ui-components/call/call-content) component. This is done below: - -```tsx {6,12} -import React, { useState } from 'react'; -import { Call, CallContent } from '@stream-io/video-react-native-sdk'; -import { StyleSheet, View } from 'react-native'; - -const VideoCallUI = () => { - const [selectedLayout, setSelectedLayout] = useState('grid'); - let call: Call; - // your logic to create a new call or get an existing call - - return ( - - - - ); -}; -``` - -## Creating `CallTopView` with layout switch button - -### Creating the Layout switching Modal/Component - -We will create a component that renders the Button which on press opens up a Modal to switch the Layout. Clicking on the layout item will switch the layout and set the state for the `selectedLayout` state in the `VideoCallUI` component that we created above. - -![Preview of the Layout Switch Modal and Button](../assets/05-ui-cookbook/11-runtime-layout-switching/runtime-layout-switching-modal.png) - -```tsx -import React, { useState } from 'react'; -import { Pressable, Text, Modal, StyleSheet, View } from 'react-native'; -import GridIconSvg from '../assets/GridIconSvg'; - -type Layout = 'grid' | 'spotlight'; - -// Component for Individual Layout Item -const LayoutSelectionItem = ({ - layout, - selectedLayout, - setSelectedLayout, - closeModal, -}: { - layout: Layout; - selectedLayout: Layout; - setSelectedLayout: (mode: Layout) => void; - closeModal: () => void; -}) => { - if (!layout) { - return null; - } - - return ( - { - setSelectedLayout(layout); - closeModal(); - }} - style={styles.modalButton} - > - - {layout[0].toUpperCase() + layout.substring(1)} - - - ); -}; - -// The Component that renders a Button which on click opens up the Modal with options to choose the Layout -export const ParticipantsLayoutSwitchButton = ({ - selectedLayout, - setSelectedLayout, -}: { - selectedLayout: Layout; - setSelectedLayout: (m: Layout) => void; -}) => { - const [modalVisible, setModalVisible] = useState(false); - const closeModal = () => setModalVisible(false); - - return ( - <> - - setModalVisible(false)} - > - true}> - - - - - - - - setModalVisible(true)} - style={styles.gridButton} - > - - - - - ); -}; - -const styles = StyleSheet.create({ - centeredView: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: 'rgba(0, 0, 0, 0.5)', - }, - modalView: { - backgroundColor: '#272A30', - borderRadius: 20, - padding: 12, - alignItems: 'flex-start', - shadowColor: '#000000', - shadowOffset: { - width: 0, - height: 2, - }, - shadowOpacity: 0.25, - shadowRadius: 4, - elevation: 5, - }, - gridButton: { - height: 30, - width: 30, - }, - modalButton: { - padding: 16, - }, - modalText: { - fontSize: 20, - fontWeight: 'bold', - }, - buttonsContainer: { - paddingHorizontal: 8, - }, -}); -``` - -### Creating a custom `CallTopView` - -We will create a custom `CallTopView` component that renders the `ParticipantsLayoutSwitchButton` as follows. - -We need to make sure the props `selectedLayout` and `setSelectedLayout` passed from the component that renders the [`CallContent`](../../ui-components/call/call-content). - -```tsx -type Layout = 'grid' | 'spotlight'; - -const CallTopViewComponent = ({ - selectedLayout, - setSelectedLayout, -}: { - selectedLayout: Layout; - setSelectedLayout: React.Dispatch>; -}) => { - return ( - - - - ); -}; - -const styles = StyleSheet.create({ - topView: { - width: '100%', - backgroundColor: 'transparent', - alignItems: 'center', - paddingVertical: 20, - }, -}); -``` - -Finally we pass the `CallTopViewComponent` created above to the [`CallContent`](../../ui-components/call/call-content) component as follows: - -```tsx -import React, { useCallback, useState } from 'react'; -import { Call, CallContent } from '@stream-io/video-react-native-sdk'; - -const VideoCallUI = () => { - const [selectedLayout, setSelectedLayout] = useState('grid'); - let call: Call; - // your logic to create a new call or get an existing call - - const CustomCallTopView = useCallback(() => { - return ( - - ); - }, [selectedLayout, setSelectedLayout]); - - return ( - - - - ); -}; -``` diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/12-landscape-mode.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/12-landscape-mode.mdx deleted file mode 100644 index befdfb8c83..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/12-landscape-mode.mdx +++ /dev/null @@ -1,116 +0,0 @@ ---- -id: landscape-mode -title: Landscape Mode ---- - -import ImageShowcase from '@site/src/components/ImageShowcase'; -import Portrait from '../assets/05-ui-cookbook/12-landscape-mode/portrait.png'; -import Landscape from '../assets/05-ui-cookbook/12-landscape-mode/landscape.png'; - -Switching to landscape mode makes it easier to view the participants in a larger area on mobile devices. -The landscape mode feature introduced in the SDK allows developers to easily implement responsive design for React Native applications between portrait and landscape mode. - -## Passing the landscape mode styles - -The SDK components can take up the default landscape mode styles once the `landscape` prop is passed as `true` on the respective components. - -It can be passed to [`CallContent`](../../ui-components/call/call-content), [`RingingCallContent`](../../ui-components/call/ringing-call-content), [`Lobby`](../../ui-components/call/lobby) components. - -An example of the above is shown below: - -```tsx {13} -import { - Call, - CallContent, - StreamCall, -} from '@stream-io/video-react-native-sdk'; - -const VideoCallUI = () => { - let call: Call; - // your logic to create a new call or get an existing call - - return ( - - - - ); -}; -``` - -## Updating the orientation styles dynamically. - -We can use the [`Dimensions API`](https://reactnative.dev/docs/dimensions) or use readily available packages such as [react-native-orientation](https://www.npmjs.com/package/react-native-orientation) or [expo-screen-orientation](https://docs.expo.dev/versions/latest/sdk/screen-orientation/) to update/inform the accurate orientation of your device to the SDK. - - - -An example without using any external packages and using the [`Dimensions API`](https://reactnative.dev/docs/dimensions) is shown below: - -### Creating the `useOrientation` hook: - -```tsx title="src/hooks/useOrientation.ts" -import { useEffect, useState } from 'react'; -import { Dimensions } from 'react-native'; - -type Orientation = 'portrait' | 'landscape'; - -const getOrientation = (): Orientation => { - const dimensions = Dimensions.get('screen'); - return dimensions.height >= dimensions.width ? 'portrait' : 'landscape'; -}; - -/** - * A hook that returns device orientation. - * @returns 'portrait' : 'landscape' - */ -export const useOrientation = () => { - const [orientation, setOrientation] = useState(getOrientation()); - - useEffect(() => { - const subscription = Dimensions.addEventListener('change', ({ screen }) => { - setOrientation(screen.height >= screen.width ? 'portrait' : 'landscape'); - }); - return () => subscription?.remove(); - }, []); - - return orientation; -}; -``` - -### Passing the orientation to the SDK components - -This can be done by checking if the value returned from the hook above is `landscape` and passing it as prop to the SDK components. - -```tsx -import { - Call, - CallContent, - StreamCall, -} from '@stream-io/video-react-native-sdk'; -import { useOrientation } from '../hooks/useOrientation'; - -const VideoCallUI = () => { - let call: Call; - // your logic to create a new call or get an existing call - const orientation = useOrientation(); - const isLandscape = orientation === 'landscape'; - - return ( - - - - ); -}; -``` diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/13-hosting-a-livestream.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/13-hosting-a-livestream.mdx deleted file mode 100644 index 2f5dcc6f5e..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/13-hosting-a-livestream.mdx +++ /dev/null @@ -1,98 +0,0 @@ ---- -id: hosting-a-livestream -title: Hosting a livestream ---- - -The Video API allows you to assign specific roles for users in a livestream, such as hosts and viewers. Our SDK provides dedicated livestreaming components for both of these roles. - -### Default component - -For the host role, our React Native SDK includes the specialized [`HostLivestream`](../../ui-components/livestream/host-livestream) component. - -Here is a preview of the above component in video mode: - -![Preview of the UI](../assets/05-ui-cookbook/13-hosting-a-livestream/host-livestream-start.png) - -```tsx -import { - HostLivestream, - StreamVideo, - StreamCall, -} from '@stream-io/video-react-native-sdk'; - -export const MyLivestreamApp = () => { - // init client and call here... - return ( - - - - - - ); -}; -``` - -### Adding customization - -The `HostLivestream` provides a lot of customization options that can be passed as props: - -- `HostLivestreamTopView` allows customizing the top view or the header of the `HostLivestream`. It contains the `LiveIndicator`, `FollowerCount`, and the `DurationBadge` component by default. -- `LivestreamLayout` allows customizing the main video layout component of the `HostLivestream`. -- `HostLivestreamControls` allows customizing the bottom livestream controls component of the `HostLivestream`. It contains the `HostStartStreamButton` and the `LivestreamMediaControls` component by default. -- `LiveIndicator` allows customizing the live indicator component that is present in the top view of the `HostLivestream`. -- `FollowerCount` allows customizing the follower count component that is present in the top view of the `HostLivestream`. -- `DurationBadge` allows customizing the duration badge that shows the duration of the livestream in the top view of the `HostLivestream`. -- `HostStartStreamButton` allows customizing the start/end button of the livestream on the controls of the `HostLivestream`. -- `LivestreamMediaControls` allows customizing the media controls button of the livestream on the controls of the `HostLivestream`. -- `onEndStreamHandler` allows full override of the default functionality on what should happen when host ends the streaming using `HostStartStreamButton`. -- `onStartStreamHandler` allows full override of the default functionality on what should happen when host starts the streaming using `HostStartStreamButton`. - -An example to customize the `FollowerCount` component is shown below: - -```tsx -import { - HostLivestream, - StreamVideo, - StreamCall, - useCallStateHooks, -} from '@stream-io/video-react-native-sdk'; -import { View, Text, StyleSheet } from 'react-native'; - -const FollowerCountComponent = () => { - const { useParticipantCount } = useCallStateHooks(); - const totalParticipants = useParticipantCount(); - - return ( - - {totalParticipants} - - ); -}; - -export const MyLivestreamApp = () => { - // init client and call here... - return ( - - - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - paddingHorizontal: 8, - paddingVertical: 4, - backgroundColor: 'gray', - }, - label: { - color: 'cyan', - fontSize: 15, - }, -}); -``` - -Result: - -![Preview of the Custom FollowerCount component](../assets/05-ui-cookbook/13-hosting-a-livestream/custom-follower-count.png) diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/14-watching-a-livestream.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/14-watching-a-livestream.mdx deleted file mode 100644 index 802aa36823..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/05-ui-cookbook/14-watching-a-livestream.mdx +++ /dev/null @@ -1,107 +0,0 @@ ---- -id: watching-a-livestream -title: Watching a livestream ---- - -import ImageShowcase from '@site/src/components/ImageShowcase'; -import ViewerLivestream from '../assets/05-ui-cookbook/14-watching-a-livestream/viewer-livestream.png'; -import ViewerLivestreamScreenShare from '../assets/05-ui-cookbook/14-watching-a-livestream/viewer-livestream-screenshare.png'; - -The Video API allows you to assign specific roles for users in a livestream, such as hosts and viewers. Our SDK provides dedicated livestreaming components for both of these roles. - -The `ViewerLivestream` component leverages the WebRTC protocol for seamless livestream viewing within the SDK. To enable external publishing, you can access HLS credentials from the dashboard. For additional information, please refer to our [livestream tutorial](https://getstream.io/video/sdk/react-native/tutorial/livestreaming/). - -This guide describes how to customize watching a livestream through our SDK. - -### Default component - -For the viewer role, our React Native SDK includes the specialized [`ViewerLivestream`](../../ui-components/livestream/viewer-livestream) component. - -Here is a preview of the above component in video mode: - - - -```tsx -import { - ViewerLivestream, - StreamVideo, - StreamCall, -} from '@stream-io/video-react-native-sdk'; - -export const MyLivestreamApp = () => { - // init client and call here... - return ( - - - - - - ); -}; -``` - -### Adding customization - -The `ViewerLivestream` provides a lot of customization options that can be passed as props: - -- `ViewerLivestreamTopView` allows customizing the top view or the header of the `ViewerLivestream`. It contains the `LiveIndicator`, `FollowerCount`, and the `DurationBadge` component by default. -- `LivestreamLayout` allows customizing the main video layout component of the `ViewerLivestream`. -- `ViewerLivestreamControls` allows customizing the bottom livestream controls component of the `ViewerLivestream`. It contains the `ViewerLeaveStreamButton`. -- `LiveIndicator` allows customizing the live indicator component that is present in the top view of the `ViewerLivestream`. -- `FollowerCount` allows customizing the follower count component that is present in the top view of the `ViewerLivestream`. -- `DurationBadge` allows customizing the duration badge that shows the duration of the livestream in the top view of the `ViewerLivestream`. -- `ViewerLeaveStreamButton` allows customizing the leave button of the livestream on the controls of the `ViewerLivestream`. -- `FloatingParticipantView` allows customizing the Floating Participant View that renders the video of the participant when screen is shared. -- `onLeaveStreamHandler` allows full override of the default functionality on what should happen when viewer ends the streaming using `ViewerLeaveStreamButton`. - -An example to customize the `ViewerLeaveStreamButton` button is shown below: - -```tsx -import { - ViewerLivestream, - StreamVideo, - StreamCall, - useCall, -} from '@stream-io/video-react-native-sdk'; -import { Button } from 'react-native'; - -const ViewerLeaveStreamButtonComponent = () => { - const call = useCall(); - - const onPressHandler = async () => { - await call.leave(); - }; - - return ; -}; -``` - -## Final recommendations - -As the translation service is based on `i18next` which `i18n` instance is made available through `StreamI18n`, we encourage you to consult the library's documentation in order to learn about: - -- the use of the translation function -- how to dynamically insert text with interpolation documentation article -- how to format interpolated value i18next's formatting guide -- how to specify different plural forms with the pluralization guide diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/01-troubleshooting-guide.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/01-troubleshooting-guide.mdx deleted file mode 100644 index a144bd235f..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/01-troubleshooting-guide.mdx +++ /dev/null @@ -1,64 +0,0 @@ ---- -id: troubleshooting -title: Troubleshooting -description: A troubleshooting guide for common issues ---- - -There are several possible integration issues that can lead to calls not being established. -This section will cover the most frequent ones. - -## Connection issues - -Connection issues usually happen when you provide an invalid token during the SDK setup. When this happens, a web socket connection can't be established with our backend, resulting in errors when trying to connect to a call. - -### Expired tokens - -When you initialize the `StreamVideoClient` object, you provide a token, as described [here](../../core/client-auth). -The tokens generated in the docs have an expiry date, therefore, please make sure to always use a token with a valid expiry date. -You can check the contents of a JWT token on websites like [this one](https://jwt.io). - -Additionally, when expiring tokens are used, you need to provide a `tokenProvider` when creating `StreamVideoClient`, -that will be invoked when the existing token expires. -This is your chance to update the token by generating a new one on your backend. - -### Wrong secret for token generation - -When you start integrating the SDK into your app, you might copy-paste the token from the docs into your project. -However, that will not work. - -Tokens are generated with the help of the app secret (available in your dashboard), and are unique per app id. -Your app id is different from the demo apps we have as examples in our docs. - -On websites like [this one](https://jwt.io), you can verify if the token is signed with the correct signature. - -While developing, you can manually generate tokens by providing your secret and the user's ID [here](https://getstream.io/chat/docs/javascript/tokens_and_authentication/?language=javascript). -However, note that for production usage, your backend would need to generate these tokens. - -### User-token mismatch - -The token can be valid and correct, but for the wrong user. -Make sure that the token you provide matches the id of the user that is used when creating the `StreamVideoClient` object. - -## Ringing calls issues - -Ringing calls issues usually present themselves in a failure to show the incoming call screen or failure to deliver a notification event to the user we're trying to call. - -### Members of a call - -One common issue is that you only specify one user and try to call the same user on another device. -This will not work, if you are the caller, you will not receive a notification that you're being called - you can't call yourself. - -As you would do it in the real world, you would need to specify another member (or members) that you want to call. -Another important note - that member should also exist in Stream's platform (it must have connected at least once). -This is needed because we need to know the user's device and where to send the call notification. - -### Reusing a call id - -Call IDs in general can be reused - you can join a call with the same id many times. -However, the ringing is done only once per call ID. -Therefore, if you implement calls with ringing, make sure that you provide a unique ID every time, -in order for the ring functionality to work. One option is to use a `uuid` as a call ID. - -## Logging - -For easier debugging, you can turn on more verbose logging. To do that, [follow these instructions](../../core/client-auth/#logging). diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/02-chat-with-video.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/02-chat-with-video.mdx deleted file mode 100644 index 48c5c68b70..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/02-chat-with-video.mdx +++ /dev/null @@ -1,1213 +0,0 @@ ---- -title: Chat Integration -description: How to integrate chat & video ---- - -import ImageShowcase from '@site/src/components/ImageShowcase'; -import ChannelListScreen from '../assets/06-advanced/02-chat-integration/channel-list-screen.png'; -import ChannelScreen from '../assets/06-advanced/02-chat-integration/channel-screen.png'; -import IncomingCall from '../assets/06-advanced/02-chat-integration/incoming-call.png'; -import OutgoingCall from '../assets/06-advanced/02-chat-integration/outgoing-call.png'; -import ActiveCall from '../assets/06-advanced/02-chat-integration/active-call.png'; - -It's common for calling apps to have chat, as well as the opposite - chat apps to have a calling functionality. Stream's Chat and Video SDKs are perfectly compatible between each other, and can easily be integrated into an app. - -Before starting with this guide on how to integrate chat and video together, we will initially focus on installation of the Chat SDK. - -## Installation - -To install Stream Chat SDK, you can go through our [Getting Started](https://getstream.io/chat/docs/sdk/reactnative/) guide and follow the installation steps and install all the relevant dependencies as well as optional dependencies according to your application's needs. - -:::note -Do not forget through the [Additional Steps](https://getstream.io/chat/docs/sdk/reactnative/#additional-steps) guide while installing the dependencies. -::: - -## Creating and Passing Chat Client - -To create the Chat Client we will mainly follow [this tutorial](https://getstream.io/chat/react-native-chat/tutorial/#add-stream-chat-to-the-application). - -### Wrapping the chat client - -To wrap our Chat initiation and client logic we will create a `ChatWrapper` component that can be wrapped at the root of the application, so that the client will be available to all the child components. - -```tsx title="src/components/ChatWrapper.tsx" -import React, { PropsWithChildren, useCallback, useMemo } from 'react'; -import { Chat, OverlayProvider, Streami18n } from 'stream-chat-react-native'; -import { useChatClient } from '../hooks/useChatClient'; -import { SafeAreaView, ActivityIndicator } from 'react-native'; - -const streami18n = new Streami18n({ - language: 'en', -}); - -export const ChatWrapper = ({ children }: PropsWithChildren<{}>) => { - const user = { - id: 'your-user-id', - name: 'your-user-name', - }; - const token = 'your-user-token'; - - const chatClient = useChatClient({ - apiKey: STREAM_API_KEY, - userData: user, - tokenProvider: token, - }); - - if (!chatClient) { - // Show a loader until the Chat client loads - return ( - - - - ); - } - - return ( - - - {children} - - - ); -}; -``` - -:::note -It is essential for you to wrap the [`GestureHandlerRootView`](https://getstream.io/chat/docs/sdk/reactnative/basics/troubleshooting/#touchables-not-working), [`OverlayProvider`](https://getstream.io/chat/docs/sdk/reactnative/core-components/overlay-provider/) and [`Chat`](https://getstream.io/chat/docs/sdk/reactnative/core-components/chat/) component on the root of your chat integration hierarchy. - -`GestureHandlerRootView` should be wrapped into the root of your application as follows: - -```tsx title="App.tsx" {16,22} -import React, { useEffect, useState } from 'react'; -import { StyleSheet } from 'react-native'; -import { - Call, - StreamCall, - StreamVideo, - StreamVideoClient, -} from '@stream-io/video-react-native-sdk'; -import { VideoCallUI } from 'src/components/VideoCallUI'; -import { GestureHandlerRootView } from 'react-native-gesture-handler'; - -export default function App() { - // ... - - return ( - - - - - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, -}); -``` - -::: - -### Creating the Chat Client - -Firstly, we will create types for the Chat Client as our setup is focused on Typescript. This can be done as follows: - -```ts title="src/types.ts" -export type LocalAttachmentType = Record; -export type LocalChannelType = Record; -export type LocalCommandType = string; -export type LocalEventType = Record; -export type LocalMessageType = Record; -export type LocalReactionType = Record; -export type LocalUserType = Record; - -export type StreamChatGenerics = { - attachmentType: LocalAttachmentType; - channelType: LocalChannelType; - commandType: LocalCommandType; - eventType: LocalEventType; - messageType: LocalMessageType; - reactionType: LocalReactionType; - userType: LocalUserType; -}; -``` - -Now, we will focus on creating the `useChatClient` hook that is responsible for creating the Chat client. This is fairly simple: - -```tsx title="src/hooks/useChatClient.tsx" -import { useEffect, useRef, useState } from 'react'; -import { - StreamChat, - OwnUserResponse, - UserResponse, - TokenOrProvider, -} from 'stream-chat'; -import { StreamChatGenerics } from '../../types'; - -export const useChatClient = < - SCG extends StreamChatGenerics = StreamChatGenerics, ->({ - apiKey, - userData, - tokenProvider, -}: { - apiKey: string; - userData?: OwnUserResponse | UserResponse; - tokenProvider?: TokenOrProvider; -}) => { - const [chatClient, setChatClient] = useState | null>(null); - const disconnectRef = useRef(Promise.resolve()); - - useEffect(() => { - if (!userData) { - return; - } - - const client = new StreamChat(apiKey); - const connectUser = async () => { - await disconnectRef.current; - try { - await client.connectUser(userData, tokenProvider); - console.log(`[Chat client]: Connected user: ${userData.id}`); - } catch (e) { - console.error('[Chat client]: Failed to establish connection', e); - } - if (!didUserConnectInterrupt) { - setChatClient(client); - } - }; - - let didUserConnectInterrupt = false; - const connectPromise = connectUser(); - - const cleanUp = async () => { - didUserConnectInterrupt = true; - await connectPromise; - try { - await client.disconnectUser(); - console.log(`[Chat client]: Disconnected user: ${userData.id}`); - } catch (e) { - console.error('[Chat client]: Failed to disconnect', e); - } - setChatClient(null); - }; - - return () => { - disconnectRef.current = cleanUp(); - }; - }, [apiKey, userData, tokenProvider]); - - return chatClient; -}; -``` - -:::note -The `apiKey`, `userData` and the `tokenProvider` comes up from the `ChatWrapper` created above. -::: - -## Adding Chat into Video - -### Step 1: Wrapping the `ChatWrapper` - -If we extend our [Video Calling Tutorial](https://getstream.io/video/sdk/react-native/tutorial/video-calling/), our `ChatWrapper` component can we wrapped as a parent/child of the `StreamVideo` as: - -```tsx title="App.tsx" {16,24} -import React, { useEffect, useState } from 'react'; -import { - Call, - StreamCall, - StreamVideo, - StreamVideoClient, -} from '@stream-io/video-react-native-sdk'; -import { VideoCallUI } from './components/VideoCallUI'; -import { ChatWrapper } from './src/components/ChatWrapper'; -import { GestureHandlerRootView } from 'react-native-gesture-handler'; - -export default function App() { - // ... - - return ( - - - - - - - - - - ); -} -``` - -### Step 2: Creating the Chat window/screen - -After the chat client is wrapped to the application, we can focus on creating the chat window/screen. We will take the help of the [`Channel`](https://getstream.io/chat/docs/sdk/reactnative/core-components/channel/), [`MessageList`](https://getstream.io/chat/docs/sdk/reactnative/ui-components/message-list/) and the [`MessageInput`](https://getstream.io/chat/docs/sdk/reactnative/ui-components/message-input/) component to do so. - -We will also create a channel with the same id as the call id to make it distinct. - -![Preview of the Chat Screen](../assets/06-advanced/02-chat-integration/chat-screen.png) - -:::note -While creating a channel, please make sure the channel type is `videocall`. This will add the appropriate permissions/capabilities to the channel for video call by default. -::: - -An example of the same is shown below: - -```tsx title="src/ChatScreen.tsx" -import React, { useEffect, useState } from 'react'; -import { SafeAreaView, ActivityIndicator, StyleSheet } from 'react-native'; -import { - Channel, - MessageInput, - MessageList, - useChatContext, -} from 'stream-chat-react-native'; -import { Channel as ChannelType } from 'stream-chat'; -import { StreamChatGenerics } from '../../../types'; - -export const ChatScreen = () => { - const [channel, setChannel] = useState< - ChannelType | undefined - >(undefined); - const { client } = useChatContext(); - - const CHANNEL_TYPE = 'videocall'; - const CHANNEL_ID = 'your-call-id'; // You can get the call id through sharing params while routing through navigation. - - useEffect(() => { - const createChannel = async () => { - const newChannel = await client.channel(CHANNEL_TYPE, CHANNEL_ID); - setChannel(newChannel); - }; - createChannel(); - }, [client]); - - if (!channel) { - return ( - - - - ); - } - - return ( - - - - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, -}); -``` - -### Step 3: Open the chat window - -To do this, we will create a custom CallControls using the [`ChatButton`](../../ui-components/call/call-controls/#chatbutton) component. - -The `ChatButton` component takes up an `onPressHandler`, which is responsible to perform an action when the button is pressed. In our case, it's the opening of the `ChatScreen`. - -![Preview of the call controls with chat button](../assets/06-advanced/02-chat-integration/call-controls-chat.png). - -In the VideoCallUI component of our [Video Calling Tutorial](https://getstream.io/video/sdk/react-native/tutorial/video-calling/), we will add the above: - -```tsx title="src/components/VideoCallUI.tsx" {13-16,25,35-38,40-42,46} -import React from 'react'; -import { SafeAreaView, StyleSheet, View } from 'react-native'; -import { - CallContent, - ChatButton, - HangUpCallButton, - ToggleAudioPublishingButton, - ToggleCameraFaceButton, - ToggleVideoPublishingButton, -} from '@stream-io/video-react-native-sdk'; - -export type CallControlsComponentProps = { - onChatOpenHandler?: () => void; - onHangupCallHandler?: () => void; -}; - -export const CallControlsComponent = ({ - onChatOpenHandler, - onHangupCallHandler, -}: CallControlsComponentProps) => { - return ( - - - - - - - - ); -}; - -export const VideoCallUI = () => { - const onChatOpenHandler = () => { - // handle how to open the Chat window/screen. You can use this to navigate to the chat screen, open a modal, etc. - // highlight-next-line - }; - - const CustomControlsComponent = useCallback(() => { - return ; - }, [onChatOpenHandler]); - - return ( - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: '#272A30', - }, - callControlsWrapper: { - flexDirection: 'row', - justifyContent: 'space-evenly', - paddingVertical: 12, - zIndex: 2, - backgroundColor: '#272A30', - }, -}); -``` - -### Step 4: Showing unread count indicator for new chats - -To get the unread count of the Channel we need to focus primarily on watching the channel and then listen to relevant events such as `message.new` to update the unread count state variable. The unread count is set to 0 when the message is marked as read, this is done by listening to the `notification.mark_read` event. - -![Preview of the unread count in call controls](../assets/06-advanced/02-chat-integration/call-controls-chat-unread-count.png) - -This is demonstrated and can be done in a hook as created below: - -```tsx title="src/hooks/useUnreadCount.tsx" -import { useEffect, useState } from 'react'; -import { useChatContext } from 'stream-chat-react-native'; -import { Event, Channel as ChannelType } from 'stream-chat'; -import { useCall } from '@stream-io/video-react-native-sdk'; -import { StreamChatGenerics } from 'src/types'; - -/** - * This hook is responsible for returning the unread count of the channel. - * This is done through listening to multiple events. - * @returns number - */ -export const useUnreadCount = () => { - const [unreadCount, setUnreadCount] = useState(0); - const CHANNEL_TYPE = 'videocall'; - const { client } = useChatContext(); - const call = useCall(); - - const cid = `${CHANNEL_TYPE}:${call?.id}`; - - // Effect to watch the channel - useEffect(() => { - let channel: ChannelType; - const watchChannel = async () => { - channel = client.channel(CHANNEL_TYPE, call?.id); - await channel.watch(); - }; - - watchChannel(); - - return () => { - channel.stopWatching(); - }; - }, [call?.id, client]); - - // Effect to set the unreadCount to 0 when the `notification.mark_read` event is received - useEffect(() => { - if (!client) { - return; - } - - const handleEvent = (event: Event) => { - if (event?.cid === cid) { - setUnreadCount(0); - } - }; - - client.on('notification.mark_read', handleEvent); - return () => client.off('notification.mark_read', handleEvent); - }, [client, cid]); - - // Effect to update the unread count when the 'message.new' is received - useEffect(() => { - if (!client) { - return; - } - - const handleEvent = () => { - const channel = client.activeChannels[cid]; - - setUnreadCount(channel?.countUnread() ?? 0); - }; - - handleEvent(); - - client.on('message.new', handleEvent); - - return () => { - client.off('message.new', handleEvent); - }; - }, [client, cid]); - - return unreadCount; -}; -``` - -:::note -Make sure to clean up/unsubscribe to all the events when the hook un-mounts. -::: - -Finally, we will pass the unread count to our component by using the `unreadBadgeCountIndicator` key of the [`chatButton`](../../ui-components/call/call-controls/#chatbutton) prop of [`CallControls`](../../ui-components/call/call-controls) component. - -```tsx title="src/components/VideoCallUI.tsx" {8,14,21,29-30,40} -import React from 'react'; -import { SafeAreaView, StyleSheet, View } from 'react-native'; -import { CallContent, ChatButton } from '@stream-io/video-react-native-sdk'; - -export type CallControlsComponentProps = { - onChatOpenHandler?: () => void; - onHangupCallHandler?: () => void; - unreadBadgeCount?: number; -}; - -export const CallControlsComponent = ({ - onChatOpenHandler, - onHangupCallHandler, - unreadBadgeCount, -}: CallControlsComponentProps) => { - return ( - - // ... - - // ... - - ); -}; - -export const VideoCallUI = () => { - const channelWatched = useChannelWatch(); - const unreadBadgeCount = useUnreadCount({ channelWatched }); - const onChatOpenHandler = () => { - // handle how to open the Chat window/screen. You can use this to navigate to the chat screen, open a modal, etc. - // highlight-next-line - }; - - const CustomControlsComponent = useCallback(() => { - return ( - - ); - }, [onChatOpenHandler]); - - return ( - - - - ); -}; -``` - -## Adding Video into Chat - -By adding Video calling in chat we achieve a messenger like application, that allows calling an individual from the chat screen. - - - -To do so we have the [`useChatClient`](#creating-the-chat-client) and the [`ChatWrapper`](#wrapping-the-chat-client) ready from the [Creating and passing Chat Client](#creating-and-passing-chat-client) steps. - -To achieve a messenger like application we need to have a basic navigation setup so that we can navigate among Channel list screen to particular channel screen, etc. - -### Step 1: Setting up navigation - -We will use [React Navigation](https://reactnavigation.org/) to achieve navigation on our application. - -#### Installing the dependencies - -Let us start with installing the relevant packages: - -As a part of our navigation setup, we plan only to have native stack navigator as a part of our app, therefore we install the following packages: - -```bash -yarn add @react-navigation/native @react-navigation/elements @react-navigation/native-stack react-native-screens react-native-safe-area-context -``` - -After installing the packages, we need to install the pods: - -```bash -npx pod-install -``` - -:::info -You can also follow the [Getting started](https://reactnavigation.org/docs/getting-started/) guide to install the [React Navigation](https://reactnavigation.org/) dependencies effectively. -::: - -#### Wrapping the App in `NavigationContainer` - -You need to wrap the [`NavigationContainer`](https://reactnavigation.org/docs/navigation-container/) in order to automatically manage app state and linking your top-level navigator to the app environment. - -```tsx title="App.tsx" -import React from 'react'; -import { GestureHandlerRootView } from 'react-native-gesture-handler'; -import { NavigationContainer } from '@react-navigation/native'; - -export default function App() { - // ... - - return ( - - // highlight-next-line - - {/* Rest of the code */} - // highlight-next-line - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, -}); -``` - -#### Creating the screens - -We will create two screens primarily, that is: - -- Channel list screen - Here we list all the channels -- Channel screen - Here we list the messages, message input and add the call button to call someone. - -Before that we set up the types for the screen. To do so, we add the following: - -```ts title="src/types.ts" -export type NavigationStackParamsList = { - ChannelListScreen: undefined; - ChannelScreen: undefined; -}; -``` - -Now we add the Channel List Screen: - -![Preview of the Channel List screen](../assets/06-advanced/02-chat-integration/channel-list-screen.png) - -```tsx title="src/screens/ChannelListScreen.tsx" -import React from 'react'; -import { StyleSheet, View } from 'react-native'; -import { ChannelList } from 'stream-chat-react-native'; -import type { ChannelSort } from 'stream-chat'; -import type { NativeStackScreenProps } from '@react-navigation/native-stack'; -import type { NavigationStackParamsList, StreamChatGenerics } from 'src/types'; -import { useAppContext } from 'src/context/AppContext'; - -const sort: ChannelSort = { - last_message_at: -1, - // Your sort options -}; -const options = { - // Your options -}; - -type ChannelListScreenProps = NativeStackScreenProps< - NavigationStackParamsList, - 'ChannelListScreen' ->; - -export function ChannelListScreen({ navigation }: ChannelListScreenProps) { - const { setChannel } = useAppContext(); - // Logged in user details - const user = { - id: 'your-user-id', - name: 'your-user-name', - }; - - const filters = { - type: 'messaging', - members: { $in: [user.id] }, - // Your filters - }; - - return ( - - - filters={filters} - onSelect={(channel) => { - setChannel(channel); - navigation.navigate('ChannelScreen'); - }} - options={options} - sort={sort} - /> - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, -}); -``` - -We primarily use the [`ChannelList`](https://getstream.io/chat/docs/sdk/reactnative/core-components/channel-list/) component to create this screen. - -Next up, we create the Channel Screen: - -![Preview of the Channel screen](../assets/06-advanced/02-chat-integration/channel-screen.png) - -```tsx title="src/screens/ChannelScreen.tsx" -import React, { useEffect } from 'react'; -import { useAppContext } from 'src/context/AppContext'; -import { useHeaderHeight } from '@react-navigation/elements'; -import { - Channel, - MessageInput, - MessageList, - useAttachmentPickerContext, - useOverlayContext, -} from 'stream-chat-react-native'; -import { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { NavigationStackParamsList, StreamChatGenerics } from 'src/types'; -import { Platform, StyleSheet, View } from 'react-native'; - -type ChannelScreenProps = NativeStackScreenProps< - NavigationStackParamsList, - 'ChannelScreen' ->; - -export function ChannelScreen({ navigation }: ChannelScreenProps) { - const { channel } = useAppContext(); - const headerHeight = useHeaderHeight(); - const { setTopInset } = useAttachmentPickerContext(); - const { overlay } = useOverlayContext(); - - useEffect(() => { - navigation.setOptions({ - gestureEnabled: Platform.OS === 'ios' && overlay === 'none', - }); - }, [navigation, overlay]); - - useEffect(() => { - setTopInset(headerHeight); - }, [headerHeight, setTopInset]); - - if (channel === undefined) { - return null; - } - - return ( - - - - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, -}); -``` - -We use the [`Channel`](https://getstream.io/chat/docs/sdk/reactnative/core-components/channel/), [`MessageList`](https://getstream.io/chat/docs/sdk/reactnative/ui-components/message-list/), and the [`MessageInput`](https://getstream.io/chat/docs/sdk/reactnative/ui-components/message-input/) components to build this screen. - -Since we navigate from one screen to other, we need to persist the state of the channel data to use it between different screens. To do this we will create a context. This can be created as follows: - -```tsx title="src/context/AppContext.tsx" -import React, { PropsWithChildren, useState } from 'react'; -import { Channel as ChannelType } from 'stream-chat'; -import { StreamChatGenerics } from '../types'; - -type AppContextType = { - channel: ChannelType | undefined; - setChannel: React.Dispatch< - React.SetStateAction | undefined> - >; -}; - -export const AppContext = React.createContext({} as AppContextType); - -export const AppProvider = ({ children }: PropsWithChildren<{}>) => { - const [channel, setChannel] = useState>(); - - return ( - - {children} - - ); -}; - -export const useAppContext = () => React.useContext(AppContext); -``` - -Wrapping the `AppProvider` created above in the app: - -```tsx title="App.tsx" -import React from 'react'; -import { StyleSheet } from 'react-native'; -import { GestureHandlerRootView } from 'react-native-gesture-handler'; -import { NavigationContainer } from '@react-navigation/native'; -import { AppProvider } from './src/context/AppContext'; - -export default function App() { - // ... - - return ( - - - // highlight-next-line - - {/* Rest of the code */} - // highlight-next-line - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, -}); -``` - -Now we set up the [Native Stack Navigator](https://reactnavigation.org/docs/native-stack-navigator/) by adding the following: - -```tsx title="App.tsx" -import React from 'react'; -import { StyleSheet } from 'react-native'; -import { NavigationStackParamsList } from './src/types'; -import { createNativeStackNavigator } from '@react-navigation/native-stack'; -import { GestureHandlerRootView } from 'react-native-gesture-handler'; -import { NavigationContainer } from '@react-navigation/native'; -import { ChannelListScreen } from './src/screens/ChannelListScreen'; -import { ChannelScreen } from './src/screens/ChannelScreen'; -import { AppProvider } from './src/context/AppContext'; - -const Stack = createNativeStackNavigator(); - -const Messenger = () => { - return ( - - - - - ); -}; - -export default function App() { - // ... - - return ( - - - - // highlight-next-line - - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, -}); -``` - -### Step 2: Wrapping the `ChatWrapper` - -Now, we will wrap the [`ChatWrapper`](#wrapping-the-chat-client) that provides the chat client to the rest of the application created initially in this tutorial. - -```tsx title="App.tsx" -import React from 'react'; -import { StyleSheet } from 'react-native'; -import { GestureHandlerRootView } from 'react-native-gesture-handler'; -import { NavigationContainer } from '@react-navigation/native'; -import { AppProvider } from './src/context/AppContext'; -import { ChatWrapper } from './src/components/ChatWrapper'; -// ... - -export default function App() { - // ... - - return ( - - - - // highlight-next-line - - - // highlight-next-line - - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, -}); -``` - -### Step 3: Creating and passing the Video Wrapper - -Adhering to the [Getting and Setting the credentials](https://getstream.io/video/sdk/react-native/tutorial/video-calling/#step-3---getting-and-setting-the-credentials) step of the [Video Call Tutorial](https://getstream.io/video/sdk/react-native/tutorial/video-calling/), we will create a video wrapper that wraps and provides the Stream's Video client to the rest of the app. - -```tsx title="src/components/VideoWrapper.tsx" -import React, { PropsWithChildren, useEffect, useState } from 'react'; -import { - StreamVideo, - StreamVideoClient, -} from '@stream-io/video-react-native-sdk'; - -export const VideoWrapper = ({ children }: PropsWithChildren<{}>) => { - const [videoClient, setVideoClient] = useState( - undefined - ); - - const user = { - id: 'your-user-id', - name: 'your-user-name', - }; - const token = 'your-user-token'; - - useEffect(() => { - if (!user) { - return; - } - const user = { - id: user.id, - name: user.name, - }; - - const _videoClient = new StreamVideoClient({ - apiKey: STREAM_API_KEY, - user, - tokenProvider: token, - }); - - setVideoClient(_videoClient); - - return () => { - _videoClient.disconnectUser(); - setVideoClient(undefined); - }; - }, [user]); - - if (!videoClient) { - return null; - } - - return ( - - {children} - - ); -}; -``` - -Now we wrap the `VideoWrapper` created above to the app, as follows: - -```tsx title="App.tsx" -import React from 'react'; -import { StyleSheet } from 'react-native'; -import { GestureHandlerRootView } from 'react-native-gesture-handler'; -import { NavigationContainer } from '@react-navigation/native'; -import { AppProvider } from './src/context/AppContext'; -import { ChatWrapper } from './src/components/ChatWrapper'; -import { VideoWrapper } from './src/components/VideoWrapper'; -// ... - -export default function App() { - // ... - - return ( - - - - - // highlight-next-line - - - // highlight-next-line - - - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, -}); -``` - -### Step 4: Passing the call to `StreamCall` - -To listen to the pending calls (both incoming and outgoing), we can use the `useCalls` hook that gives us the list of all the pending calls for a user. - -To pass the call and provide it to the components within the hierarchy we use the [`StreamCall`](../../ui-components/core/stream-call/) component that has a prop [`call`](../../ui-components/core/stream-call/#call). - -We can use the following to create a `Calls` component that can later be passed down the hierarchy in the app. - -```tsx src="src/components/Calls.tsx" {31,39} -import React, { useCallback, useEffect } from 'react'; -import { StreamCall, useCalls } from '@stream-io/video-react-native-sdk'; -import { Alert } from 'react-native'; - -export const Calls = () => { - const calls = useCalls(); - - const handleMoreCalls = useCallback(async () => { - const lastCallCreatedBy = calls[1].data?.created_by; - Alert.alert( - `Incoming call from ${ - lastCallCreatedBy?.name ?? lastCallCreatedBy?.id - }, only 1 call at a time is supported` - ); - }, [calls]); - - // Reset the state of the show variable when there are no calls. - useEffect(() => { - if (calls.length > 1) { - handleMoreCalls(); - } - }, [calls.length, handleMoreCalls]); - - const firstCall = calls[0]; - - if (!firstCall) { - return null; - } - - return {/* Rest of the code */}; -}; -``` - -:::note -We currently support one call at a time, and therefore we show an alert, if a user gets multiple calls while they are in the call already. You can handle it in a different way. - -This is the reason why we pass `calls[0]` to the `StreamCall` component, for now. -::: - -### Step 5: Using the `RingingCallContent` - -We can use the [`RingingCallContent`](../../ui-components/call/ringing-call-content) component to intercept the calling states and show the appropriate(Incoming, Outgoing, JoiningCallIndicator and CallContent) components. - -```tsx title="src/components/Calls.tsx" {28} -import { - StreamCall, - useCalls, - RingingCallContent, -} from '@stream-io/video-react-native-sdk'; -import { CallPanel } from 'src/components/CallPanel'; - -export const Calls = () => { - const calls = useCalls(); - - // ... - - const firstCall = calls[0]; - - if (!firstCall) { - return null; - } - - return ( - - - - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, -}); -``` - -### Step 6: Creating the Channel Header with ability to Create Call - -Creating a Channel Header is fairly simple, you can create a component and pass it to the `options` prop of `StackScreen` to the `header` key as follows: - -![Preview of the Channel Screen Header](../assets/06-advanced/02-chat-integration/channel-screen-header.png) - -```tsx - -``` - -The `ChannelHeader` can be created as follows: - -```tsx title="src/components/ChannelHeader.tsx" -import React, { useCallback } from 'react'; -import { Pressable, StyleSheet, Text, View } from 'react-native'; -import { useAppContext } from 'src/context/AppContext'; -import { NativeStackHeaderProps } from '@react-navigation/native-stack'; -import { - MemberRequest, - useStreamVideoClient, -} from '@stream-io/video-react-native-sdk'; - -type ChannelHeaderProps = NativeStackHeaderProps; - -const ChannelHeader = (props: ChannelHeaderProps) => { - const { navigation } = props; - const { channel } = useAppContext(); - const videoClient = useStreamVideoClient(); - const members = Object.values( - channel?.state?.members ?? {} - ).map((member) => ({ - user_id: member.user_id!, - })); - - const joinCallHandler = useCallback(async () => { - try { - const call = videoClient?.call('default', 'random-call-id'); - await call?.getOrCreate({ - ring: true, - data: { - // more timeout to cancel the call automatically so that it works when callee's app is in quit state - settings_override: { - ring: { - auto_cancel_timeout_ms: 30000, - incoming_call_timeout_ms: 5000, - }, - }, - custom: { channelCid: channel?.cid }, - members: members, - }, - }); - } catch (error) { - console.log('Failed to createCall', error); - } - }, [videoClient, members, channel?.cid]); - - const goBackHandler = useCallback(() => { - navigation.goBack(); - }, [navigation]); - - return ( - - - {/* Back icon component */} - - {/* Your custom Header Title */} - - {/* Call icon component */} - - - ); -}; - -export const ChannelHeaderComponent = (props: NativeStackHeaderProps) => { - return ; -}; - -const styles = StyleSheet.create({ - header: { - backgroundColor: 'white', - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - paddingHorizontal: 10, - paddingTop: 50, - paddingBottom: 20, - }, - name: { - fontSize: 20, - fontWeight: 'bold', - }, - icon: { - height: 18, - width: 18, - }, -}); -``` - -Finally, when you press on the call button, an outgoing call be sent to the participant, which can be accepted or rejected using the Incoming call component screen. On accepting, the call is joined the participants and the call controls are available. - -## Further Steps - -Now that you have the final project for the **Chat in Video** and **Video in Chat** app, you can use it as a template for any kind of apps that feature Chat primarily, with the option to use Video. - -If you're looking to explore more Stream Video based topics, we suggest the following to further customize and improve your Video experience: - -- [Deep Linking Guide](../deeplinking/) - Easily integrate deep linking in your app using the guide. -- [Push Notifications Guide](../push-notifications/overview) - Every Chat and/or Video app needs Push Notifications to notify users for incoming calls. This guide will teach you how to set up different providers and seamlessly integrate push into your app. diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/02-custom-data.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/02-custom-data.mdx deleted file mode 100644 index 03e4b474a9..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/02-custom-data.mdx +++ /dev/null @@ -1,54 +0,0 @@ ---- -id: custom-data -title: Custom Data -description: Managing Call's custom data ---- - -Custom data is additional information that can be added to the default data of Stream Call. -It is an object with key-value pairs that can be attached to users, events, and pretty much almost every domain model in the Stream Video SDK. - -The type definition is the following: - -```typescript -export type Custom = { - [key: string]: any; // where `any` should be JSON-serializable value -}; -``` - -## Adding custom data - -Adding extra data can be done through the Server-Side SDKs or through the Client SDKs. -In the Stream Video SDK, you can add extra data when creating/updating a user, event, reaction and other models. - -As a simple example, let's see how you can add a new `topic` field to a `call` instance. - -```typescript -const call = client.call(type, id); -await call.getOrCreate({ - data: { custom: { topic: 'Monthly sync' } }, -}); - -// or update a custom field -await call.update({ - custom: { topic: 'Weekly sync' }, -}); -``` - -## Reading custom data - -The custom data is exposed through the `custom` state property and `useCallCustomData()` hook: - -```typescript -import { useEffect } from 'react'; -import { useCallStateHooks } from '@stream-io/video-react-native-sdk'; - -const { useCallCustomData } = useCallStateHooks(); -const custom = useCallCustomData(); - -const topic = custom?.topic; -console.log('The topic of the current call is:', topic); - -useEffect(() => { - console.log('The topic is changed to:', custom?.topic); -}, [custom]); -``` diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/03-events.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/03-events.mdx deleted file mode 100644 index f89ebd0b24..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/03-events.mdx +++ /dev/null @@ -1,198 +0,0 @@ ---- -id: events -title: Events -description: How to listen to events ---- - -In most cases, you can simply use the [reactive state store](../../core/call-and-participant-state) to be notified about state changes. -However, for some advanced use cases, you might need to subscribe to the underlying WebSocket events. - -## List of events - -### Client events - -Client events are always delivered if a user is connected to the client using the `connectUser` method. - -The list of client events: - -| Name | Description | -| ------------------ | ----------------------------------------------------------- | -| `connection.ok` | Fired when the authentication process finished successfully | -| `connection.error` | Fired when the WS connections fails | - -### Call events - -These events are related to a specific call. Some of these events are only delivered to clients that are watching the specific call. There are 3 ways to watch a call: - -1. Call the `queryCalls` method with the `watch` option set to `true`: - -```typescript -import { StreamVideoClient } from '@stream-io/video-react-native-sdk'; - -let client: StreamVideoClient; - -client.queryCalls({ - ... - watch: true -}); -``` - -2. Join a call: - -```typescript -import { StreamVideoClient } from '@stream-io/video-react-native-sdk'; - -let client: StreamVideoClient; - -client.call('default', 'test-call').join(); -``` - -3. Watch a call: - -```typescript -import { StreamVideoClient } from '@stream-io/video-react-native-sdk'; - -let client: StreamVideoClient; - -client.call('default', 'test-call').getOrCreate(); -// or -client.call('default', 'test-call').get(); -``` - -The list of call events: - -| Name | Description | Delivered to | -| --------------------------------- | -------------------------------------------------------------- | ---------------- | -| `call.accepted` | A user accepts a notification to join a call | All call members | -| `call.blocked_user` | A user is blocked from the call | Call watchers | -| `call.created` | The call was created | All call members | -| `call.ended` | The call was ended | All call members | -| `call.hls_broadcasting_failed` | The call failed to broadcast | Call watchers | -| `call.hls_broadcasting_started` | The call started to broadcast | Call watchers | -| `call.hls_broadcasting_stopped` | The call stopped broadcasting | Call watchers | -| `call.live_started` | The call left backstage mode | Call watchers | -| `call.member_added` | One or more members were added to the call | All call members | -| `call.member_removed` | One or more members were removed from the call | All call members | -| `call.member_updated` | One or more members were updated | All call members | -| `call.member_updated_permission` | One or more members' role was updated | All call members | -| `call.notification` | A user is calling all call members | All call members | -| `call.permission_request` | A user is requesting permissions | Call watchers | -| `call.permissions_updated` | A member's permissions were updated | Call watchers | -| `call.reaction_new` | A new reaction was sent | Call watchers | -| `call.recording_failed` | A recording failed | Call watchers | -| `call.recording_ready` | A recording is ready | Call watchers | -| `call.recording_started` | A recording has been started | Call watchers | -| `call.recording_stopped` | The recording was stopped | Call watchers | -| `call.rejected` | A user declined to join the call | All call members | -| `call.ring` | A user is calling all call members | All call members | -| `call.session_ended` | A call session ended (all participants have left the call) | Call watchers | -| `call.session_participant_joined` | A participant joined to the call sessions | Call watchers | -| `call.session_participant_left` | A participant left a call session | Call watchers | -| `call.session_started` | A call session started (the first participant joined the call) | Call watchers | -| `call.unblocked_user` | A user is unblocked | Call watchers | -| `call.updated` | The call was updated | Call watchers | -| `custom` | Custom event | All call members | - -## Listening to client and call events - -You can use the `on` method of the `StreamVideoClient` instance to subscribe to client and call WebSocket events. - -The `on` method takes the type of the event you want to subscribe to or the 'all' keyword indicating that you want to be notified about all events. - -The event handler will receive an object with type [`StreamVideoEvent`](https://github.com/GetStream/stream-video-js/blob/main/packages/client/src/coordinator/connection/types.ts) that has a `type` attribute that tells the type of the event. The available event types are described by the [`EventTypes`](https://github.com/GetStream/stream-video-js/blob/main/packages/client/src/coordinator/connection/types.ts) type. - -The `on` method returns a method that can be called to unsubscribe from WebSocket events. - -Subscribing to all events: - -```typescript -import { StreamVideoClient } from '@stream-io/video-react-native-sdk'; - -let client: StreamVideoClient; - -// Subscribe to all events -const unsubscribe = client.on('all', (event: StreamVideoEvent) => { - console.log(event); -}); - -// Unsubscribe -unsubscribe(); -``` - -Subscribing to `call.created` events: - -```typescript -import { StreamVideoClient } from '@stream-io/video-react-native-sdk'; - -let client: StreamVideoClient; - -// Subscribe to all events -const unsubscribe = client.on('call.created', (event: StreamVideoEvent) => { - if (event.type === 'call.created') { - console.log(`Call created: ${event.call_cid}`); - } -}); - -// Unsubscribe -unsubscribe(); -``` - -## Listening to call events - -You can use the `on` method of the `Call` instance to subscribe to WebSocket events belonging to a specific call. - -The `on` method takes the type of the event you want to subscribe to. - -The event handler will receive an object with type [`StreamCallEvent`](https://github.com/GetStream/stream-video-js/blob/main/packages/client/src/coordinator/connection/types.ts) that has a `type` attribute that tells the type of the event. The available event types are described by the [`CallEventTypes`](https://github.com/GetStream/stream-video-js/blob/main/packages/client/src/coordinator/connection/types.ts) type. - -The `on` method returns a method that can be called to unsubscribe from WebSocket events. - -Subscribing to `call.reaction_new` event. - -```typescript -import { Call } from '@stream-io/video-react-native-sdk'; - -let call: Call; - -// Subscribe to new reactions event -const unsubscribe = call.on('call.reaction_new', (event: StreamVideoEvent) => { - if (event.type === 'call.reaction_new') { - console.log(`New reaction: ${event.reaction}`); - } -}); - -// Unsubscribe -unsubscribe(); -``` - -## Custom events - -You can send custom events between the call participants using the `sendCustomEvent` method of the `call` instance. -Note that the payload for these events is limited to 5KB in size. - -```typescript -import { - Call, - CustomVideoEvent, - StreamVideoEvent, -} from '@stream-io/video-react-native-sdk'; - -let call: Call; - -// sending a custom event -await call.sendCustomEvent({ - type: 'my-event-type', - payload: { - foo: 'bar', - }, -}); - -// listening to or receiving a custom event -call.on('custom', (event: StreamVideoEvent) => { - const customEvent = event as CustomVideoEvent; - const payload = customEvent.custom; - if (payload.type === 'my-event-type') { - console.log(payload.foo); - } -}); -``` diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/03-ringing.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/03-ringing.mdx deleted file mode 100644 index 6d0334974e..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/03-ringing.mdx +++ /dev/null @@ -1,166 +0,0 @@ ---- -id: ringing-calls -title: Ringing calls -description: An overview of how to create ringing calls ---- - -The `Call` object provides several options to ring and notify users about a call. - -## Create call - -To create a ring call, we need to set the `ring` flag to `true` and provide the list of members we want to call. -It is important to note that the caller should also be included in the list of members. - -```typescript -await client.call('default', 'test-outgoing-call').getOrCreate({ - // highlight-next-line - ring: true, - data: { - members: [ - // highlight-next-line - { user_id: 'myself' }, - { user_id: 'my friend' }, - ], - }, -}); -``` - -### Call creation options - -The following options are supported when creating a call: - -| Option | Description | Default | -| ---------- | --------------------------------------------------------------------------------------------------------------- | ------- | -| `members` | A list of members to add to this call. You can specify the role and custom data on these members | - | -| `custom` | Any custom data you want to store | - | -| `settings` | You can overwrite certain call settings for this specific call. This overwrites the call type standard settings | - | -| `startsAt` | When the call will start. Used for calls scheduled in the future, livestreams, audio rooms etc | - | -| `team` | Restrict the access to this call to a specific team | - | -| `ring` | If you want the call to ring for each member | `false` | -| `notify` | If you want the call to nofiy each member by sending push notification. | `false` | - -This step will start the signaling flow. -The caller will automatically join the call once the first callee accepts the call. -The call will automatically stop if every callee rejects the call. - -:::note -When ring is true, a **push notification** will be sent to the members, provided their app have the required setup. -For more details around push notifications, please check [this page](../../advanced/push-notifications/overview). -::: - -## Watch for incoming and outgoing calls - -The easiest way to watch for incoming and outgoing calls is to use the `useCalls` hook and the [`RingingCallContent`](../../ui-components/call/ringing-call-content) component. - -**Important**: Make sure that the ringing calls are watched in the root component of your app. This makes sure that in whichever screen the user is in, or if the app was opened from a push notification it is shown. Below is an example of how to watch for ringing calls in the root component of your App. - -```ts -import { SafeAreaView, StyleSheet } from 'react-native'; -import { - StreamCall, - StreamVideo, - useCalls, - RingingCallContent, - StreamVideoClient, - User, -} from '@stream-io/video-react-native-sdk'; - -const user: User = { - id: 'sara', -}; -const apiKey = ''; -const tokenProvider = () => Promise.resolve(''); -const client = StreamVideoClient.getOrCreateInstance({ apiKey, tokenProvider, user }); - -const RingingCalls = () => { - // filter for ringing calls - const calls = useCalls().filter( - (c) => c.state.callingState === CallingState.RINGING, - ); - const call = calls[0]; - if (!call) return null; - - return ( - - - - - - ); -} - -const App = () => { - return ( - - - - - ); -}; - -export default App; -``` - -In the above example, the component `RingingCalls` renders over the rest of the App whenever there is a incoming or outgoing call. Alternatively you can use a Modal view or Dialog to show there is a ringing call over the rest of your app. - -## Canceling a call - -A caller can cancel an outgoing call until the first callee accepts the call. Canceling a call will stop the signaling flow. - -```typescript -await call.leave(); -``` - -Please note that calling `call.leave()` after joining the call won't stop the signaling flow. - -## Accepting a call - -A callee can accept or reject an incoming call. To accept and join the call: - -```typescript -await call.join(); -``` - -Please note that it's possible to join multiple calls. If you only want to allow one active call, you must leave joined calls before accepting an incoming call. - -## Rejecting a call - -A callee can accept or reject an incoming call. To reject the call: - -```typescript -await call.leave({ reject: true }); -``` - -## Leave call - -To leave a joined call, you can use the `leave` method: - -```typescript -await call.leave(); -``` - -## End call - -Ending a call requires a [special permission](../../core/permissions-and-moderation). This action terminates the call for everyone. - -```typescript -await call.endCall(); -``` - -## Notifying - -In some cases, you just want to notify users that you joined a call, instead of ringing. -To do this, you should use the `notify` option: - -```typescript -await call.getOrCreate({ notify: true }); -``` - -When notify is true, a regular push notification will be sent to all the members. -This can be useful for livestreams apps or huddles. - -Similarly to ringing, you can use the get method if you are sure that the call exists: - -```typescript -await call.get({ notify: true }); -``` diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/01-overview.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/01-overview.mdx deleted file mode 100644 index fc00b487d4..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/01-overview.mdx +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: Overview ---- - -Push notifications can be configured to receive updates when the application is closed or on the background. Stream Video Server sends push notification for Ringing calls and Live calls that are about to start to users that have at least one registered device. - -Push notifications are sent in the following scenarios: -- you create a call with the `ring` value set to true. In this case, a notification that shows a ringing screen is sent. -- you create a call with the `notify` value set to true. In this case, a regular push notification is sent. -- you haven't answered a call. In this case, a missed call notification is sent (regular push notification). - -To receive push notifications from Stream Video Server, you'll need to: - -1. Configure your push notification provider on the [Stream Dashboard](https://dashboard.getstream.io). -2. Add the native setup for both iOS and Android. -3. Setup the push config for the SDK. \ No newline at end of file diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/02-push-providers/01-firebase.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/02-push-providers/01-firebase.mdx deleted file mode 100644 index af520bbc1b..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/02-push-providers/01-firebase.mdx +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Firebase Cloud Messaging ---- - -import DashboardFirebaseConfig from '../../../../../shared/video/_dashboard-firebase-config.md'; - - diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/02-push-providers/02-apn-voip.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/02-push-providers/02-apn-voip.mdx deleted file mode 100644 index 0515d4fa9a..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/02-push-providers/02-apn-voip.mdx +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Apple Push Notification service (APNs) ---- - -import DashboardApnVoipConfig from '../../../../../shared/video/_dashboard-apn-voip-config.md'; - - \ No newline at end of file diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/02-push-providers/_category_.json b/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/02-push-providers/_category_.json deleted file mode 100644 index 24e7081671..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/02-push-providers/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "Push Providers" -} diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/03-ringing-setup/01-react-native.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/03-ringing-setup/01-react-native.mdx deleted file mode 100644 index ef29b9a05b..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/03-ringing-setup/01-react-native.mdx +++ /dev/null @@ -1,458 +0,0 @@ ---- -title: React Native ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -This guide discusses how to add push notifications for ringing calls to your project. It will discuss both Android and iOS and go through all the necessary steps. - -The normal user experience in a ringing app, when a user receives a call, is to show a push notification. The user can then interact with the notification to accept or reject the call. In this guide, you will learn how to set up your React Native app to get push notifications from Stream for the incoming calls that your user will receive. - -| Android preview | iOS preview | -|---|---| -| ![Android preview of the Firebase push notification](../../../assets/06-advanced/04-push-notifications/android-preview.png) | ![iOS preview of VoIP notification using Apple Push Notification service (APNs)](../../../assets/06-advanced/04-push-notifications/ios-preview.png) - -## Add push provider credentials to Stream - -Please follow the below guides for adding appropriate push providers to Stream: - -- Android - [Firebase Cloud Messaging](../../push-providers/firebase/) -- iOS - [Apple Push Notification Service (APNs)](../../push-providers/apn-voip/) - -## Install Dependencies - -```bash title=Terminal -yarn add @react-native-firebase/app -yarn add @react-native-firebase/messaging -yarn add @notifee/react-native -yarn add react-native-callkeep -yarn add react-native-voip-push-notification -npx pod-install -``` - -So what did we install precisely? - -- `@react-native-firebase/app` and `@react-native-firebase/messaging` for handling incoming [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging) notifications on Android. -- `@notifee/react-native` - is used to customize and display push notifications. -- `react-native-voip-push-notification` for handling incoming [PushKit](https://developer.apple.com/documentation/pushkit) notifications on iOS. -- `react-native-callkeep` for reporting incoming calls to iOS [CallKit](https://developer.apple.com/documentation/callkit). - -## iOS-specific setup - -### Disable Firebase installation - -We don't use Firebase cloud messaging for iOS in the SDK. Unless Firebase is used for other purposes in your app, you can safely remove it from being installed by iOS and avoid the auto-linking. To do that create a file named `react-native.config.js` in the root of your project and add the following contents: - -```js title="react-native.config.js" -module.exports = { - dependencies: { - '@react-native-firebase/app': { - platforms: { - ios: null, - }, - }, - '@react-native-firebase/messaging': { - platforms: { - ios: null, - }, - }, - }, -}; -``` - -Once this is done, `pod install` must be run again to remove the installed pods. - -### Link required libraries for react native callkeep library - -1. In Xcode: Click on `Build Phases` tab, then open `Link Binary With Libraries`. -2. Add `CallKit.framework` -3. Add `Intents.framework` (and mark it Optional). - -![Example of how to use link libraries required for callkeep library](../../../assets/06-advanced/04-push-notifications/ios-callkit-libraries-link.png) - -### Add header search path for react native callkeep library - -1. In Xcode: Click on `Build Settings` tab, then search for `Header Search Paths`. -2. Add `$(SRCROOT)/../node_modules/react-native-callkeep/ios/RNCallKeep`. - -![Example of how to add header search paths that are required for callkeep library](../../../assets/06-advanced/04-push-notifications/ios-search-paths.png) - -### Add background modes - -In Xcode: Open `Info.plist` file and add the following in `UIBackgroundModes`. By editing this file with the text editor, you should see: - -```xml -UIBackgroundModes - - voip - -``` - -### Enable push notifications - -To receive push notifications, enable the Push Notifications capability in the Xcode `Project` > `Signing & Capabilities` pane. - -### Update AppDelegate - -Update `AppDelegate.m` or `AppDelegate.mm` in Xcode with the following parts for iOS support. - -#### Add headers - -At the top of the file, right after '#import "AppDelegate.h"', add the following headers to import and invoke the methods for the required libraries. - -```objectivec -// highlight-start -#import "RNCallKeep.h" -#import -#import "RNVoipPushNotificationManager.h" -#import "StreamVideoReactNative.h" -// highlight-end -``` - -#### Initialize on app launch - -We need to configure the Firebase library, set up the callkeep library and register VoIP at the app launch. To do this, add the following methods to your existing `didFinishLaunchingWithOptions` method, - -```objectivec -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - // highlight-start - NSString *localizedAppName = [[[NSBundle mainBundle] localizedInfoDictionary] objectForKey:@"CFBundleDisplayName"]; - NSString *appName = [[[NSBundle mainBundle] infoDictionary]objectForKey :@"CFBundleDisplayName"]; - [RNCallKeep setup:@{ - @"appName": localizedAppName != nil ? localizedAppName : appName, - @"supportsVideo": @YES, - // pass @YES here if you want the call to be shown in calls history in the built-in dialer app - @"includesCallsInRecents": @NO, - }]; - // highlight-end - - // highlight-next-line - [RNVoipPushNotificationManager voipRegistration]; - - // the rest -} -``` - -#### Add PushKit methods - -Add the following method to process the VoIP token from iOS and send it to the `react-native-voip-push-notification` library. - -```objectivec -// handle updated push credentials -- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(PKPushType)type { - [RNVoipPushNotificationManager didUpdatePushCredentials:credentials forType:(NSString *)type]; -} -``` - -The final method to add is the one that gets invoked when there is a VoIP push notification from Stream. When there is a push notification and if the app is in the background, we want to display an incoming call notification. Add the following method to achieve this, - -```objectivec -// handle incoming pushes -- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion { - // send event to JS - [RNVoipPushNotificationManager didReceiveIncomingPushWithPayload:payload forType:(NSString *)type]; - - // process the payload - NSDictionary *stream = payload.dictionaryPayload[@"stream"]; - NSString *uuid = [[NSUUID UUID] UUIDString]; - NSString *createdCallerName = stream[@"created_by_display_name"]; - NSString *cid = stream[@"call_cid"]; - - [StreamVideoReactNative registerIncomingCall:cid uuid:uuid]; - - [RNVoipPushNotificationManager addCompletionHandler:uuid completionHandler:completion]; - - // display the incoming call notification - [RNCallKeep reportNewIncomingCall: uuid - handle: createdCallerName - handleType: @"generic" - hasVideo: YES - localizedCallerName: createdCallerName - supportsHolding: YES - supportsDTMF: YES - supportsGrouping: YES - supportsUngrouping: YES - fromPushKit: YES - payload: stream - withCompletionHandler: nil]; -} -``` - -## Android-specific setup - -1. To create a Firebase project, go to the [Firebase console](https://console.firebase.google.com/) and click on **Add project**. - -2. In the console, click the setting icon next to **Project overview** and open **Project settings**. Then, under **Your apps**, click the Android icon to open **Add Firebase to your Android app** and follow the steps. **Make sure that the Android package name you enter is the same as the value of** `android.package` **from your app.json.** - -3. After registering the app, download the **google-services.json** file and place it inside of your project at the following location: `/android/app/google-services.json.` - -4. To allow Firebase on Android to use the credentials, the `google-services` plugin must be enabled on the project. This requires modification to two files in the Android directory. Add the highlighted lines in the relevant files: - -```groovy title="/android/build.gradle" -buildscript { - dependencies { - // ... other dependencies - // highlight-next-line - classpath 'com.google.gms:google-services:4.3.15' - } -} -``` - -```groovy title="/android/build.gradle" -apply plugin: 'com.android.application' -// highlight-next-line -apply plugin: 'com.google.gms.google-services' -``` - -:::note - -The **google-services.json** file contains unique and non-secret identifiers of your Firebase project. For more information, see [Understand Firebase Projects](https://firebase.google.com/docs/projects/learn-more#config-files-objects). - -::: - -### Add declarations in AndroidManifest - -Add the following in `AndroidManifest.xml`: - -```xml title="AndroidManifest.xml" - - - - - -``` - -### Request for notification permissions - -At an appropriate place in your app, request for notification permissions from the user. Below is a small example of how to request permissions using [`react-native-permissions`](https://github.com/zoontek/react-native-permissions) library: - - -```js -import { requestNotifications } from 'react-native-permissions'; - -await requestNotifications(['alert', 'sound']); -``` - - -### Add Firebase message handlers -To process the incoming push notifications, the SDK provides the utility functions that you must add to your existing or new Firebase notification listeners. Below is the snippet of how to add the firebase listeners: - -```ts title="src/utils/setFirebaseListeners.ts" -import messaging from '@react-native-firebase/messaging'; -import { - isFirebaseStreamVideoMessage, - firebaseDataHandler, - onAndroidNotifeeEvent, - isNotifeeStreamVideoEvent, -} from '@stream-io/video-react-native-sdk'; - -export const setFirebaseListeners = () => { - // Set up the background message handler - messaging().setBackgroundMessageHandler(async (msg) => { - if (isFirebaseStreamVideoMessage(msg)) { - await firebaseDataHandler(msg.data); - } else { - // your other background notifications (if any) - } - }); - - // on press handlers of background notifications - notifee.onBackgroundEvent(async (event) => { - if (isNotifeeStreamVideoEvent(event)) { - await onAndroidNotifeeEvent({ event, isBackground: true }); - } else { - // your other background notifications (if any) - } - }); - - // Optionally: set up the foreground message handler - messaging().onMessage((msg) => { - if (isFirebaseStreamVideoMessage(msg)) { - firebaseDataHandler(msg.data); - } else { - // your other foreground notifications (if any) - } - }); - // Optionally: on press handlers of foreground notifications - notifee.onForegroundEvent((event) => { - if (isNotifeeStreamVideoEvent(event)) { - onAndroidNotifeeEvent({ event, isBackground: false }); - } else { - // your other foreground notifications (if any) - } - }); -}; -``` - -**The Firebase message handlers** -* The `onMessage` handler should not be added if you do not want notifications to show up when the app is in the foreground. When the app is in foreground, you would automatically see the incoming call screen. -* The `isFirebaseStreamVideoMessage` method is used to check if this push message is a video related message. And only this needs to be processed by the SDK. -* The `firebaseDataHandler` method is the callback to be invoked to process the message. This callback reads the message and uses the `@notifee/react-native` library to display push notifications. - -**The Notifee event handlers** -* The `onForegroundEvent` handler should not be added if you did not add foreground notifications above. -* The `isNotifeeStreamVideoEvent` method is used to check if the event was a video related notifee event. And only this needs to be processed by the SDK. -* The `onAndroidNotifeeEvent` method is the callback to be invoked to process the event. This callback reads the event and makes sure that the call is accepted or declined. - -:::infoNOTE -If you had disabled the installation of Firebase on iOS, add the above method only for Android using the Platform-specific extensions for React Native. - -For example, say you add the following files in your project: - -``` -setFirebaseListeners.android.ts -setFirebaseListeners.ts -``` - -The method above must only be added to the file that `.android` extension. The other file must add the method but do nothing like below: - -```ts title="setFirebaseListeners.ts" -export const setFirebaseListeners = () => { - // do nothing -}; -``` - -This is to ensure that `@react-native-firebase/messaging` is only imported on the Android platform. -::: - -## Setup the push notifications configuration for the SDK - -The SDK automatically processes the incoming push notifications once the setup above is done if the push notifications configuration has been set using `StreamVideoRN.setPushConfig`. Below is an example of how this method can be called, - -```ts title="src/utils/setPushConfig.ts" -import { - StreamVideoClient, - StreamVideoRN, -} from '@stream-io/video-react-native-sdk'; -import { AndroidImportance } from '@notifee/react-native'; -import AsyncStorage from '@react-native-async-storage/async-storage'; -import { STREAM_API_KEY } from '../../constants'; - -export function setPushConfig() { - StreamVideoRN.setPushConfig({ - ios: { - // add your push_provider_name for iOS that you have setup in Stream dashboard - pushProviderName: __DEV__ ? 'apn-video-staging' : 'apn-video-production', - }, - android: { - // add your push_provider_name for Android that you have setup in Stream dashboard - pushProviderName: __DEV__ - ? 'firebase-video-staging' - : 'firebase-video-production', - // configure the notification channel to be used for incoming calls for Android. - incomingCallChannel: { - id: 'stream_incoming_call', - name: 'Incoming call notifications', - // This is the advised importance of receiving incoming call notifications. - // This will ensure that the notification will appear on-top-of applications. - importance: AndroidImportance.HIGH, - // optional: if you dont pass a sound, default ringtone will be used - // sound: - }, - // configure the functions to create the texts shown in the notification - // for incoming calls in Android. - incomingCallNotificationTextGetters: { - getTitle: (createdUserName: string) => - `Incoming call from ${createdUserName}`, - getBody: (_createdUserName: string) => 'Tap to answer the call', - }, - }, - // add the async callback to create a video client - // for incoming calls in the background on a push notification - createStreamVideoClient: async () => { - // note that since the method is async, - // you can call your server to get the user data or token or retrieve from offline storage. - const userId = await AsyncStorage.getItem('@userId'); - const userName = await AsyncStorage.getItem('@userName'); - if (!userId) return undefined; - // an example promise to fetch token from your server - const tokenProvider = () => yourServer.getTokenForUser(userId).then((auth) => auth.token); - const user = { id: userId, name: userName }; - return StreamVideoClient.getOrCreateInstance({ - apiKey: STREAM_API_KEY, // pass your stream api key - user, - tokenProvider, - }); - }, - }); -} -``` - -## Call the created methods outside of the application lifecycle - -Call the methods we have created outside of your application cycle. That is, alongside your `AppRegistry.registerComponent()` method call at the entry point of your application code. This is because the app can be opened from a dead state through a push notification and in that case, we need to use the configuration and notification callbacks as soon as the JS bridge is initialized. - -Following is an example, - -```js title="index.js" -import { AppRegistry } from 'react-native'; -// highlight-next-line -import { setPushConfig } from 'src/utils/setPushConfig'; -// highlight-next-line -import { setFirebaseListeners } from 'src/utils/setFirebaseListeners'; -import App from './App'; - -// Set push config -// highlight-next-line -setPushConfig(); -// Set the firebase listeners -// highlight-next-line -setFirebaseListeners(); -AppRegistry.registerComponent('app', () => App); -``` - -## Disabling push - usually on logout - -In some cases you would want to disable push from happening. For example, if user logs out of your app. Or if the user switches. You can disable push like below: - -```js -import { StreamVideoRN } from '@stream-io/video-react-native-sdk'; - -await StreamVideoRN.onPushLogout(); -``` - -## Optional: On Android show full-screen incoming call view when phone is locked - -If you want to display a full-screen notification for the incoming call when the phone is locked, please add the following to your main activity: - -```xml - -``` - -In `AndroidManifest.xml` add the following permission before the `` section. - -```xml - -``` - -:::infoNOTE -For apps installed on phones running versions Android 13 or lower, the `USE_FULL_SCREEN_INTENT` permission is enabled by default. - -For all apps being installed on Android 14 and above, the Google Play Store revokes the `USE_FULL_SCREEN_INTENT` for apps that do not have calling or alarm functionalities. Which means, while submitting your app to the play store, if you do **declare that 'Making and receiving calls' is a 'core' functionality** in your app, this permission is granted by default on Android 14 and above. - -If the `USE_FULL_SCREEN_INTENT` permission is not granted, the notification will show up as an expanded heads up notification on the lock screen. -::: - -## Show the incoming and outgoing call UI when app is on the foreground - -The last part of the setup for ringing calls is to show the incoming and outgoing call UIs in the app whenever there is a ringing call. If this was not implemented before, please headover to [this page](../../../ringing-calls/#watch-for-incoming-and-outgoing-calls) of our documentation to implement that. - -## Troubleshooting - -- During development, you may be facing a situation where push notification is shown but its events like accepting or rejecting a call don't work. This is because, during hot module reloading the global event listeners may get de-registered. To properly test during development, make sure that you fully restart the app or test in release mode without the metro packager. -- You can check the "Webhook & Push Logs" section in the [Stream Dashboard](https://dashboard.getstream.io/) to see if Notifications were sent by Stream. -- If you are still having trouble with Push Notifications, please submit a ticket to us at [support](https://getstream.io/contact/support/). - -### Closed notification behavior on Android - -On Android, users can set certain OS-level settings, usually revolving around performance and battery optimization, that can prevent notifications from being delivered when the app is in a killed state. For example, one such setting is the **Deep Clear** option on OnePlus devices using Android 9 and lower versions. diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/03-ringing-setup/02-expo.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/03-ringing-setup/02-expo.mdx deleted file mode 100644 index 8d301dc6b5..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/03-ringing-setup/02-expo.mdx +++ /dev/null @@ -1,359 +0,0 @@ ---- -title: Expo ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -This guide discusses how to add push notifications for ringing calls to your project. It will discuss both Android and iOS and go through all the necessary steps. - -The normal user experience in a ringing app, when a user receives a call, is to show a push notification. The user can then interact with the notification to accept or reject the call. In this guide, you will learn how to set up your Expo app to get push notifications from Stream for the incoming calls that your user will receive. - -| Android preview | iOS preview | -| --------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | -| ![Android preview of the Firebase push notification](../../../assets/06-advanced/04-push-notifications/android-preview.png) | ![iOS preview of VoIP notification using Apple Push Notification service (APNs)](../../../assets/06-advanced/04-push-notifications/ios-preview.png) | - -## Add push provider credentials to Stream - -Please follow the below guides for adding appropriate push providers to Stream: - -- Android - [Firebase Cloud Messaging](../../push-providers/firebase/) -- iOS - [Apple Push Notification Service (APNs)](../../push-providers/apn-voip/) - -## Install Dependencies - -```bash title=Terminal -npx expo install @react-native-firebase/app -npx expo install @react-native-firebase/messaging -npx expo install @notifee/react-native -npx expo install react-native-voip-push-notification -npx expo install react-native-callkeep @config-plugins/react-native-callkeep -``` - -So what did we install precisely? - -- `@react-native-firebase/app` and `@react-native-firebase/messaging` for handling incoming [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging) notifications on Android. -- `@notifee/react-native` - is used to customize and display push notifications. -- `react-native-voip-push-notification` for handling incoming [PushKit](https://developer.apple.com/documentation/pushkit) notifications on iOS. -- `react-native-callkeep` and `@config-plugins/react-native-callkeep` for reporting incoming calls to iOS [CallKit](https://developer.apple.com/documentation/callkit). - -## Add Firebase credentials - -1. To create a Firebase project, go to the [Firebase console](https://console.firebase.google.com/) and click on **Add project**. - -2. In the console, click the setting icon next to **Project overview** and open **Project settings**. Then, under **Your apps**, click the Android icon to open **Add Firebase to your Android app** and follow the steps. **Make sure that the Android package name you enter is the same as the value of** `android.package` **from your app.json.** - -3. After registering the app, download the **google-services.json** file and place it in your project's root directory. - -4. In **app.json**, add an `android.googleServicesFile` field with the relative path to the downloaded **google-services.json** file. If you placed it in the root directory, the path is: - -```js title="app.json" -{ - "android": { - "googleServicesFile": "./google-services.json" - } -} -``` - -5. Similarly, for iOS, in the console, click the setting icon next to **Project overview** and open **Project settings**. Then, under **Your apps**, click the iOS icon to open **Add Firebase to your Apple app** and follow the steps. **Make sure that the Apple bundle ID you enter is the same as the value of** `ios.bundleIdentifier` **from your app.json.** - -6. After registering the app, download the **GoogleService-Info.plist** file and place it in your project's root directory. - -7. In **app.json**, add an `ios.googleServicesFile` field with the relative path to the downloaded **GoogleService-Info.plist** file. If you placed it in the root directory, the path is: - -```js title="app.json" -{ - "ios": { - "googleServicesFile": "./GoogleService-Info.plist", - }, -} -``` - -:::infoINFO - -The **google-services.json** and **GoogleService-Info.plist** files contain unique and non-secret identifiers of your Firebase project. For more information, see [Understand Firebase Projects](https://firebase.google.com/docs/projects/learn-more#config-files-objects). - -::: - -:::note - -We will not be using firebase for iOS. But it is necessary for the setup for react-native-firebase to have the **GoogleService-Info.plist** file. - -::: - -## Add the config plugin properties - -In **app.json**, in the `plugins` field, add the `ringingPushNotifications` property to the `@stream-io/video-react-native-sdk` plugin. Also, add the `@config-plugins/react-native-callkeep` plugin. - -```js title="app.json" -{ - "plugins": [ - [ - "@stream-io/video-react-native-sdk", - { - // highlight-start - "ringingPushNotifications": { - "disableVideoIos": false, - "includesCallsInRecentsIos": false, - "showWhenLockedAndroid": true - }, - // highlight-end - } - ], - // highlight-next-line - "@config-plugins/react-native-callkeep", - [ - // highlight-next-line - "@config-plugins/react-native-webrtc", - { - "cameraPermission": "$(PRODUCT_NAME) requires camera access in order to capture and transmit video", - "microphonePermission": "$(PRODUCT_NAME) requires microphone access in order to capture and transmit audio" - } - ], - // highlight-next-line - "@react-native-firebase/app", - [ - "expo-build-properties", - { - "ios": { - // highlight-next-line - "useFrameworks": "static" - } - } - ] - // your other plugins - ] -} -``` - -:::note - -- The `disableVideoIos` field is used for apps with audio only calls. Pass true to this property to disable video in iOS [CallKit](https://developer.apple.com/documentation/callkit). -- The `includesCallsInRecentsIos` field is used to show call history. Pass true to show the history of calls made in the iOS native dialer -- The `showWhenLockedAndroid` field is used to display a full-screen notification for the incoming call when the phone is locked. Pass true to enable it. -- For iOS only, since `firebase-ios-sdk` requires `use_frameworks` then you want to configure `expo-build-properties` by adding `"useFrameworks": "static"`. -::: - -If Expo EAS build is not used, please do `npx expo prebuild --clean` to generate the native directories again after adding the config plugins. - -## Optional: Disable Firebase initialisation on iOS - -React Native Firebase Messaging automatically registers the device with APNs to receive remote messages. But since we do not use Firebase on iOS, we can disable it via the `firebase.json` file that we can newly create: - -```js title="/firebase.json" -{ - "react-native": { - "messaging_ios_auto_register_for_remote_messages": false - } -} -``` - -## Add Firebase message handlers -To process the incoming push notifications, the SDK provides the utility functions that you must add to your existing or new Firebase notification listeners. Below is the snippet of how to add the firebase listeners: - -```ts title="src/utils/setFirebaseListeners.ts" -import messaging from '@react-native-firebase/messaging'; -import { - isFirebaseStreamVideoMessage, - firebaseDataHandler, - onAndroidNotifeeEvent, - isNotifeeStreamVideoEvent, -} from '@stream-io/video-react-native-sdk'; - -export const setFirebaseListeners = () => { - // Set up the background message handler - messaging().setBackgroundMessageHandler(async (msg) => { - if (isFirebaseStreamVideoMessage(msg)) { - await firebaseDataHandler(msg.data); - } else { - // your other background notifications (if any) - } - }); - - // on press handlers of background notifications - notifee.onBackgroundEvent(async (event) => { - if (isNotifeeStreamVideoEvent(event)) { - await onAndroidNotifeeEvent({ event, isBackground: true }); - } else { - // your other background notifications (if any) - } - }); - - // Optionally: set up the foreground message handler - messaging().onMessage((msg) => { - if (isFirebaseStreamVideoMessage(msg)) { - firebaseDataHandler(msg.data); - } else { - // your other foreground notifications (if any) - } - }); - // Optionally: on press handlers of foreground notifications - notifee.onForegroundEvent((event) => { - if (isNotifeeStreamVideoEvent(event)) { - onAndroidNotifeeEvent({ event, isBackground: false }); - } else { - // your other foreground notifications (if any) - } - }); -}; -``` - -**The Firebase message handlers** -* The `onMessage` handler should not be added if you do not want notifications to show up when the app is in the foreground. When the app is in foreground, you would automatically see the incoming call screen. -* The `isFirebaseStreamVideoMessage` method is used to check if this push message is a video related message. And only this needs to be processed by the SDK. -* The `firebaseDataHandler` method is the callback to be invoked to process the message. This callback reads the message and uses the `@notifee/react-native` library to display push notifications. - -**The Notifee event handlers** -* The `onForegroundEvent` handler should not be added if you did not add foreground notifications above. -* The `isNotifeeStreamVideoEvent` method is used to check if the event was a video related notifee event. And only this needs to be processed by the SDK. -* The `onAndroidNotifeeEvent` method is the callback to be invoked to process the event. This callback reads the event and makes sure that the call is accepted or declined. - -:::infoNOTE -If you had disabled the initialization of Firebase on iOS, add the above method only for Android using the Platform-specific extensions for React Native. - -For example, say you add the following files in your project: - -``` -setFirebaseListeners.android.ts -setFirebaseListeners.ts -``` - -The method above must only be added to the file that `.android` extension. The other file must add the method but do nothing like below: - -```ts title="setFirebaseListeners.ts" -export const setFirebaseListeners = () => { - // do nothing -}; -``` -::: - -## Setup the push notifications configuration for the SDK - -The SDK automatically processes the incoming push notifications once the setup above is done if the push notifications configuration has been set using `StreamVideoRN.setPushConfig`. Below is an example of how this method can be called, - -```ts title="src/utils/setPushConfig.ts" -import { - StreamVideoClient, - StreamVideoRN, -} from '@stream-io/video-react-native-sdk'; -import { AndroidImportance } from '@notifee/react-native'; -import AsyncStorage from '@react-native-async-storage/async-storage'; -import { STREAM_API_KEY } from '../../constants'; - -export function setPushConfig() { - StreamVideoRN.setPushConfig({ - // pass true to inform the SDK that this is an expo app - isExpo: true, - ios: { - // add your push_provider_name for iOS that you have setup in Stream dashboard - pushProviderName: __DEV__ ? 'apn-video-staging' : 'apn-video-production', - }, - android: { - // add your push_provider_name for Android that you have setup in Stream dashboard - pushProviderName: __DEV__ - ? 'firebase-video-staging' - : 'firebase-video-production', - // configure the notification channel to be used for incoming calls for Android. - incomingCallChannel: { - id: 'stream_incoming_call', - name: 'Incoming call notifications', - // This is the advised importance of receiving incoming call notifications. - // This will ensure that the notification will appear on-top-of applications. - importance: AndroidImportance.HIGH, - // optional: if you dont pass a sound, default ringtone will be used - // sound: - }, - // configure the functions to create the texts shown in the notification - // for incoming calls in Android. - incomingCallNotificationTextGetters: { - getTitle: (createdUserName: string) => - `Incoming call from ${createdUserName}`, - getBody: (_createdUserName: string) => 'Tap to answer the call', - }, - }, - // add the async callback to create a video client - // for incoming calls in the background on a push notification - createStreamVideoClient: async () => { - // note that since the method is async, - // you can call your server to get the user data or token or retrieve from offline storage. - const userId = await AsyncStorage.getItem('@userId'); - const userName = await AsyncStorage.getItem('@userName'); - if (!userId) return undefined; - // an example promise to fetch token from your server - const tokenProvider = () => yourServer.getTokenForUser(userId).then((auth) => auth.token); - const user = { id: userId, name: userName }; - return StreamVideoClient.getOrCreateInstance({ - apiKey: STREAM_API_KEY, // pass your stream api key - user, - tokenProvider, - }); - }, - }); -} -``` - -## Call the created methods outside of the application lifecycle - -Call the methods we have created outside of your application cycle. That is inside `index.js` or the equivalent entry point file. This is because the app can be opened from a dead state through a push notification and in that case, we need to use the configuration and notification callbacks as soon as the JS bridge is initialized. - -Following is an example, - -```js title="index.js" -import 'expo-router/entry'; -// highlight-next-line -import { setPushConfig } from 'src/utils/setPushConfig'; -// highlight-next-line -import { setFirebaseListeners } from 'src/utils/setFirebaseListeners'; - -// Set push config -// highlight-next-line -setPushConfig(); -// Set the firebase listeners -// highlight-next-line -setFirebaseListeners(); -``` - -## Request for notification permissions in Android - -At an appropriate place in your app, request for notification permissions from the user on Android. Below is a small example of how to request permissions in Expo: - -```js -import {PermissionsAndroid} from 'react-native'; -PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS); -``` - -## Disabling push - usually on logout - -In some cases you would want to disable push from happening. For example, if user logs out of your app. Or if the user switches. You can disable push like below: - -```js -import { StreamVideoRN } from '@stream-io/video-react-native-sdk'; - -await StreamVideoRN.onPushLogout(); -``` - -## Optional: On Android show full-screen incoming call view when phone is locked - -Passing `true` to `ringingPushNotifications.showWhenLockedAndroid` will add the `USE_FULL_SCREEN_INTENT` permission to the android app and add the necessary configurations to the `MainActivity`. - -:::infoNOTE -For apps installed on phones running versions Android 13 or lower, the `USE_FULL_SCREEN_INTENT` permission is enabled by default. - -For all apps being installed on Android 14 and above, the Google Play Store revokes the `USE_FULL_SCREEN_INTENT` for apps that do not have calling or alarm functionalities. Which means, while submitting your app to the play store, if you do **declare that 'Making and receiving calls' is a 'core' functionality** in your app, this permission is granted by default on Android 14 and above. - -If the `USE_FULL_SCREEN_INTENT` permission is not granted, the notification will show up as an expanded heads up notification on the lock screen. -::: - -## Show the incoming and outgoing call UI when app is on the foreground - -The last part of the setup for ringing calls is to show the incoming and outgoing call UIs in the app whenever there is a ringing call. If this was not implemented before, please headover to [this page](../../../ringing-calls/#watch-for-incoming-and-outgoing-calls) of our documentation to implement that. - -## Troubleshooting - -- During development, you may be facing a situation where push notification is shown but its events like accepting or rejecting a call don't work. This is because, during hot module reloading the global event listeners may get de-registered. To properly test during development, make sure that you fully restart the app or test in release mode without the metro packager. -- You can check the "Webhook & Push Logs" section in the [Stream Dashboard](https://dashboard.getstream.io/) to see if Notifications were sent by Stream. -- If you are still having trouble with Push Notifications, please submit a ticket to us at [support](https://getstream.io/contact/support/). - -### Closed notification behavior on Android - -On Android, users can set certain OS-level settings, usually revolving around performance and battery optimization, that can prevent notifications from being delivered when the app is in a killed state. For example, one such setting is the **Deep Clear** option on OnePlus devices using Android 9 and lower versions. \ No newline at end of file diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/03-ringing-setup/_category_.json b/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/03-ringing-setup/_category_.json deleted file mode 100644 index b65f34022e..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/03-ringing-setup/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "Ringing Setup" -} diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/04-other-than-ringing-setup/01-react-native.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/04-other-than-ringing-setup/01-react-native.mdx deleted file mode 100644 index d0ea800118..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/04-other-than-ringing-setup/01-react-native.mdx +++ /dev/null @@ -1,489 +0,0 @@ ---- -title: React Native ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -This guide discusses how to set up your React Native app to get push notifications from Stream for the non-ringing calls that your user will receive. - -## Add push provider credentials to Stream - -Please follow the below guides for adding appropriate push providers to Stream: - -- Android - [Firebase Cloud Messaging](../../push-providers/firebase/) -- iOS - [Apple Push Notification Service (APNs)](../../push-providers/apn-voip/) - -## Install Dependencies - -```bash title=Terminal -yarn add @react-native-firebase/app -yarn add @react-native-firebase/messaging -yarn add @notifee/react-native -yarn add @react-native-community/push-notification-ios -npx pod-install -``` - -So what did we install precisely? - -- `@react-native-firebase/app` and `@react-native-firebase/messaging` for handling incoming [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging) notifications on Android. -- `@notifee/react-native` - is used to customize and display push notifications. -- `@react-native-community/push-notification-ios` for handling [Apple Push Notification service (APNs)](https://developer.apple.com/documentation/usernotifications/registering_your_app_with_apns) notifications on iOS. - -## Android-specific setup - -1. To create a Firebase project, go to the [Firebase console](https://console.firebase.google.com/) and click on **Add project**. - -2. In the console, click the setting icon next to **Project overview** and open **Project settings**. Then, under **Your apps**, click the Android icon to open **Add Firebase to your Android app** and follow the steps. **Make sure that the Android package name you enter is the same as the value of** `android.package` **from your app.json.** - -3. After registering the app, download the **google-services.json** file and place it inside of your project at the following location: `/android/app/google-services.json.` - -4. To allow Firebase on Android to use the credentials, the `google-services` plugin must be enabled on the project. This requires modification to two files in the Android directory. Add the highlighted lines in the relevant files: - -```groovy title="/android/build.gradle" -buildscript { - dependencies { - // ... other dependencies - // highlight-next-line - classpath 'com.google.gms:google-services:4.3.15' - } -} -``` - -```groovy title="/android/build.gradle" -apply plugin: 'com.android.application' -// highlight-next-line -apply plugin: 'com.google.gms.google-services' -``` - -:::note - -The **google-services.json** file contains unique and non-secret identifiers of your Firebase project. For more information, see [Understand Firebase Projects](https://firebase.google.com/docs/projects/learn-more#config-files-objects). - -::: - -## iOS-specific setup - -### Disable Firebase installation - -We don't use Firebase cloud messaging for iOS in the SDK. Unless Firebase is used for other purposes in your app, you can safely remove it from being installed by iOS and avoid the auto-linking. To do that create a file named `react-native.config.js` in the root of your project and add the following contents: - -```js title="react-native.config.js" -module.exports = { - dependencies: { - '@react-native-firebase/app': { - platforms: { - ios: null, - }, - }, - '@react-native-firebase/messaging': { - platforms: { - ios: null, - }, - }, - }, -}; -``` - -Once this is done, `pod install` must be run again to remove the installed pods. - -### Add background modes - -In Xcode: Open `Info.plist` file and add the following in `UIBackgroundModes`. By editing this file with the text editor, you should see: - -```xml -UIBackgroundModes - - remote-notification - -``` - -### Enable push notifications capability - -Enable the Push Notifications capability in the Xcode `Project` > `Signing & Capabilities` pane. - -### Update `AppDelegate.h` - -At the top of the file, add: - -```objectivec -#import -``` - -Then, add the `UNUserNotificationCenterDelegate`: - -For React-Native v0.71 and above: - -```objectivec -@interface AppDelegate : RCTAppDelegate -``` - -For React-Native v0.70 and below: - -```objectivec -@interface AppDelegate : UIResponder -#import -``` - -#### Add methods - -Then add the following methods: - -```objectivec -// Required for the register event. -- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken -{ - [RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; -} -// Required for the notification event. You must call the completion handler after handling the remote notification. -- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo -fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler -{ - [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; -} -// Required for the registrationError event. -- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error -{ - [RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error]; -} -// Required for localNotification event -- (void)userNotificationCenter:(UNUserNotificationCenter *)center -didReceiveNotificationResponse:(UNNotificationResponse *)response - withCompletionHandler:(void (^)(void))completionHandler -{ - [RNCPushNotificationIOS didReceiveNotificationResponse:response]; -} -//Called when a notification is delivered to a foreground app. --(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler -{ - completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge); -} -``` - -Then add the following lines to `didFinishLaunchingWithOptions`: - -```objectivec -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - // highlight-start - UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; - center.delegate = self; - // highlight-end - // ...rest -} -``` - -### Enable push notifications - -To receive push notifications, enable the Push Notifications capability in the Xcode `Project` > `Signing & Capabilities` pane. - -## Add Push message handlers -To process the incoming push notifications, the SDK provides the utility functions that you must add to your existing or new notification listeners. - -### Add callbacks to process notifications and displaying it - -When Firebase a push message, it must be processed first. For this we expose handler function from the SDK which reads the message and displays it using the `@notifee/react-native` library. Below is the snippet to add message handlers: - -```ts title="src/utils/setPushMessageHandlers.ts" -import messaging from '@react-native-firebase/messaging'; -import { - isFirebaseStreamVideoMessage, - firebaseDataHandler, -} from '@stream-io/video-react-native-sdk'; - -export const setFirebaseListeners = () => { - // Set up the background message handler for Android - messaging().setBackgroundMessageHandler(async (msg) => { - if (isFirebaseStreamVideoMessage(msg)) { - await firebaseDataHandler(msg.data); - } else { - // your other messages (if any) - } - }); - // Set up the foreground message handler for Android - messaging().onMessage((msg) => { - if (isFirebaseStreamVideoMessage(msg)) { - firebaseDataHandler(msg.data); - } else { - // your other messages (if any) - } - }); -}; -``` - -**The Firebase message handlers** -* The `isFirebaseStreamVideoMessage` method is used to check if this push message is a video related message. And only this needs to be processed by the SDK. -* The `firebaseDataHandler` method is the callback to be invoked to process the message. This callback reads the message and uses the `@notifee/react-native` library to display push notifications. - -:::infoNOTE -If you had disabled the installation of Firebase on iOS, add the above method only for Android using the Platform-specific extensions for React Native. - -For example, say you add the following files in your project: - -``` -setFirebaseListeners.android.ts -setFirebaseListeners.ts -``` - -The method above must only be added to the file that `.android` extension. The other file must add the method but do nothing like below: - -```ts title="setPushMessageListeners.ts" -export const setFirebaseListeners = () => { - // do nothing -}; -``` - -This is to ensure that `@react-native-firebase/messaging` is only imported on the Android platform. -::: - -The `firebaseDataHandler` method is the callback to be invoked to process the message. This callback reads the message and uses the `@notifee/react-native` library to display push notifications. - -### Add notification onPress listeners - -Below is the snippet of how to add the notification onPress listeners for Android using `@notifee/react-native` library: - -```ts title="src/utils/setNotifeeListeners.ts" -import { - isNotifeeStreamVideoEvent, - onAndroidNotifeeEvent, -} from '@stream-io/video-react-native-sdk'; -import { Platform } from 'react-native'; -import notifee from '@notifee/react-native'; - -export const setNotifeeListeners = () => { - // on press handlers of background notifications for Android - notifee.onBackgroundEvent(async (event) => { - if (isNotifeeStreamVideoEvent(event)) { - await onAndroidNotifeeEvent({ event, isBackground: true }); - } else { - // your other notifications (if any) - } - }); - // on press handlers of foreground notifications for Android - notifee.onForegroundEvent((event) => { - if (isNotifeeStreamVideoEvent(event)) { - onAndroidNotifeeEvent({ event, isBackground: false }); - } else { - // your other notifications (if any) - } - }); -}; -``` - -**The Notifee event handlers** -* The `isNotifeeStreamVideoEvent` method is used to check if the event was a video related notifee event. And only this needs to be processed by the SDK. -* The `onAndroidNotifeeEvent` method is the callback to be invoked to process the event. This callback reads the event and makes sure that the call is accepted or declined. - -**Adding handler for iOS** - -Below is the snippet of how to add the notification onPress listeners for iOS using `@react-native-community/push-notification-ios` library. Add the following `useEffect` in the root component of your App, this is most likely in `App.tsx`. - -```ts -import PushNotificationIOS from '@react-native-community/push-notification-ios'; -import { - isPushNotificationiOSStreamVideoEvent, - onPushNotificationiOSStreamVideoEvent, -} from '@stream-io/video-react-native-sdk'; - -useEffect(() => { - PushNotificationIOS.addEventListener('notification', (notification) => { - if (isPushNotificationiOSStreamVideoEvent(notification)) { - onPushNotificationiOSStreamVideoEvent(notification); - } else { - // any other APN notifications - } - }); - return () => { - PushNotificationIOS.removeEventListener('notification'); - }; -}, []); -``` - -## Setup the push config for the SDK - -The SDK automatically processes the incoming push notifications once the setup above is done if the push config has been set using `StreamVideoRN.setPushConfig`. To do this follow the steps below, - -### Add the ability to statically navigate to screens in your app - -When a user taps on the push notification and the JS engine is not ready, they should still be able to navigate to the screen that shows the active call. You can achieve this by adding the ability to [navigate without the navigation property in the react-navigation library](https://reactnavigation.org/docs/navigating-without-navigation-prop/). - -The following is an example implementation of a utility file that has helpers to statically navigate in the app: - -```ts title="src/utils/staticNavigation.ts" -import { createNavigationContainerRef } from '@react-navigation/native'; - -import { RootStackParamList } from '../navigation/types'; - -export const navigationRef = createNavigationContainerRef(); - -/** - * This is used to run the navigation logic from root level even before the navigation is ready - */ -export const staticNavigate = ( - ...navigationArgs: Parameters -) => { - // note the use of setInterval, it is responsible for constantly checking if requirements are met and then navigating - // highlight-start - const intervalId = setInterval(async () => { - // run only when the navigation is ready and add any other requirements (like authentication) - if (navigationRef.isReady() && GlobalState.hasAuthentication) { - clearInterval(intervalId); - navigationRef.navigate(...navigationArgs); - } - }, 300); - // highlight-end -}; -``` - -When doing this it is _very important_ to set the `navigationRef` in your navigation container as shown below: - -```ts -import { navigationRef } from './src/utils/staticNavigationUtils'; - -// highlight-next-line - - -; -``` - -### Setup the push config - -Once we have set up the methods to navigate the app from a static method we are ready to call the `StreamVideoRN.setPushConfig` method. Below is an example of how this method can be called, - -```ts title="src/utils/setPushConfig.ts" -import { - StreamVideoClient, - StreamVideoRN, -} from '@stream-io/video-react-native-sdk'; -import { AndroidImportance } from '@notifee/react-native'; -import AsyncStorage from '@react-native-async-storage/async-storage'; -import { STREAM_API_KEY } from '../../constants'; -import { staticNavigate } from './staticNavigationUtils'; - -export function setPushConfig() { - StreamVideoRN.setPushConfig({ - ios: { - // add your push_provider_name for iOS that you have setup in Stream dashboard - pushProviderName: __DEV__ ? 'apn-video-staging' : 'apn-video-production', - }, - android: { - // add your push_provider_name for Android that you have setup in Stream dashboard - pushProviderName: __DEV__ - ? 'firebase-video-staging' - : 'firebase-video-production', - // configure the notification channel to be used for non ringing calls for Android. - callChannel: { - id: 'stream_call_notifications', - name: 'Call notifications', - // This importance will ensure that the notification will appear on-top-of applications. - importance: AndroidImportance.HIGH, - sound: "default", - }, - // configure the functions to create the texts shown in the notification - // for non ringing calls in Android. - callNotificationTextGetters: { - getTitle(type, createdUserName) { - if (type === 'call.live_started') { - return `Call went live, it was started by ${createdUserName}`; - } else { - return `${createdUserName} is notifying you about a call`; - } - }, - getBody(_type, createdUserName) { - return 'Tap to open the call'; - }, - }, - }, - // optional: add the callback to be executed when a non ringing call notification is tapped - onTapNonRingingCallNotification: () => { - const [callType, callId] = call_cid.split(':'); - if (callType === 'livestream') { - staticNavigate({ name: 'LiveStreamCallScreen', params: undefined }); - } else { - staticNavigate({ name: 'ActiveCallScreen', params: undefined }); - } - }, - // add the async callback to create a video client - // for incoming calls in the background on a push notification - createStreamVideoClient: async () => { - // note that since the method is async, - // you can call your server to get the user data or token or retrieve from offline storage. - const userId = await AsyncStorage.getItem('@userId'); - const userName = await AsyncStorage.getItem('@userName'); - if (!userId) return undefined; - // an example promise to fetch token from your server - const tokenProvider = () => yourServer.getTokenForUser(userId).then((auth) => auth.token); - const user = { id: userId, name: userName }; - return StreamVideoClient.getOrCreateInstance({ - apiKey: STREAM_API_KEY, // pass your stream api key - user, - tokenProvider, - }); - }, - }); -} -``` - -## Call the created methods outside of the application lifecycle - -Call the methods we have created outside of your application cycle. That is, alongside your `AppRegistry.registerComponent()` method call at the entry point of your application code. This is because the app can be opened from a dead state through a push notification and in that case, we need to use the config as soon as the JS bridge is initialized. Following is an example, - -Following is an example, - -```js title="index.js" -import { AppRegistry } from 'react-native'; -// highlight-next-line -import { setPushConfig } from 'src/utils/setPushConfig'; -// highlight-next-line -import { setNotifeeListeners } from 'src/utils/setNotifeeListeners'; -// highlight-next-line -import { setFirebaseListeners } from 'src/utils/setFirebaseListeners'; -import App from './App'; - -// Set push config -// highlight-next-line -setPushConfig(); -AppRegistry.registerComponent('app', () => App); -``` - -## Request for notification permissions - -At an appropriate place in your app, request for notification permissions from the user. Below is a small example of how to request permissions using [`react-native-permissions`](https://github.com/zoontek/react-native-permissions) library: - - -```js -import { requestNotifications } from 'react-native-permissions'; - -await requestNotifications(['alert', 'sound']); -``` - - -## Disabling push - usually on logout - -In some cases you would want to disable push from happening. For example, if user logs out of your app. Or if the user switches. You can disable push like below: - -```js -import { StreamVideoRN } from '@stream-io/video-react-native-sdk'; - -await StreamVideoRN.onPushLogout(); -``` - -## Troubleshooting - -- During development, you may be facing a situation where push notification is shown but its events like accepting or rejecting a call don't work. This is because, during hot module reloading the global event listeners may get de-registered. To properly test during development, make sure that you fully restart the app or test in release mode without the metro packager. -- You can check the "Webhook & Push Logs" section in the [Stream Dashboard](https://dashboard.getstream.io/) to see if Notifications were sent by Stream. -- If you are still having trouble with Push Notifications, please submit a ticket to us at [support](https://getstream.io/contact/support/). - -### Closed notification behavior on Android - -On Android, users can set certain OS-level settings, usually revolving around performance and battery optimization, that can prevent notifications from being delivered when the app is in a killed state. For example, one such setting is the **Deep Clear** option on OnePlus devices using Android 9 and lower versions. \ No newline at end of file diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/04-other-than-ringing-setup/02-expo.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/04-other-than-ringing-setup/02-expo.mdx deleted file mode 100644 index 6c84e04f3e..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/04-other-than-ringing-setup/02-expo.mdx +++ /dev/null @@ -1,440 +0,0 @@ ---- -title: Expo ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -This guide discusses how to set up your Expo app to get push notifications from Stream for the non-ringing calls that your user will receive. - -## Add push provider credentials to Stream - -Please follow the below guides for adding appropriate push providers to Stream: - -- Android - [Firebase Cloud Messaging](../../push-providers/firebase/) -- iOS - [Apple Push Notification Service (APNs)](../../push-providers/apn-voip/) - -## Install Dependencies - -```bash title=Terminal -npx expo install expo-notifications -npx expo install expo-task-manager -npx expo install @notifee/react-native -``` - -So what did we install precisely? - -- `expo-notifications` and `expo-task-manager` for handling incoming [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging) notifications on Android and iOS. -- `@notifee/react-native` - is used to customize and display push notifications. - -## Add Firebase credentials - -1. To create a Firebase project, go to the [Firebase console](https://console.firebase.google.com/) and click on **Add project**. - -2. In the console, click the setting icon next to **Project overview** and open **Project settings**. Then, under **Your apps**, click the Android icon to open **Add Firebase to your Android app** and follow the steps. **Make sure that the Android package name you enter is the same as the value of** `android.package` **from your app.json.** - -3. After registering the app, download the **google-services.json** file and place it in your project's root directory. - -4. In **app.json**, add an `android.googleServicesFile` field with the relative path to the downloaded **google-services.json** file. If you placed it in the root directory, the path is: - -```js title="app.json" -{ - "android": { - "googleServicesFile": "./google-services.json" - } -} -``` - -:::note - -The **google-services.json** file contains unique and non-secret identifiers of your Firebase project. For more information, see [Understand Firebase Projects](https://firebase.google.com/docs/projects/learn-more#config-files-objects). - -::: -## Add the config plugin property - -In **app.json**, in the `plugins` field, add true to the `enableNonRingingPushNotifications` property in the `@stream-io/video-react-native-sdk` plugin. - -```js title="app.json" -{ - "plugins": [ - [ - "@stream-io/video-react-native-sdk", - { - // highlight-next-line - "enableNonRingingPushNotifications": true - } - ], - // your other plugins - ] -} -``` - -If Expo EAS build is not used, please do `npx expo prebuild --clean` to generate the native directories again after adding the config plugins. - -## Add Push message handlers -To process the incoming push notifications, the SDK provides the utility functions that you must add to your existing or new notification listeners. - -### Add callbacks to process notifications and displaying it - -To process notifications on Android, we can use either `@react-native-firebase` library or `expo-task-manager`. The disadvantage of `expo-task-manager` is that it does not work when push is delivered to an app that has its underlying process in a killed state. So we recommend using the `@react-native-firebase` library. For iOS, we only need the `expo-notifications` library. - - - - -First we have to install the `react-native-firebase` library. - -```bash title=Terminal -yarn add @react-native-firebase/app -yarn add @react-native-firebase/messaging -``` - -Below is the snippet to add message handlers: - -```ts title="src/utils/setPushMessageHandlers.ts" -import messaging from '@react-native-firebase/messaging'; -import { - isFirebaseStreamVideoMessage, - firebaseDataHandler, -} from '@stream-io/video-react-native-sdk'; -import { Platform } from 'react-native'; -import * as Notifications from 'expo-notifications'; - -export const setPushMessageListeners = () => { - // Set up the background message handler for Android - messaging().setBackgroundMessageHandler(async (msg) => { - if (isFirebaseStreamVideoMessage(msg)) { - await firebaseDataHandler(msg.data); - } else { - // your other messages (if any) - } - }); - // Set up the foreground message handler for Android - messaging().onMessage((msg) => { - if (isFirebaseStreamVideoMessage(msg)) { - firebaseDataHandler(msg.data); - } else { - // your other messages (if any) - } - }); - - if (Platform.OS === 'ios') { - // show notification on foreground on iOS - Notifications.setNotificationHandler({ - // example configuration below to show alert and play sound - handleNotification: async (notification) => ({ - shouldShowAlert: true, - shouldPlaySound: true, - shouldSetBadge: false, - }), - }); - } -}; -``` - -**The Firebase message handlers** -* The `isFirebaseStreamVideoMessage` method is used to check if this push message is a video related message. And only this needs to be processed by the SDK. -* The `firebaseDataHandler` method is the callback to be invoked to process the message. This callback reads the message and uses the `@notifee/react-native` library to display push notifications. - -**Disable Firebase initialisation on iOS** - -React Native Firebase Messaging automatically registers the device with APNs to receive remote messages. But since we do not use Firebase on iOS, we can disable it via the `firebase.json` file that we can newly create: - -```js title="/firebase.json" -{ - "react-native": { - "messaging_ios_auto_register_for_remote_messages": false - } -} -``` - - - -First we have to install the `expo-task-manager` library. - -```bash title=Terminal -npx expo install expo-task-manager -``` - -Below is the snippet to add message handlers: - -```ts title="src/utils/setPushMessageHandlers.ts" -const BACKGROUND_NOTIFICATION_TASK = - 'STREAM-VIDEO-BACKGROUND-NOTIFICATION-TASK'; - -import { - isFirebaseStreamVideoMessage, - firebaseDataHandler, - isExpoNotificationStreamVideoEvent, -} from '@stream-io/video-react-native-sdk'; -import { Platform } from 'react-native'; -import * as Notifications from 'expo-notifications'; - -export const setPushMessageListeners = () => { - TaskManager.defineTask( - BACKGROUND_NOTIFICATION_TASK, - ({ data, error }) => { - if (error) { - return; - } - // @ts-ignore - const dataToProcess = data.notification?.data; - if (data?.sender === 'stream.video'} { - firebaseDataHandler(dataToProcess); - } - } - ); - // background handler (does not handle on app killed state) - Notifications.registerTaskAsync(BACKGROUND_NOTIFICATION_TASK); - // foreground handler - Notifications.setNotificationHandler({ - handleNotification: async (notification) => { - if (Platform.OS === 'android' && isExpoNotificationStreamVideoEvent(notification)) { - const data = notification.request.trigger.remoteMessage?.data!; - await firebaseDataHandler(data, pushConfig); - // do not show this message, it processed by the above handler - return { shouldShowAlert: false, shouldPlaySound: false, shouldSetBadge: false }; - } else { - // configuration for iOS call notification && your other messages, example below to show alert and play sound - return { shouldShowAlert: true, shouldPlaySound: true, shouldSetBadge: false }; - } - }, - }); -}; -``` - -The `firebaseDataHandler` method is the callback to be invoked to process the message. This callback reads the message and uses the `@notifee/react-native` library to display push notifications. - - - - - -### Add notification button listeners - -Below is the snippet of how to add the notification button listeners: - -```ts title="src/utils/setNotifeeListeners.ts" -import { - isNotifeeStreamVideoEvent, - onAndroidNotifeeEvent, - oniOSNotifeeEvent, -} from '@stream-io/video-react-native-sdk'; -import { Platform } from 'react-native'; -import notifee from '@notifee/react-native'; - -export const setNotifeeListeners = () => { - // on press handlers of background notifications for Android - notifee.onBackgroundEvent(async (event) => { - if (isNotifeeStreamVideoEvent(event)) { - await onAndroidNotifeeEvent({ event, isBackground: true }); - } else { - // your other notifications (if any) - } - }); - // on press handlers of foreground notifications for Android - notifee.onForegroundEvent((event) => { - if (Platform.OS === "android" && isNotifeeStreamVideoEvent(event)) { - onAndroidNotifeeEvent({ event, isBackground: false }); - } else { - // your other notifications (if any) - } - }); -}; -``` - -**The Notifee event handlers** -* The `isNotifeeStreamVideoEvent` method is used to check if the event was a video related notifee event. And only this needs to be processed by the SDK. -* The `onAndroidNotifeeEvent` method is the callback to be invoked to process the event. This callback reads the event and makes sure that the call is accepted or declined. - -**Adding handler for iOS** - -Add the following `useEffect` in the root component of your App, this is most likely in `App.tsx`. - -```ts -import * as Notifications from 'expo-notifications'; - -useEffect(() => { - if (Platform.OS === 'ios') { - const subscription = Notifications.addNotificationReceivedListener( - (notification) => { - if (isExpoNotificationStreamVideoEvent(notification)) { - oniOSExpoNotificationEvent(notification); - } else { - // your other notifications (if any) - } - }, - ); - return () => { - subscription.remove(); - }; - } -}, []); -``` - -## Setup the push config for the SDK - -The SDK automatically processes the non ringing call push notifications once the setup above is done if the push config has been set using `StreamVideoRN.setPushConfig`. To do this follow the steps below, - -### Add the ability to statically navigate to screens in your app - -When a user taps on the push notification and the JS engine is not ready, they should still be able to navigate to the screen that shows the active call. You can achieve this by using [imperative navigation in the expo router](https://docs.expo.dev/routing/navigating-pages/#imperative-navigation). - -The following is an example implementation of a utility file that has helpers to statically navigate in the app: - -```ts title="src/utils/staticNavigation.ts" -import { User } from '@stream-io/video-react-native-sdk'; -import { router } from 'expo-router'; - -/** - * This is used to run the navigation logic from root level - */ -export const staticNavigateToActiveCall = () => { - const intervalId = setInterval(async () => { - // add any requirements here (like authentication) - if (GlobalState.hasAuthentication) { - clearInterval(intervalId); - router.push('/activecall'); - } - }, 300); -}; - -export const staticNavigateToLivestreamCall = () => { - const intervalId = setInterval(async () => { - // add any requirements here (like authentication) - if (GlobalState.hasAuthentication) { - clearInterval(intervalId); - router.push('/livestream'); - } - }, 300); -}; -``` - -### Setup the push config - -Once we have set up the methods to navigate the app from a static method we are ready to call the `StreamVideoRN.setPushConfig` method. Below is an example of how this method can be called, - -```ts title="src/utils/setPushConfig.ts" -import { - StreamVideoClient, - StreamVideoRN, -} from '@stream-io/video-react-native-sdk'; -import { AndroidImportance } from '@notifee/react-native'; -import AsyncStorage from '@react-native-async-storage/async-storage'; -import { STREAM_API_KEY } from '../../constants'; -import { staticNavigateToRingingCall, staticNavigateToLivestreamCall } from './staticNavigationUtils'; - -export function setPushConfig() { - StreamVideoRN.setPushConfig({ - // pass true to inform the SDK that this is an expo app - isExpo: true, - ios: { - // add your push_provider_name for iOS that you have setup in Stream dashboard - pushProviderName: __DEV__ ? 'apn-video-staging' : 'apn-video-production', - }, - android: { - // add your push_provider_name for Android that you have setup in Stream dashboard - pushProviderName: __DEV__ - ? 'firebase-video-staging' - : 'firebase-video-production', - // configure the notification channel to be used for non ringing calls for Android. - callChannel: { - id: 'stream_call_notifications', - name: 'Call notifications', - // This importance will ensure that the notification will appear on-top-of applications. - importance: AndroidImportance.HIGH, - sound: "default", - }, - // configure the functions to create the texts shown in the notification - // for non ringing calls in Android. - callNotificationTextGetters: { - getTitle(type, createdUserName) { - if (type === 'call.live_started') { - return `Call went live, it was started by ${createdUserName}`; - } else { - return `${createdUserName} is notifying you about a call`; - } - }, - getBody(_type, createdUserName) { - return 'Tap to open the call'; - }, - }, - }, - // optional: add the callback to be executed when a non ringing call notification is tapped - onTapNonRingingCallNotification: () => { - const [callType, callId] = call_cid.split(':'); - if (callType === 'livestream') { - staticNavigateToLivestreamCall(); - } else { - staticNavigateToActiveCall(); - } - }, - // add the async callback to create a video client - // for incoming calls in the background on a push notification - createStreamVideoClient: async () => { - // note that since the method is async, - // you can call your server to get the user data or token or retrieve from offline storage. - const userId = await AsyncStorage.getItem('@userId'); - const userName = await AsyncStorage.getItem('@userName'); - // an example promise to fetch token from your server - const tokenProvider = () => yourServer.getTokenForUser(userId).then((auth) => auth.token); - const user = { id: userId, name: userName }; - return StreamVideoClient.getOrCreateInstance({ - apiKey: STREAM_API_KEY, // pass your stream api key - user, - tokenProvider, - }); - }, - }); -} -``` - -## Call the created methods outside of the application lifecycle - -Call the methods we have created outside of your application cycle. That is inside `index.js` or the equivalent entry point file. This is because the app can be opened from a dead state through a push notification and in that case, we need to use the configuration and notification callbacks as soon as the JS bridge is initialized. - -Following is an example, - -```js title="index.js" -import 'expo-router/entry'; -// highlight-next-line -import { setPushConfig } from 'src/utils/setPushConfig'; -// highlight-next-line -import { setNotifeeListeners } from 'src/utils/setNotifeeListeners'; -// highlight-next-line -import { setPushMessageListeners } from 'src/utils/setPushMessageListeners'; - -// highlight-next-line -setPushConfig(); -// highlight-next-line -setNotifeeListeners(); -// highlight-next-line -setPushMessageListeners(); -``` - -## Request for notification permissions - -At an appropriate place in your app, request for notification permissions from the user. Below is a small example of how to request permissions in Expo: - -```js -import * as Notifications from 'expo-notifications'; -await Notifications.requestPermissionsAsync(); -``` - -## Disabling push - usually on logout - -In some cases you would want to disable push from happening. For example, if user logs out of your app. Or if the user switches. You can disable push like below: - -```js -import { StreamVideoRN } from '@stream-io/video-react-native-sdk'; - -await StreamVideoRN.onPushLogout(); -``` - -## Troubleshooting - -- During development, you may be facing a situation where push notification is shown but its events like accepting or rejecting a call don't work. This is because, during hot module reloading the global event listeners may get de-registered. To properly test during development, make sure that you fully restart the app or test in release mode without the metro packager. -- You can check the "Webhook & Push Logs" section in the [Stream Dashboard](https://dashboard.getstream.io/) to see if Notifications were sent by Stream. -- If you are still having trouble with Push Notifications, please submit a ticket to us at [support](https://getstream.io/contact/support/). - -### Closed notification behavior on Android - -On Android, users can set certain OS-level settings, usually revolving around performance and battery optimization, that can prevent notifications from being delivered when the app is in a killed state. For example, one such setting is the **Deep Clear** option on OnePlus devices using Android 9 and lower versions. \ No newline at end of file diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/04-other-than-ringing-setup/_category_.json b/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/04-other-than-ringing-setup/_category_.json deleted file mode 100644 index cd918aab7e..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/04-other-than-ringing-setup/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "Other notifications Setup" -} diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/_category_.json b/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/_category_.json deleted file mode 100644 index 89dda607cf..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/04-push-notifications/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "Push Notifications" -} diff --git a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/05-recording.mdx b/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/05-recording.mdx deleted file mode 100644 index 13e8889803..0000000000 --- a/packages/react-native-sdk/docusaurus/docs/reactnative/06-advanced/05-recording.mdx +++ /dev/null @@ -1,84 +0,0 @@ ---- -id: recording -title: Recording -description: Recording ---- - -One highly sought-after feature in many communication applications is call recording. Whether it's for legal compliance, quality assurance, or simply for future reference, the ability to record calls is essential in numerous scenarios. - -This documentation article serves as a guide for implementing a call recording feature in your application. We will explore the technical aspects and best practices involved in capturing and storing recording data during calls. - -## Recording calls - -The `Call` object exposes the call recording API. To start recording, we simply invoke `call.startRecording()`. To stop recording, we use `call.stopRecording()`. -To determine, whether a call recording is in progress, we use `useIsCallRecordingInProgress` hook. This may serve us well when we want to provide visual clues about the recording state. We have to be aware, that it can take a few moments until a call recording starts. We recommend creating a state for signaling that the call recording is starting, but has not begun yet. - -```tsx -import React, { useCallback, useEffect, useState } from 'react'; -import { useCall, useCallStateHooks } from '@stream-io/video-react-native-sdk'; - -import { ActivityIndicator, Button } from 'react-native'; - -export const CustomCallRecordButton = () => { - const call = useCall(); - const { useIsCallRecordingInProgress } = useCallStateHooks(); - const isCallRecordingInProgress = useIsCallRecordingInProgress(); - const [isAwaitingResponse, setIsAwaitingResponse] = useState(false); - - useEffect(() => { - if (!call) { - return; - } - // we wait until call.recording_started/stopped event - // to remove the loading indicator - const eventHandlers = [ - call.on('call.recording_started', () => setIsAwaitingResponse(false)), - call.on('call.recording_stopped', () => setIsAwaitingResponse(false)), - ]; - return () => { - eventHandlers.forEach((unsubscribe) => unsubscribe()); - }; - }, [call]); - - const toggleRecording = useCallback(async () => { - try { - setIsAwaitingResponse(true); - if (isCallRecordingInProgress) { - await call?.stopRecording(); - } else { - await call?.startRecording(); - } - } catch (e) { - console.error('Failed start recording', e); - } - }, [call, isCallRecordingInProgress]); - - return isAwaitingResponse ? ( - - ) : ( - - ); -}; - -export const MyMicrophoneButton = () => { - const { useMicrophoneState } = useCallStateHooks(); - const { microphone, isMute } = useMicrophoneState(); - return ( - - ); -}; -``` - -More information about this topic can be found in the [Camera & Microphone guide](../../guides/camera-and-microphone). - -## UI Components - -The goal of this library is to make it easy to build any type of video/calling experience. You have a few options for the UI: - -- Build your own UI components using the state as shown above. -- Use our library of built-in components. -- Mix & Match between your own and built-in components. - -The built-in components you can customize using [theming](../../ui-components/video-theme) and props. If you decide to build your own components we have a [UI Cookbook section](../../ui-cookbook/overview) to help you get started. - -Let's see an example where we set up the UI using built-in components: - -- Set up theming using the `StreamTheme` provider -- Choose a [UI layout](../../ui-components/core/call-layout) from the SDK-provided ones, such as ``, which provides a layout suitable for speaker-focused video calls. -- Additionally, hook in the default `` component to enable call control functionalities (such as mute, hang up, etc.). - -```tsx -import { - CallControls, - SpeakerLayout, - StreamCall, - StreamTheme, - StreamVideo, -} from '@stream-io/video-react-sdk'; - -import '@stream-io/video-react-sdk/dist/css/styles.css'; - -export const MyApp = () => { - // Assuming you have the 'client' and 'call' created - return ( - - - - - - - - - ); -}; -``` - -## See it in action - -We have prepared a [CodeSandbox example](https://codesandbox.io/embed/stream-video-quickstart-y0wo3m) -that demonstrates the above steps in action. -Feel free to play around with it and explore the SDK's features. - - diff --git a/packages/react-sdk/docusaurus/docs/React/01-basics/04-supported-platforms.mdx b/packages/react-sdk/docusaurus/docs/React/01-basics/04-supported-platforms.mdx deleted file mode 100644 index 4151872ebe..0000000000 --- a/packages/react-sdk/docusaurus/docs/React/01-basics/04-supported-platforms.mdx +++ /dev/null @@ -1,15 +0,0 @@ ---- -title: Supported Platforms -description: A list of supported platforms and SDK size information ---- - -Our React Video SDK can run on all modern browsers and platforms. Here is a list of supported platforms: - -- Chrome 91+ -- Firefox 89+ -- Edge 91+ -- Safari and Mobile Safari 15+ - -## WebView - -Our SDK can run in Web View on Android and iOS. However, we recommend using the SDK in a browser for the best experience. diff --git a/packages/react-sdk/docusaurus/docs/React/01-basics/_category_.json b/packages/react-sdk/docusaurus/docs/React/01-basics/_category_.json deleted file mode 100644 index ece9ba735d..0000000000 --- a/packages/react-sdk/docusaurus/docs/React/01-basics/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "Setup" -} diff --git a/packages/react-sdk/docusaurus/docs/React/02-guides/01-client-auth.mdx b/packages/react-sdk/docusaurus/docs/React/02-guides/01-client-auth.mdx deleted file mode 100644 index 202f2c143e..0000000000 --- a/packages/react-sdk/docusaurus/docs/React/02-guides/01-client-auth.mdx +++ /dev/null @@ -1,208 +0,0 @@ ---- -id: client-auth -title: Client & Authentication -description: How to setup the client and authenticate ---- - -import { TokenSnippet } from '../../../shared/_tokenSnippet.jsx'; - -## Client & Auth - -Before joining a call, it is necessary to set up the video client. Here's a basic example: - -```ts -import { StreamVideoClient, User } from '@stream-io/video-react-sdk'; - -const user: User = { - id: 'sara', -}; -const apiKey = 'my-stream-api-key'; -const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'; -const client = new StreamVideoClient({ apiKey, token, user }); -``` - -- The API Key can be found in your dashboard. -- The user can be either authenticated, anonymous or guest. -- Note: You can store custom data on the user object, if required. - -Typically, you'll want to initialize the client when your application loads and use a context provider to make it available to the rest of your application. - -## Generating a token - -Tokens need to be generated server side. You can use our [server side SDKs](https://getstream.io/video/docs/api/authentication/) to quickly add support for this. -Typically, you integrate this into the part of your codebase where you log in or register users. -The tokens provide a way to authenticate a user or give access to a specific set of calls. - - - -:::note -For development purposes, you can use our [Token Generator](https://getstream.io/chat/docs/token_generator/). -::: - -## Different types of users - -- Authenticated users are users that have an account on your app. -- Guest users are temporary user accounts. You can use it to temporarily give someone a name and image when joining a call. -- Anonymous users are users that are not authenticated. It's common to use this for watching a livestream or similar where you aren't authenticated. - -This example shows the client setup for a guest user: - -```ts -import { StreamVideoClient, User } from '@stream-io/video-react-sdk'; - -const user: User = { - id: 'jack-guest', - type: 'guest', -}; -const apiKey = 'my-stream-api-key'; -const client = new StreamVideoClient({ apiKey, user }); -``` - -And here's an example for an anonymous user - -```ts -import { StreamVideoClient, User } from '@stream-io/video-react-sdk'; - -const user: User = { - type: 'anonymous', -}; - -const apiKey = 'my-stream-api-key'; -const client = new StreamVideoClient({ apiKey, user }); -``` - -Anonymous users don't establish an active web socket connection, therefore they won't receive any events. They are just able to watch a livestream or join a call. - -The token for an anonymous user should contain the `call_cids` field, which is an array of the call `cid`'s that the user is allowed to join. - -Here's an example JWT token payload for an anonymous user: - -```ts -{ - "iss": "@stream-io/dashboard", - "iat": 1726406693, - "exp": 1726493093, - "user_id": "!anon", - "role": "viewer", - "call_cids": [ - "livestream:123" - ] -} -``` - -## Client options - -### `token` or `tokenProvider` - -To authenticate users you can either provide a string `token` or a `tokenProvider` function that returns `Promise`. -If you use the `tokenProvider` the SDK will automatically call the provider whenever the token is expired. - -```typescript -import { StreamVideoClient, User } from '@stream-io/video-react-sdk'; - -const tokenProvider = async () => { - const response = await fetch('/api/token'); - const data = await response.json(); - return data.token; -}; - -const user: User = { - id: 'sara', -}; -const apiKey = 'my-stream-api-key'; -const client = new StreamVideoClient({ apiKey, tokenProvider, user }); -``` - -### Logging - -You can configure the log level and the logger method used by the SDK. - -The default log level is `warn`, other options are: `trace`, `debug`, `info`, and `error`. - -The default logger method will log to the browser's `console`. - -```ts -import { StreamVideoClient, Logger } from '@stream-io/video-react-sdk'; - -const myLogger: Logger = (logLevel, message, ...args) => { - // Do something with the log message -}; - -const client = new StreamVideoClient({ - apiKey, - token, - user, - options: { - logLevel: 'info', - logger: myLogger, - }, -}); -``` - -### Sentry - -Here is an example showing a basic [Sentry](https://sentry.io/welcome/) integration: - -```ts -import { LogLevel, Logger, logToConsole } from '@stream-io/video-react-sdk'; -import * as Sentry from '@sentry/nextjs'; - -const logLevelMapping = new Map(); -logLevelMapping.set('debug', 'debug'); -logLevelMapping.set('info', 'info'); -logLevelMapping.set('warn', 'warning'); -logLevelMapping.set('error', 'error'); - -export const customSentryLogger: Logger = ( - logLevel: LogLevel, - message: string, - ...args: unknown[] -) => { - if (logLevel === 'warn' || logLevel === 'error') { - Sentry.captureEvent({ - level: logLevelMapping.get(logLevel), - extra: args, - }); - } - - // Call the SDK's default log method - logToConsole(logLevel, message, { data: 'some data' }); -}; -``` - -## StreamVideo context provider - -You can use the `StreamVideo` context provider to make the SDK client available to the rest of the application. We also use the `tokenProvider` to show how to perform auth server-side. - -```tsx -import { useEffect, useState } from 'react'; -import { - StreamVideo, - StreamVideoClient, - User, -} from '@stream-io/video-react-sdk'; - -const apiKey = 'my-stream-api-key'; -const user: User = { - id: 'sara', -}; - -export const MyApp = () => { - const [client, setClient] = useState(); - useEffect(() => { - const tokenProvider = () => Promise.resolve(''); - const myClient = new StreamVideoClient({ apiKey, user, tokenProvider }); - setClient(myClient); - return () => { - myClient.disconnectUser(); - setClient(undefined); - }; - }, []); - - return ( - - - - ); -}; -``` diff --git a/packages/react-sdk/docusaurus/docs/React/02-guides/02-joining-creating-calls.mdx b/packages/react-sdk/docusaurus/docs/React/02-guides/02-joining-creating-calls.mdx deleted file mode 100644 index 9872c7758c..0000000000 --- a/packages/react-sdk/docusaurus/docs/React/02-guides/02-joining-creating-calls.mdx +++ /dev/null @@ -1,278 +0,0 @@ ---- -id: joining-and-creating-calls -title: Joining & Creating Calls -description: An overview of how to create calls and join them ---- - -This guide shows how to create, join, leave, and end calls and ring calls. - -## Call - -`Call` represents the main building block of our SDK. This object abstracts away the user actions, join flows and exposes the call state. - -### Create call - -You can create a call by specifying its `callType` and `callId`: - -The [Call Type](../../guides/configuring-call-types) controls which features are enabled, and sets up permissions. -You can reuse the same call multiple times. As an example, if you're building a telemedicine app calls will be connected to an appointment. -Using your own appointment id as the call id makes it easy to find the call later. - -```typescript -const callType = 'default'; -const callId = 'test-call'; - -const call = client.call(callType, callId); -await call.getOrCreate(); - -// or create it with options: -await call.getOrCreate({ - data: { - /* call creation options */ - }, -}); -``` - -See all possible options at the [Call creation options section](#call-creation-options). - -### Join call - -```typescript -const callType = 'default'; -const callId = 'test-call'; - -const call = client.call(callType, callId); -await call.join(); -``` - -### Create and join a call - -For convenience, you can create and join a call in a single operation. One of the flags you can provide there is `create`. -Set this to `true` if you want to enable creating new call. Set it to `false` if you only want to join an existing call. - -See all possible options at the [Call creation options section](#call-creation-options). - -```typescript -await call.join({ - create: true, - data: { - /* call creation options */ - }, -}); -``` - -### Join with mic and camera on or off - -You can override the default mic and camera settings before you join a call. -Typically, you should configure this in your Lobby view: - -```typescript -const call = client.call('default', 'test-call'); - -// enable mic and camera -await call.camera.enable(); -await call.microphone.enable(); - -// alternatively, you can disable them -await call.camera.disable(); -await call.microphone.disable(); - -// and then join the call -await call.join(); -``` - -### Leave call - -To leave a call, you can use the `leave` method: - -```typescript -await call.leave(); -``` - -### End call - -Ending a call requires a [special permission](../../guides/permissions-and-moderation). This action terminates the call for everyone. - -```typescript -await call.endCall(); -``` - -Only users with a special permission (`OwnCapability.JOIN_ENDED_CALL`) can join an ended call. - -### Load call - -Existing calls can be loaded through the following API: - -```typescript -const call = client.call(type, id); -await call.get(); // just load - -await call.getOrCreate(); // create if not present and load it -``` - -These operations initialize the `call.state` and create a subscription for call updates to our backend. -This means that this `call` instance will receive real-time updates in case it is modified somewhere else. - -Read more about call state here: [Call & Participant State](../../guides/call-and-participant-state/). - -### Update call - -After creating a call, you can update some of its properties: - -```typescript -import { RecordSettingsRequestModeEnum } from '@stream-io/video-react-sdk'; - -await call.update({ - custom: { color: 'green' }, - settings_override: { - recording: { - mode: RecordSettingsRequestModeEnum.DISABLED, - }, - }, -}); -``` - -## Call creation options - -The following options are supported when creating a call: - -| Option | Description | Default | -| ---------- | --------------------------------------------------------------------------------------------------------------- | ------- | -| `members` | A list of members to add to this call. You can specify the role and custom data on these members | - | -| `custom` | Any custom data you want to store | - | -| `settings` | You can overwrite certain call settings for this specific call. This overwrites the call type standard settings | - | -| `startsAt` | When the call will start. Used for calls scheduled in the future, livestreams, audio rooms etc | - | -| `team` | Restrict the access to this call to a specific team | - | -| `ring` | If you want the call to ring for each member | `false` | -| `notify` | If you want the call to nofiy each member by sending push notification. | `false` | - -### Set call members - -```typescript -const call = client.call(type, id); -await call.getOrCreate({ - data: { - members: [{ user_id: 'alice', role: 'admin' }, { user_id: 'bob' }], - }, -}); -``` - -### Update call members - -```typescript -await call.updateCallMembers({ - update_members: [{ user_id: 'charlie', role: 'admin' }], - remove_members: ['alice'], -}); -``` - -### Custom call data - -```typescript -await call.getOrCreate({ - data: { - custom: { color: 'blue' }, - }, -}); -``` - -### Settings override - -By default, the `call` instances inherit the settings defined in the call type. -In some cases, you might want to override call settings on the instance itself: - -```typescript -// at creation time -await call.getOrCreate({ - data: { - settings_override: { - audio: { mic_default_on: false }, - video: { camera_default_on: false }, - }, - }, -}); - -// or later -await call.update({ - settings_override: { - video: { camera_default_on: true }, - }, -}); -``` - -### Backstage setup - -The backstage feature makes it easy to build a use-case where you and your co-hosts can set up your camera before going live. -Only after you call `call.goLive()` the regular users will be allowed to join the livestream. - -However, you can also specify a `join_ahead_time_seconds`, -which will allow regular users to join the livestream before the call is live, in the specified join time before the stream starts. - -Here's an example of how to do that: - -```typescript -await call.getOrCreate({ - data: { - starts_at: new Date(Date.now() + 500 * 1000), // 500 seconds from now - settings_override: { - backstage: { - enabled: true, - join_ahead_time_seconds: 300, - }, - }, - }, -}); -``` - -In the code snippet above, we are creating a call that starts 500 seconds from now. -We are also enabling backstage mode, with a `join_ahead_time_seconds` of 300 seconds. -That means that regular users will be able to join the call 200 seconds from now. - -## Restricting access - -You can restrict access to a call by tweaking the [Call Type](../../guides/configuring-call-types/) permissions and roles. -A typical use case is to restrict access to a call to a specific set of users -> call members. - -#### Step 1: Set up the roles and permissions - -On our [dashboard](https://dashboard.getstream.io/), navigate to the **Video & Audio -> Roles & Permissions** section and select the appropriate role and scope. -In this example, we will use `my-call-type` scope. - -By default, all users unless specified otherwise, have the `user` role. - -We start by removing the `JoinCall` permission from the `user` role for the `my-call-type` scope. -It will prevent regular users from joining a call of this type. - -![Revoke JoinCall for user role](../assets/02-guides/02-joining-creating-calls/user-revoke-joincall.png) - -Next, let's ensure that the `call_member` role has the `JoinCall` permission for the `my-call-type` scope. -It will allow users with the `call_member` role to join a call of this type. - -![Grant JoinCall for call_member role](../assets/02-guides/02-joining-creating-calls/call_member-grant-joincall.png) - -Once this is set, we can proceed with setting up a `call` instance. - -#### Step 2: Set up the call - -```typescript -const call = client.call('my-call-type', 'my-call-id'); -await call.getOrCreate({ - data: { - members: [ - // please note the `role` property - { user_id: 'alice', role: 'call_member' }, - { user_id: 'bob', role: 'call_member' }, - ], - }, -}); - -// and if necessary, to grant access to more users -await call.updateCallMembers({ - update_members: [{ user_id: 'charlie', role: 'call_member' }], -}); - -// or, to remove access from some users -await call.updateCallMembers({ - remove_members: ['charlie'], -}); -``` diff --git a/packages/react-sdk/docusaurus/docs/React/02-guides/03-call-and-participant-state.mdx b/packages/react-sdk/docusaurus/docs/React/02-guides/03-call-and-participant-state.mdx deleted file mode 100644 index d0d703b5d8..0000000000 --- a/packages/react-sdk/docusaurus/docs/React/02-guides/03-call-and-participant-state.mdx +++ /dev/null @@ -1,265 +0,0 @@ ---- -title: Call & Participant State -description: How the state is exposed ---- - -You can access call, participant and client state using hooks. These hooks are reactive (their value is updated on WebSocket events and API calls). - -## Call State - -To observe call state, you need to provide a `Call` instance to the [`StreamCall` component](../../ui-components/core/stream-call). - -:::note -For the best experience, please make sure that the provided `Call` instance is loaded -and connected to our backend: [Load Call](../../joining-and-creating-calls/#load-call). - -Otherwise, `call.state` and the call state hooks will provide empty values. -::: - -Let's see an example where we use the `useCall`, `useCallCallingState` and `useParticipants` hooks to display some basic information about the call: - -```tsx -import { - Call, - StreamCall, - useCall, - useCallStateHooks, -} from '@stream-io/video-react-sdk'; - -export default function MyApp() { - let call: Call; - - return ( - - - - ); -} - -const MyCallUI = () => { - const call = useCall(); - - const { useCallCallingState, useParticipants } = useCallStateHooks(); - const callingState = useCallCallingState(); - const participants = useParticipants(); - - return ( -
-
Call: {call?.cid}
-
State: {callingState}
-
Participants: {participants.length}
-
- ); -}; -``` - -This approach makes it possible to access the call state and be notified about changes anywhere in your application without having to manually subscribe to WebSocket events. - -The `StreamCall` component is a context provider that makes the call state available to all child components. -The `useCall` hook returns the `Call` instance that is registered with `StreamCall`. You need the `Call` instance to initiate API calls. - -## Call State Hooks - -Here is an excerpt of the available call state hooks: - -| Name | Description | -| ------------------------------------ | ------------------------------------------------------------------------------------------------------------- | -| `useCall` | The `Call` instance that is registered with `StreamCall`. You need the `Call` instance to initiate API calls. | -| `useCallBlockedUserIds` | The list of blocked user IDs. | -| `useCallCallingState` | Provides information about the call state. For example, `RINGING`, `JOINED` or `RECONNECTING`. | -| `useCallCreatedAt` | The time the call was created. | -| `useCallCreatedBy` | The user that created the call. | -| `useCallCustomData` | The custom data attached to the call. | -| `useCallEgress` | The egress information of the call. | -| `useCallEndedBy` | The user that ended the call. | -| `useCallIngress` | The ingress information of the call. | -| `useCallMembers` | The list of call members | -| `useCallSession` | The information for the current call session. | -| `useCallSettings` | The settings of the call. | -| `useCallStartedAt` | The actual start time of the current call session. | -| `useCallStartsAt` | The scheduled start time of the call. | -| `useCallStatsReport` | When stats gathering is enabled, this observable will emit a new value at a regular (configurable) interval. | -| `useCallThumbnail` | The thumbnail of the call. | -| `useCallUpdatedAt` | The time the call was last updated. | -| `useCameraState` | The camera state of the local participant. | -| `useDominantSpeaker` | The participant that is the current dominant speaker of the call. | -| `useHasOngoingScreenShare` | It will return `true` if at least one participant is sharing their screen. | -| `useHasPermissions` | Returns `true` if the local participant has all the given permissions. | -| `useIsCallHLSBroadcastingInProgress` | It's `true` if the call is being broadcasted in HLS mode. | -| `useIsCallLive` | It's `true` if the call is currently live. | -| `useIsCallRecordingInProgress` | It's' `true` if the call is being recorded. | -| `useIsCallTranscribingInProgress` | It's `true` if the call is being transcribed. | -| `useMicrophoneState` | The microphone state of the local participant. | -| `useOwnCapabilities` | The capabilities of the local participant. | -| `useScreenShareState` | The screen share state of the local participant. | -| `useSpeakerState` | The speaker state of the local participant. | - -In your IDE of choice, you can see the full list if you destructure the `useCallStateHooks` object: - -```ts -import { useCallStateHooks } from '@stream-io/video-react-sdk'; - -const { - useCallMembers, - useDominantSpeaker, - useParticipants, - useLocalParticipant, - useIsCallRecordingInProgress, - // ... -} = useCallStateHooks(); -``` - -## Participant state - -If you want to display information about the joined participants of the call you can use these hooks: - -| Name | Description | -| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `useLocalParticipant` | The local participant is the logged-in user. | -| `useRemoteParticipants` | All participants except the local participant. | -| `useParticipants` | All participants, including local and remote participants. | -| `useParticipantCount` | The approximate participant count of the active call. This includes the [anonymous users](../client-auth/#anonymous-users) as well, it is computed on the server-side. | -| `useAnonymousParticipantCount` | The approximate participant count of anonymous users in the active call. | - -```tsx -import { useCallStateHooks, StreamCall } from '@stream-io/video-react-sdk'; - -export default function App() { - let call: call; - - return ( - - - - ); -} - -const MyCallUI = () => { - const { useLocalParticipant, useParticipantCount } = useCallStateHooks(); - const participantCount = useParticipantCount(); - const localParticipant = useLocalParticipant(); - - return ( -
-
Number of participants: {participantCount}
-
Session ID: {localParticipant.sessionId}
-
- ); -}; -``` - -The `StreamVideoParticipant` object contains the following information: - -| Name | Description | -| ------------------------- | -------------------------------------------------------------------------------- | -| `audioLevel` | The audio level of the participant (determined on the server). | -| `audioStream` | The published audio `MediaStream`. | -| `audioVolume` | The audio volume level of the participant (overridable local audioVolume level). | -| `connectionQuality` | The participant's connection quality. | -| `custom` | The participant's custom data. Comes from the `custom` field of the user object. | -| `image` | The image of the participant. | -| `isDominantSpeaker` | It's `true` if the participant is the current dominant speaker in the call. | -| `isLocalParticipant` | It's `true` if the participant is the local participant. | -| `isSpeaking` | It's `true` if the participant is currently speaking. | -| `joinedAt` | The time the participant joined the call. | -| `name` | The name of the participant. | -| `pin` | Holds pinning information. | -| `publishedTracks` | The track types the participant is currently publishing | -| `reaction` | The last reaction this user has sent to this call. | -| `roles` | The roles of the participant in this call. | -| `sessionId` | The identifier of the participant within the existing call session | -| `screenShareAudioStream` | The published screen share audio `MediaStream`. | -| `screenShareStream` | The published screen share `MediaStream`. | -| `userId` | The user ID of the participant. | -| `videoStream` | The published video `MediaStream`. | -| `viewportVisibilityState` | The viewport visibility state of the participant. | - -The SDK also provides a few utility functions that help you to work with participants: - -```ts -import { - hasAudio, - hasVideo, - hasScreenShare, - hasScreenShareAudio, - isPinned, - useCallStateHooks, -} from '@stream-io/video-react-sdk'; - -// example usage -const { useParticipants } = useCallStateHooks(); -const participants = useParticipants(); - -// check if the participant has audio, video, screen share or screen share audio -const [participant] = participants; -const hasAudioOn = hasAudio(participant); -const hasVideoOn = hasVideo(participant); -const hasScreenShareOn = hasScreenShare(participant); -const hasScreenShareAudioOn = hasScreenShareAudio(participant); -const isPinnedOn = isPinned(participant); - -// participants with a specific role -const hosts = participants.filter((p) => p.roles.includes('host')); - -// participants that publish video and audio -const videoParticipants = participants.filter( - (p) => hasVideo(p) && hasAudio(p), -); -``` - -In a call with many participants, the list returned by the `useParticipants` call state hook is truncated to 250 participants. The participants who are publishing video, audio, or screen sharing have priority over the other participants in the list. This means, for example, that in a livestream with one host and many viewers, the host is guaranteed to be in the list. - -## Client state - -To observe client state you need to provide a `StreamVideoClient` instance to the `StreamVideo` context provider. -If you want to observe the connected user you can use the `useConnectedUser` hook. - -Let's see an example: - -```tsx -import { - useConnectedUser, - StreamVideo, - StreamVideoClient, -} from '@stream-io/video-react-sdk'; - -export default function App() { - let client: StreamVideoClient; - - return ( - - - - ); -} - -const MyHeader = () => { - const user = useConnectedUser(); - return
{user ? `Logged in: ${user.name}` : 'Logged out'}
; -}; -``` - -This approach makes it possible to access the client state and be notified about changes anywhere in your application without having to manually subscribe to WebSocket events. - -Here is the list of client-state hooks: - -| Name | Description | -| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `useStreamVideoClient` | The `StreamVideoClient` instance. | -| `useConnectedUser` | Returns the connected user. Holds the server-side data of the connected user. | -| `useCalls` | A list of all tracked calls. These calls can be outgoing (I have called somebody) or incoming (somebody has called me). Loaded calls (`call.get()`) are also part of this list. | - -The `connectedUser` object contains the following properties: - -| Name | Description | -| ------------ | ----------------------------------------------------- | -| `created_at` | The time the user was created. | -| `custom` | Custom user data. | -| `deleted_at` | The time the user was deleted. | -| `devices` | The registered push notification devices of the user. | -| `id` | The id of the user. | -| `image` | The profile image of the user. | -| `name` | The name of the user. | -| `role` | The role of the user. | -| `teams` | The teams the user belongs to. | -| `updated_at` | The time when the user was updated. | diff --git a/packages/react-sdk/docusaurus/docs/React/02-guides/03-calling-state-and-lifecycle.mdx b/packages/react-sdk/docusaurus/docs/React/02-guides/03-calling-state-and-lifecycle.mdx deleted file mode 100644 index 7b9200b433..0000000000 --- a/packages/react-sdk/docusaurus/docs/React/02-guides/03-calling-state-and-lifecycle.mdx +++ /dev/null @@ -1,159 +0,0 @@ ---- -id: calling-state-and-lifecycle -title: Calling State and Lifecycle -description: Calling State machine and Call Lifecycle. ---- - -The `call` object instance manages everything related to a particular call instance, such as: - -- creating and joining a call -- performing actions (mute, unmute, send reaction, etc...) -- manage event subscriptions (`call.on('call.session_started', callback)`, etc...) -- and many more - -Every `call` instance should be created through the `client.call(type, id)` helper. - -Our `StreamVideoClient` is responsible for maintaining a WebSocket connection to our servers and also takes care about the API calls that are proxied from the `call` instance. - -As we learned in [Joining and Creating Calls](../../guides/joining-and-creating-calls/) guide, a call instance is managed like this: - -```ts -import { Call, StreamVideoClient } from '@stream-io/video-react-sdk'; - -let client: StreamVideoClient; // ... - -const call: Call = client.call(type, id); - -// load existing call information from our servers -await call.get(); - -// Creates the call on our servers in case it doesn't exist. Otherwise, -// loads the call information from our servers. -await call.getOrCreate(); - -// join the call -await call.join(); - -// leave the call and dispose all allocated resources -await call.leave(); -``` - -Every `call` instance has a local state, exposed to integrators through: - -- `call.state.callingState` - a getter that returns the current value -- `call.state.callingState$` - an observable that an integrator can subscribe to and be notified everytime the value changes -- `useCallCallingState()` - a [call state hook](../../guides/call-and-participant-state/#call-state-hooks) that makes it easy to read and update the UI based on calling state values in React components. - -## Call Instance - -The call instance is a stateful resource that you acquire with `client.call()` and must dispose of with `call.leave()`. Failure to dispose of the call properly can result in memory leaks and unexpected behavior. - -In practice, this means that: - -1. You should only create call instances in effects. -2. Effects that create a call instance should have a `call.leave()` cleanup. - -```ts -const [call, setCall] = useState(undefined); - -useEffect(() => { - const myCall = client.call(callType, callId); - myCall.join({ create: true }).then( - () => setCall(myCall), - () => console.error('Failed to join the call'), - ); - - return () => { - myCall.leave().catch(() => console.error('Failed to leave the call')); - setCall(undefined); - }; -}, [callType, callId]); -``` - -To join the same call again, you can reuse the same call instance, or create a new one using `client.call(type, id)`. - -## Calling State - -Every `call` instance has its own local state managed by the SDK. - -These values are exposed through the `CallingState` enum: - -```ts -import { CallingState, useCallStateHooks } from '@stream-io/video-react-sdk'; - -const { useCallCallingState } = useCallStateHooks(); -const callingState = useCallCallingState(); - -switch (callingState) { - case CallingState.JOINED: - // ... - break; - default: - const exhaustiveCheck: never = callingState; - throw new Error(`Unknown calling state: ${exhaustiveCheck}`); -} -``` - -:::note -As `CallingState` is an enum that can be extended at any time by us, it would be good to make sure you -use it exhaustively. This way, if you use TypeScript, you can get a compile time error and be notified that -there are few more states that you should handle. -::: - -### Calling States - -| State | Description | -| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `CallingState.UNKNOWN` | The state is unknown. This value is set when Calling State isn't initialized properly. | -| `CallingState.IDLE` | A call instance is created on the client side but a WebRTC session isn't established yet. | -| `CallingState.RINGING` | This is an incoming (ring) call. You are the callee. | -| `CallingState.JOINING` | The call join flow is executing (typically right after `call.join()`). Our systems are preparing to accept the new call participant. | -| `CallingState.JOINED` | The join flow has finished successfully and the current participant is part of the call. The participant can receive and publish audio and video. | -| `CallingState.LEFT` | The call has been left (`call.leave()`) and all allocated resources are released. Please create a new `call` instance if you want to re-join. | -| `CallingState.RECONNECTING` | A network connection has been lost (due to various factors) and the `call` instance attempts to re-establish a connection and resume the call. | -| `CallingState.RECONNECTING_FAILED` | The SDK failed to recover the connection after a couple of consecutive attempts. You need to inform the user that he needs to go online and manually attempt to rejoin the call. | -| `CallingState.MIGRATING` | The SFU node that is hosting the current participant is shutting down or tries to rebalance the load. This `call` instance is being migrated to another SFU node. | -| `CallingState.OFFLINE` | No network connection can be detected. Once the connection restores, the SDK will automatically attempt to recover the connection (signalled with `RECONNECTING` state). | - -### Example handling - -To understand these values better, here is a hypothetical example of how these values can be mapped: - -```tsx -import { CallingState, useCallStateHooks } from '@stream-io/video-react-sdk'; - -const { useCallCallingState } = useCallStateHooks(); -const callingState = useCallCallingState(); - -switch (callingState) { - case CallingState.UNKNOWN: - case CallingState.IDLE: - return ; - - case CallingState.RINGING: - return ; - - case CallingState.JOINING: - return ; - - case CallingState.JOINED: - return ; - - case CallingState.LEFT: - return ; - - case CallingState.RECONNECTING: - case CallingState.MIGRATING: - return ; - - case CallingState.RECONNECTING_FAILED: - return ; - - case CallingState.OFFLINE: - return ; - - default: - const exhaustiveCheck: never = callingState; - throw new Error(`Unknown calling state: ${exhaustiveCheck}`); -} -``` diff --git a/packages/react-sdk/docusaurus/docs/React/02-guides/04-camera-and-microphone.mdx b/packages/react-sdk/docusaurus/docs/React/02-guides/04-camera-and-microphone.mdx deleted file mode 100644 index 0753b28f41..0000000000 --- a/packages/react-sdk/docusaurus/docs/React/02-guides/04-camera-and-microphone.mdx +++ /dev/null @@ -1,396 +0,0 @@ ---- -id: camera-and-microphone -title: Camera & Microphone -description: Docs on the media manager ---- - -Handling audio and video devices in a web application means working with [`MediaStream`](https://developer.mozilla.org/en-US/docs/Web/API/MediaStream), -[`MediaDeviceInfo`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo) and other WebRTC API objects. -We did our best to hide this complexity through a set of APIs exposed as through a `call` instance, or a set of utility hooks. - -## Camera management - -The SDK does its best to make working with the camera easy. We expose the following objects on the call: - -### Call settings - -The default state of the camera is determined by the [call type](../../guides/configuring-call-types/) settings: - -```tsx -import { useCallStateHooks } from '@stream-io/video-react-sdk'; - -const { useCallSettings } = useCallStateHooks(); -const settings = useCallSettings(); - -console.log(settings?.video.camera_default_on); -``` - -### Start-stop camera - -```tsx -import { useCallStateHooks } from '@stream-io/video-react-sdk'; - -const { useCameraState } = useCallStateHooks(); -const { camera, isMute } = useCameraState(); - -console.log(`Camera is ${isMute ? 'off' : 'on'}`); -await camera.toggle(); - -// or, alternatively -await camera.enable(); -await camera.disable(); -``` - -It's always best to await calls to `enable()`, `disable()`, and `toggle()`, however the SDK does its best to resolve potential race conditions: the last call always "wins", so it's safe to make these calls in an event handler. - -Status is updated once the camera is actually enabled or disabled. Use `optimisticIsMute` for the "optimistic" status that is updated immediately after toggling the camera. - -### List and select devices - -```typescript -import { useCallStateHooks } from '@stream-io/video-react-sdk'; - -const { useCameraState } = useCallStateHooks(); -const { camera, selectedDevice, devices } = useCameraState(); - -console.log('current camera id:', selectedDevice); -console.log('available devices:', devices); - -const preferredDevice = devices.find((d) => d.label === 'My Camera'); -await camera.select(preferredDevice.deviceId); -``` - -### Camera permissions - -```typescript -import { useCallStateHooks } from '@stream-io/video-react-sdk'; - -const { useCameraState } = useCallStateHooks(); -const { hasBrowserPermission } = useCameraState(); - -if (hasBrowserPermission) { - console.log('User has granted camera permissions!'); -} else { - console.log('User has denied or not granted camera permissions!'); -} -``` - -### Lobby preview - -Here is how to set up a video preview displayed before joining the call: - -```tsx -import { useCallStateHooks } from '@stream-io/video-react-sdk'; - -const { useCameraState } = useCallStateHooks(); -const { camera, mediaStream } = useCameraState(); - -// will turn on the camera -await camera.enable(); - -// play the video preview -