├── .editorconfig
├── .env.example
├── .eslintignore
├── .eslintrc
├── .gitattributes
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.yml
│ ├── feature_request.yml
│ └── questions.yml
├── PULL_REQUEST_TEMPLATE.md
├── actions
│ └── setup
│ │ └── action.yml
└── workflows
│ ├── build.yml
│ ├── gitleak.yml
│ ├── pr.yml
│ ├── publish.yml
│ ├── publishCDN.yml
│ ├── sync.yml
│ ├── typedoc.yml
│ ├── update-doc.yml
│ └── upgrade-dep.yml
├── .gitignore
├── .gitleaks.toml
├── .husky
├── commit-msg
└── pre-commit
├── .nvmrc
├── .prettierignore
├── .prettierrc
├── .vscode
├── extensions.json
└── settings.json
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── README.zh-CN.md
├── cspell.json
├── examples
├── basic
│ ├── .env.example
│ ├── README.md
│ ├── index.html
│ ├── index.scss
│ ├── main.tsx
│ ├── package.json
│ ├── src
│ │ ├── App.tsx
│ │ ├── components
│ │ │ ├── AutoLayout.tsx
│ │ │ ├── Client.tsx
│ │ │ ├── Container.tsx
│ │ │ ├── Label.tsx
│ │ │ ├── LocalTracks.tsx
│ │ │ ├── MediaControl.tsx
│ │ │ ├── RemoteUsers.tsx
│ │ │ ├── Room.tsx
│ │ │ ├── ScreenShare.tsx
│ │ │ ├── ToolBox.tsx
│ │ │ ├── UsersInfo.tsx
│ │ │ └── index.ts
│ │ ├── pages
│ │ │ ├── advanced
│ │ │ │ ├── index.ts
│ │ │ │ ├── multi-channel
│ │ │ │ │ ├── index.scss
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── store.ts
│ │ │ │ ├── share-screen
│ │ │ │ │ └── index.tsx
│ │ │ │ └── switch-layout
│ │ │ │ │ ├── index.scss
│ │ │ │ │ └── index.tsx
│ │ │ ├── basic
│ │ │ │ ├── index.ts
│ │ │ │ └── overview
│ │ │ │ │ └── index.tsx
│ │ │ ├── component
│ │ │ │ ├── LocalAudioTrack
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── LocalUser
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── LocalVideoTrack
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── RemoteAudioTrack
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── RemoteUser
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── RemoteVideoTrack
│ │ │ │ │ └── index.tsx
│ │ │ │ └── index.ts
│ │ │ ├── hook
│ │ │ │ ├── index.ts
│ │ │ │ ├── useAutoPlayAudioTrack
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── useAutoPlayVideoTrack
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── useClientEvent
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── useConnectionState
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── useCurrentUID
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── useIsConnected
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── useJoin
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── useLocalScreenTrack
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── useNetworkQuality
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── usePublish
│ │ │ │ │ └── index.tsx
│ │ │ │ └── useRemoteUsers
│ │ │ │ │ └── index.tsx
│ │ │ ├── index.ts
│ │ │ └── setting
│ │ │ │ └── index.tsx
│ │ └── utils
│ │ │ ├── const.ts
│ │ │ ├── fake.ts
│ │ │ └── index.ts
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ ├── vite-env.d.ts
│ └── vite.config.ts
└── mobx
│ ├── .env.example
│ ├── README.md
│ ├── index.html
│ ├── index.scss
│ ├── package.json
│ ├── src
│ ├── components
│ │ ├── App.tsx
│ │ ├── Controls.tsx
│ │ ├── Home.tsx
│ │ ├── LocalUser.tsx
│ │ ├── Room.tsx
│ │ ├── RoomInfo.tsx
│ │ ├── ShareScreenTracks.tsx
│ │ ├── Users.tsx
│ │ └── buttons.tsx
│ ├── constants.ts
│ ├── global.d.ts
│ ├── main.tsx
│ ├── stores
│ │ ├── app.store.ts
│ │ ├── local-user.store.ts
│ │ ├── remote-user.store.ts
│ │ ├── share-screen.store.ts
│ │ └── users.store.ts
│ ├── styles.css
│ ├── utils.ts
│ └── vite-env.d.ts
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
├── package.json
├── packages
├── agora-rtc-react-ui
│ ├── .storybook
│ │ ├── main.js
│ │ ├── preview-head.html
│ │ └── preview.js
│ ├── package.json
│ ├── src
│ │ ├── assets
│ │ │ └── UserControl.css
│ │ ├── components
│ │ │ ├── CameraControl.tsx
│ │ │ ├── CameraVideoTrack.tsx
│ │ │ ├── LocalMicrophoneAndCameraUser.tsx
│ │ │ ├── MicControl.tsx
│ │ │ ├── MicrophoneAudioTrack.tsx
│ │ │ ├── RemoteVideoPlayer.tsx
│ │ │ ├── icons
│ │ │ │ ├── SVGCamera.tsx
│ │ │ │ ├── SVGCameraMute.tsx
│ │ │ │ ├── SVGMicrophone.tsx
│ │ │ │ ├── SVGMicrophoneMute.tsx
│ │ │ │ └── index.ts
│ │ │ └── index.ts
│ │ ├── index.ts
│ │ ├── stories
│ │ │ ├── CameraControl.stories.tsx
│ │ │ ├── CameraVideoTrack.stories.tsx
│ │ │ ├── Devices.stories.tsx
│ │ │ ├── LocalMicrophoneAndCameraUser.stories.tsx
│ │ │ ├── MicControl.stories.tsx
│ │ │ ├── MicrophoneAudioTrack.stories.tsx
│ │ │ └── RemoteVideoPlayer.stories.tsx
│ │ └── vite-env.d.ts
│ ├── test
│ │ ├── CameraVideoTrack.test.tsx
│ │ ├── LocalMicrophoneAndCameraUser.test.tsx
│ │ ├── MicrophoneAudioTrack.test.tsx
│ │ ├── RemoteVideoPlayer.test.tsx
│ │ └── setup.tsx
│ ├── tsconfig.json
│ ├── tsup.config.ts
│ └── vite.config.ts
├── agora-rtc-react
│ ├── .storybook
│ │ ├── main.ts
│ │ ├── preview-head.html
│ │ └── preview.ts
│ ├── docs
│ │ ├── components-en.mdx
│ │ ├── components.mdx
│ │ ├── components
│ │ │ ├── AgoraRTCProvider.en-US.mdx
│ │ │ ├── AgoraRTCProvider.zh-CN.mdx
│ │ │ ├── AgoraRTCScreenShareProvider.en-US.mdx
│ │ │ ├── AgoraRTCScreenShareProvider.zh-CN.mdx
│ │ │ ├── LocalAudioTrack.en-US.mdx
│ │ │ ├── LocalAudioTrack.zh-CN.mdx
│ │ │ ├── LocalUser.en-US.mdx
│ │ │ ├── LocalUser.zh-CN.mdx
│ │ │ ├── LocalVideoTrack.en-US.mdx
│ │ │ ├── LocalVideoTrack.zh-CN.mdx
│ │ │ ├── RemoteAudioTrack.en-US.mdx
│ │ │ ├── RemoteAudioTrack.zh-CN.mdx
│ │ │ ├── RemoteUser.en-US.mdx
│ │ │ ├── RemoteUser.zh-CN.mdx
│ │ │ ├── RemoteVideoTrack.en-US.mdx
│ │ │ └── RemoteVideoTrack.zh-CN.mdx
│ │ ├── data-types-en.mdx
│ │ ├── data-types.mdx
│ │ ├── data-types
│ │ │ ├── AgoraRTCReactError.en-US.mdx
│ │ │ ├── AgoraRTCReactError.zh-CN.mdx
│ │ │ ├── JoinOptions.en-US.mdx
│ │ │ ├── JoinOptions.zh-CN.mdx
│ │ │ ├── NetworkQuality.en-US.mdx
│ │ │ └── NetworkQualityEx.zh-CN.mdx
│ │ ├── hooks-en.mdx
│ │ ├── hooks.mdx
│ │ └── hooks
│ │ │ ├── useAutoPlayAudioTrack.en-US.mdx
│ │ │ ├── useAutoPlayAudioTrack.zh-CN.mdx
│ │ │ ├── useAutoPlayVideoTrack.en-US.mdx
│ │ │ ├── useAutoPlayVideoTrack.zh-CN.mdx
│ │ │ ├── useClientEvent.en-US.mdx
│ │ │ ├── useClientEvent.zh-CN.mdx
│ │ │ ├── useConnectionState.en-US.mdx
│ │ │ ├── useConnectionState.zh-CN.mdx
│ │ │ ├── useCurrentUID.en-US.mdx
│ │ │ ├── useCurrentUID.zh-CN.mdx
│ │ │ ├── useIsConnected.en-US.mdx
│ │ │ ├── useIsConnected.zh-CN.mdx
│ │ │ ├── useJoin.en-US.mdx
│ │ │ ├── useJoin.zh-CN.mdx
│ │ │ ├── useLocalCameraTrack.en-US.mdx
│ │ │ ├── useLocalCameraTrack.zh-CN.mdx
│ │ │ ├── useLocalMicrophoneTrack.en-US.mdx
│ │ │ ├── useLocalMicrophoneTrack.zh-CN.mdx
│ │ │ ├── useLocalScreenTrack.en-US.mdx
│ │ │ ├── useLocalScreenTrack.zh-CN.mdx
│ │ │ ├── useNetworkQuality.en-US.mdx
│ │ │ ├── useNetworkQuality.zh-CN.mdx
│ │ │ ├── usePublish.en-US.mdx
│ │ │ ├── usePublish.zh-CN.mdx
│ │ │ ├── useRTCClient.en-US.mdx
│ │ │ ├── useRTCClient.zh-CN.mdx
│ │ │ ├── useRemoteAudioTracks.en-US.mdx
│ │ │ ├── useRemoteAudioTracks.zh-CN.mdx
│ │ │ ├── useRemoteUserTrack.en-US.mdx
│ │ │ ├── useRemoteUserTrack.zh-CN.mdx
│ │ │ ├── useRemoteUsers.en-US.mdx
│ │ │ ├── useRemoteUsers.zh-CN.mdx
│ │ │ ├── useRemoteVideoTracks.en-US.mdx
│ │ │ ├── useRemoteVideoTracks.zh-CN.mdx
│ │ │ ├── useTrackEvent.en-US.mdx
│ │ │ ├── useTrackEvent.zh-CN.mdx
│ │ │ ├── useVolumeLevel.en-US.mdx
│ │ │ └── useVolumeLevel.zh-CN.mdx
│ ├── package.json
│ ├── src
│ │ ├── assets
│ │ │ └── styles.ts
│ │ ├── components
│ │ │ ├── LocalAudioTrack.tsx
│ │ │ ├── LocalUser.tsx
│ │ │ ├── LocalVideoTrack.tsx
│ │ │ ├── RemoteAudioTrack.tsx
│ │ │ ├── RemoteUser.tsx
│ │ │ ├── RemoteVideoTrack.tsx
│ │ │ ├── TrackBoundary.tsx
│ │ │ ├── UserCover.tsx
│ │ │ └── index.ts
│ │ ├── error.ts
│ │ ├── hooks
│ │ │ ├── events.ts
│ │ │ ├── index.ts
│ │ │ ├── tools.ts
│ │ │ ├── types.ts
│ │ │ ├── useConnectionState.ts
│ │ │ ├── useCurrentUID.ts
│ │ │ ├── useIsConnected.ts
│ │ │ ├── useJoin.ts
│ │ │ ├── useLocalCameraTrack.ts
│ │ │ ├── useLocalMicrophoneTrack.ts
│ │ │ ├── useLocalScreenTrack.ts
│ │ │ ├── useNetworkQuality.ts
│ │ │ ├── usePublish.ts
│ │ │ ├── useRTCClient.tsx
│ │ │ ├── useRTCScreenShareClient.tsx
│ │ │ ├── useRemoteAudioTracks.ts
│ │ │ ├── useRemoteUserTrack.ts
│ │ │ ├── useRemoteUsers.ts
│ │ │ ├── useRemoteVideoTracks.ts
│ │ │ └── useVolumeLevel.ts
│ │ ├── index.ts
│ │ ├── misc
│ │ │ ├── listen.ts
│ │ │ ├── store.ts
│ │ │ └── utils.ts
│ │ ├── rtc.ts
│ │ ├── stories
│ │ │ ├── LocalAudioTrack.stories.tsx
│ │ │ ├── LocalUser.stories.tsx
│ │ │ ├── LocalVideoTrack.stories.tsx
│ │ │ ├── RemoteAudioTrack.stories.tsx
│ │ │ ├── RemoteUser.stories.tsx
│ │ │ ├── RemoteVideoTrack.stories.tsx
│ │ │ ├── TrackBoundary.stories.tsx
│ │ │ └── hooks
│ │ │ │ ├── useAutoPlayAudioTrack.en-US.mdx
│ │ │ │ ├── useAutoPlayVideoTrack.en-US.mdx
│ │ │ │ ├── useClientEvent.en-US.mdx
│ │ │ │ ├── useConnectionState.en-US.mdx
│ │ │ │ ├── useCurrentUID.en-US.mdx
│ │ │ │ ├── useIsConnected.en-US.mdx
│ │ │ │ ├── useJoin.en-US.mdx
│ │ │ │ ├── useLocalCameraTrack.en-US.mdx
│ │ │ │ ├── useLocalMicrophoneTrack.en-US.mdx
│ │ │ │ ├── useLocalScreenTrack.en-US.mdx
│ │ │ │ ├── useNetworkQuality.en-US.mdx
│ │ │ │ ├── usePublish.en-US.mdx
│ │ │ │ ├── useRTCClient.en-US.mdx
│ │ │ │ ├── useRemoteAudioTracks.en-US.mdx
│ │ │ │ ├── useRemoteUserTrack.en-US.mdx
│ │ │ │ ├── useRemoteUsers.en-US.mdx
│ │ │ │ ├── useRemoteVideoTracks.en-US.mdx
│ │ │ │ ├── useTrackEvent.en-US.mdx
│ │ │ │ └── useVolumeLevel.en-US.mdx
│ │ ├── types.ts
│ │ └── vite-env.d.ts
│ ├── test
│ │ ├── component
│ │ │ ├── LocalAudioTrack.test.tsx
│ │ │ ├── LocalUser.test.tsx
│ │ │ ├── LocalVideoTrack.test.tsx
│ │ │ ├── RemoteAudioTrack.test.tsx
│ │ │ ├── RemoteUser.test.tsx
│ │ │ ├── RemoteVideoTrack.test.tsx
│ │ │ └── TrackBoundary.test.tsx
│ │ ├── error.test.ts
│ │ └── hook
│ │ │ ├── useConnectionState.test.ts
│ │ │ ├── useCurrentUID.test.ts
│ │ │ ├── useIsConnected.test.ts
│ │ │ ├── useJoin.test.ts
│ │ │ ├── useLocalCameraTrack.test.ts
│ │ │ ├── useLocalMicrophoneTrack.test.ts
│ │ │ ├── useLocalScreenTrack.test.ts
│ │ │ ├── useNetworkQuality.test.ts
│ │ │ ├── usePublish.test.ts
│ │ │ ├── useRTCClient.test.ts
│ │ │ ├── useRemoteAudioTracks.test.ts
│ │ │ ├── useRemoteUserTrack.test.tsx
│ │ │ ├── useRemoteUsers.test.ts
│ │ │ ├── useRemoteVideoTracks.test.ts
│ │ │ └── useVolumeLevel.test.ts
│ ├── tsconfig.json
│ ├── tsup.config.ts
│ ├── typedoc.json
│ └── vite.config.ts
└── shared
│ ├── assets
│ └── storybook
│ │ └── global.scss
│ └── test
│ ├── setup.tsx
│ └── setup
│ ├── agora.tsx
│ └── wrapper.tsx
├── patches
└── seedrandom@3.0.5.patch
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
└── scripts
├── const.ts
├── copy-docs.ts
├── docs
├── api.ts
├── component.ts
├── data-types.ts
├── generate-docs.ts
├── generate-storybook-mdx.ts
├── update-readme.ts
└── utils.ts
├── publishCN
├── common.sh
├── rewrite-dep.sh
└── rewrite-example.sh
├── release
├── clean.ts
└── update-version.ts
├── renew.ts
├── tsup
└── set-globals.ts
├── upgrade-deps.ts
└── utils.ts
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | # https://docs.agora.io/en/video-calling/reference/manage-agora-account?platform=web#get-the-app-id
2 | AGORA_APPID=
3 |
4 | # https://docs.agora.io/en/video-calling/develop/integrate-token-generation
5 | # if Security is Disabled in the Agora Console, leave this to blank
6 | # if not you need to `npm run renew`
7 | AGORA_CERTIFICATE=
8 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | build/
2 | **/dist/*
3 | **/coverage/*
4 | **/node_modules/*
5 | **/storybook-static/*
6 | .snapshots/
7 | *.min.js
8 | **/typedoc/*
9 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.mdx linguist-detectable=false
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: "🐛 Bug report"
2 | description: Create a report to help us improve
3 | assignees: "guoxianzhe"
4 | body:
5 | - type: input
6 | id: version
7 | attributes:
8 | label: agora-rtc-react version
9 | validations:
10 | required: true
11 | - type: input
12 | id: version
13 | attributes:
14 | label: agora-rtc-sdk-ng version
15 | validations:
16 | required: true
17 | - type: textarea
18 | id: steps-to-reproduce
19 | attributes:
20 | label: Steps to reproduce
21 | description: |
22 | What do we need to do to make the bug happen? Clear and concise reproduction instructions are important for us to be able to triage your issue in a timely manner. Note that you can use [Markdown](https://guides.github.com/features/mastering-markdown/) to format lists and code.
23 | placeholder: Steps to reproduce
24 | validations:
25 | required: true
26 | - type: textarea
27 | id: expected
28 | attributes:
29 | label: What is your expected?
30 | validations:
31 | required: true
32 | - type: textarea
33 | id: system-info
34 | attributes:
35 | label: System Info
36 | description: Output of `npx envinfo --system --binaries --browsers`
37 | render: shell
38 | placeholder: e.g. System, Binaries, Browsers
39 | - type: textarea
40 | id: additional-comments
41 | attributes:
42 | label: Any additional comments?
43 | description: e.g. some background/context of how you ran into this bug.
44 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.yml:
--------------------------------------------------------------------------------
1 | name: "New feature proposal"
2 | description: Suggest an idea for this project
3 | labels: ["feature request"]
4 | body:
5 | - type: textarea
6 | id: problem-description
7 | attributes:
8 | label: What kind of problem does this feature solve?
9 | description: |
10 | Explain your use case, context, and rationale behind this feature request. More importantly, what is the **end user experience** you are trying to build that led to the need for this feature?
11 | validations:
12 | required: true
13 | - type: textarea
14 | id: proposed-API
15 | attributes:
16 | label: What does the proposed API look like?
17 | description: |
18 | Describe how you propose to solve the problem and provide code samples of how the API would work once implemented. Note that you can use [Markdown](https://guides.github.com/features/mastering-markdown/) to format your code blocks.
19 | placeholder: Assumed API
20 | validations:
21 | required: true
22 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/questions.yml:
--------------------------------------------------------------------------------
1 | name: "Questions and Help"
2 | description: Looking for help with your app?
3 | labels: ["help wanted"]
4 | body:
5 | - type: textarea
6 | id: problem-description
7 | attributes:
8 | label: What kind of problem do you need help?
9 | description: |
10 | Describe your problem in as much detail as possible so that we can help you better.
11 | validations:
12 | required: true
13 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
12 |
13 | ### What kind of pull request did you make?
14 |
15 | - [ ] New Feature
16 | - [ ] Bug fix
17 | - [ ] Test Case
18 | - [ ] Demo update
19 | - [ ] Other
20 |
21 | ### Related issue link
22 |
23 |
27 |
28 | ### Changelog
29 |
30 |
33 |
--------------------------------------------------------------------------------
/.github/actions/setup/action.yml:
--------------------------------------------------------------------------------
1 | name: Setup
2 | description: Setup Node.js and pnpm
3 |
4 | runs:
5 | using: composite
6 | steps:
7 | - name: Install pnpm 🤏🏻
8 | uses: pnpm/action-setup@v4
9 | with:
10 | version: latest
11 |
12 | - name: Setup Node 💚
13 | uses: actions/setup-node@v3
14 | with:
15 | node-version: 18
16 | cache: "pnpm"
17 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 | on:
3 | workflow_dispatch:
4 | push:
5 | branches:
6 | - main
7 | jobs:
8 | build-and-deploy:
9 | runs-on: ubuntu-latest
10 | environment: github-pages
11 | steps:
12 | - name: Checkout 🛎️
13 | uses: actions/checkout@v3
14 |
15 | - name: Setup 🛠️
16 | uses: ./.github/actions/setup
17 |
18 | - name: Build 🔧
19 | env:
20 | AGORA_APPID: ${{ secrets.AGORA_APPID }}
21 | AGORA_AES_SALT: ${{secrets.AGORA_AES_SALT}}
22 | run: |
23 | pnpm install
24 | pnpm run lint
25 | pnpm run test
26 | pnpm run renew
27 | pnpm run build
28 |
29 | - name: Build Docs 📖
30 | run: |
31 | pnpm run typedoc
32 | pnpm run build-storybook
33 | pnpm run copy-docs
34 |
35 | - name: Deploy
36 | uses: peaceiris/actions-gh-pages@v3
37 | with:
38 | github_token: ${{ secrets.GITHUB_TOKEN }}
39 | publish_dir: ./docs
40 |
--------------------------------------------------------------------------------
/.github/workflows/gitleak.yml:
--------------------------------------------------------------------------------
1 | name: gitleaks
2 | on:
3 | pull_request:
4 | push:
5 | workflow_dispatch:
6 |
7 | jobs:
8 | scan:
9 | name: gitleaks
10 | runs-on: ubuntu-latest
11 | if: github.actor != 'dependabot[bot]'
12 | steps:
13 | - uses: actions/checkout@v3
14 | with:
15 | fetch-depth: 0
16 | - uses: gitleaks/gitleaks-action@v2
17 | env:
18 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
19 | GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
20 |
--------------------------------------------------------------------------------
/.github/workflows/pr.yml:
--------------------------------------------------------------------------------
1 | on: ["pull_request"]
2 |
3 | name: Build and Test PR
4 |
5 | jobs:
6 | build:
7 | name: Build
8 | runs-on: ubuntu-latest
9 |
10 | permissions:
11 | # Required to checkout the code
12 | contents: read
13 | # Required to put a comment into the pull-request
14 | pull-requests: write
15 |
16 | concurrency:
17 | group: ${{ github.workflow }}-${{ github.ref }}
18 | cancel-in-progress: true
19 | steps:
20 | - name: Checkout 🛎️
21 | uses: actions/checkout@v3
22 |
23 | - name: Setup 🛠️
24 | uses: ./.github/actions/setup
25 |
26 | - name: Build 🔧
27 | run: |
28 | pnpm install
29 | pnpm run lint
30 | pnpm run test
31 | pnpm run build
32 |
33 | - name: Report Coverage for agora-rtc-react 🟢
34 | if: always() # Also generate the report if tests are failing
35 | uses: davelosert/vitest-coverage-report-action@v2
36 | with:
37 | working-directory: "./packages/agora-rtc-react"
38 |
--------------------------------------------------------------------------------
/.github/workflows/sync.yml:
--------------------------------------------------------------------------------
1 | name: Sync to shengwang
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | move-to-shengwang:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Setup
11 | uses: ./.github/actions/setup
12 |
13 | - name: Setup Dependencies 💚
14 | run: |
15 | pnpm install
16 |
17 | - name: Sync to shengwang
18 | uses: AgoraIO-Extensions/actions/.github/actions/shengwang-sync@main
19 | with:
20 | target-repo: "shengwang-rtc-react"
21 | source-repo: "agora-rtc-react"
22 | github-token: ${{ secrets.GH_TOKEN }}
23 | target-branch: ${{ github.ref_name }}
24 | github-email: ${{ secrets.GIT_EMAIL }}
25 | github-private-key: ${{ secrets.GH_PRIVATE_KEY }}
26 | github-username: ${{ secrets.GIT_USERNAME }}
27 | pre-command: |
28 | sh scripts/publishCN/rewrite-dep.sh
29 | sh scripts/publishCN/rewrite-example.sh
30 | pnpm run lint
31 | create-pr: true
32 |
--------------------------------------------------------------------------------
/.github/workflows/typedoc.yml:
--------------------------------------------------------------------------------
1 | # Simple workflow for deploying static content to GitHub Pages
2 | name: TypeDoc
3 |
4 | on:
5 | # Runs on new releases
6 | release: {}
7 | push:
8 | branches: [main]
9 |
10 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
11 | permissions:
12 | contents: write
13 | pages: write
14 | id-token: write
15 |
16 | jobs:
17 | # Single deploy job since we're just deploying
18 | deploy:
19 | runs-on: ubuntu-latest
20 | steps:
21 | - name: Checkout 🛎️
22 | uses: actions/checkout@v3
23 | - name: Build TypeDoc
24 | run: |
25 | npm i -g pnpm && pnpm install && pnpm typedoc
26 | - name: Zip It Up 🤐
27 | run: |
28 | zip -r agora-rtc-react-docs.zip typedoc
29 | - name: Upload Artifact ⬆️
30 | uses: actions/upload-artifact@v4
31 | with:
32 | name: agora-rtc-react-docs.zip
33 | path: agora-rtc-react-docs.zip
34 | - name: Upload Doc Archive to GitHub release ⬆️
35 | if: github.event.release
36 | uses: svenstaro/upload-release-action@2.6.0
37 | with:
38 | file: agora-rtc-react-docs.zip
39 | asset_name: agora-rtc-react-docs.zip
40 | tag: ${{ github.ref_name }}
41 |
--------------------------------------------------------------------------------
/.github/workflows/upgrade-dep.yml:
--------------------------------------------------------------------------------
1 | name: Upgrade Dependencies
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | package-name:
7 | description: 'upgrade package name: [default: "agora-rtc-sdk-ng"]'
8 | required: true
9 | default: "agora-rtc-sdk-ng"
10 | type: string
11 | package-version:
12 | description: "upgrade package version: [eg: '*.*.*']"
13 | required: true
14 | type: string
15 |
16 | jobs:
17 | upgrade-dep:
18 | runs-on: ubuntu-latest
19 | steps:
20 | - name: Checkout
21 | uses: actions/checkout@v4
22 | with:
23 | fetch-depth: 0
24 | token: ${{ secrets.GITHUB_TOKEN }}
25 |
26 | - name: Setup 🛠️
27 | uses: ./.github/actions/setup
28 |
29 | - name: Upgrade Dependencies 🚀
30 | run: |
31 | pnpm install
32 | pnpm -r exec esbuild-dev $PWD/scripts/upgrade-deps.ts dep:${{ inputs.package-name }} @${{ inputs.package-version }}
33 | pnpm -w exec esbuild-dev $PWD/scripts/upgrade-deps.ts dep:${{ inputs.package-name }} @${{ inputs.package-version }}
34 | pnpm install --no-frozen-lockfile
35 | pnpm run test
36 |
37 | - name: Create pull request
38 | uses: AgoraIO-Extensions/actions/.github/actions/pr@main
39 | with:
40 | github-token: ${{ secrets.GH_TOKEN }}
41 | target-repo: ${{ github.workspace }}
42 | target-branch: ${{ github.ref_name }}
43 | target-branch-name-surffix: dep-update
44 | pull-request-title: |
45 | chore: upgrade ${{inputs.package-name}} to ${{inputs.package-version}}
46 | add-paths: .
47 |
--------------------------------------------------------------------------------
/.gitleaks.toml:
--------------------------------------------------------------------------------
1 | [allowlist]
2 | description = "gh pages build file allow lists"
3 | paths = [
4 | '''gitleaks.toml''',
5 | ]
6 |
7 | # ----- BEGIN Agora AppId -----
8 | [[rules]]
9 | id = "agora-appid"
10 | description = "Agora AppId"
11 | regex = '''"[0-9a-f]{32}"'''
12 | # ----- END Agora AppId -----
13 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx commitlint --edit "$1"
5 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx lint-staged --allow-empty
5 |
6 | if command -v gitleaks >/dev/null 2>&1;
7 | then
8 | echo "gitleaks found, proceeding..."
9 | gitleaks protect --staged --source . -v agora-rtc-react.git -c .gitleaks.toml
10 | else
11 | echo "gitleaks not found, skip"
12 | fi
13 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 18.16.0
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | **/dist/*
2 | **/coverage/*
3 | **/node_modules/*
4 | **/storybook-static/*
5 | pnpm-lock.yaml
6 | **/typedoc/*
7 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "all",
3 | "tabWidth": 2,
4 | "semi": true,
5 | "singleQuote": false,
6 | "endOfLine": "auto",
7 | "arrowParens": "avoid",
8 | "printWidth": 100,
9 | "quoteProps": "consistent"
10 | }
11 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "vivaxy.vscode-conventional-commits",
4 | "EditorConfig.EditorConfig",
5 | "dbaeumer.vscode-eslint",
6 | "esbenp.prettier-vscode",
7 | "streetsidesoftware.code-spell-checker"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Agora.io
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/cspell.json:
--------------------------------------------------------------------------------
1 | {
2 | "language": "en",
3 | "words": [
4 | "antd",
5 | "autodocs",
6 | "clsx",
7 | "coverallsapp",
8 | "CRIMX",
9 | "guoxianzhe",
10 | "docgen",
11 | "emsp",
12 | "esbuild",
13 | "falso",
14 | "hyrious",
15 | "iconify",
16 | "iife",
17 | "lcov",
18 | "LichKing",
19 | "lucide",
20 | "ngneat",
21 | "nums",
22 | "paren",
23 | "mobx",
24 | "Parens",
25 | "peaceiris",
26 | "pnpm",
27 | "Preact",
28 | "preinstall",
29 | "stremas",
30 | "treeshake",
31 | "tsup",
32 | "typedoc",
33 | "UNILBS",
34 | "unocss",
35 | "unpublish",
36 | "unpublishes",
37 | "Vals",
38 | "vite",
39 | "vitest",
40 | "yoctocolors",
41 | "zustand"
42 | ],
43 | "ignorePaths": [
44 | "**/coverage/**",
45 | "**/node_modules/**",
46 | "**/dist/**",
47 | "cspell.config.js",
48 | "pnpm-lock.yaml",
49 | "CHANGELOG.md"
50 | ]
51 | }
52 |
--------------------------------------------------------------------------------
/examples/basic/.env.example:
--------------------------------------------------------------------------------
1 | # https://docs.agora.io/en/video-calling/reference/manage-agora-account?platform=web#get-the-app-id
2 | AGORA_APPID=
3 |
4 | # https://docs.agora.io/en/video-calling/reference/manage-agora-account?platform=web#generate-a-temporary-token
5 | AGORA_CHANNEL=test
6 |
7 | # if Security is Disabled in the Agora Console, leave this to blank
8 | AGORA_TOKEN=
9 |
--------------------------------------------------------------------------------
/examples/basic/README.md:
--------------------------------------------------------------------------------
1 | # Agora RTC React basic Example
2 |
3 | - Simple example without external state management.
4 |
5 |
6 |
7 | ## Develop
8 |
9 | 1. Add a `.env.local` file to this directory and fill in the Agora account info following the format of `.env.example`.
10 | - You can also add a `.env.local` at monorepo root with `AGORA_APPID` and `AGORA_CERTIFICATE`, then `pnpm renew` to auto-renew tokens.
11 | 2. `pnpm run start`.
12 |
--------------------------------------------------------------------------------
/examples/basic/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Agora RTC React Track Example
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/examples/basic/index.scss:
--------------------------------------------------------------------------------
1 | .github-corner {
2 | position: fixed;
3 | top: 0;
4 | right: 0;
5 | }
6 | .github-corner:hover .github-corner-svg-arm {
7 | animation: github-corner-svg-wave 560ms ease-in-out;
8 | }
9 | .github-corner-svg {
10 | fill: #151513;
11 | color: #fff;
12 | position: absolute;
13 | top: 0;
14 | border: 0;
15 | right: 0;
16 | &-arm {
17 | transform-origin: 130px 106px;
18 | }
19 | }
20 | @keyframes github-corner-svg-wave {
21 | 0%,
22 | 100% {
23 | transform: rotate(0);
24 | }
25 | 20%,
26 | 60% {
27 | transform: rotate(-25deg);
28 | }
29 | 40%,
30 | 80% {
31 | transform: rotate(10deg);
32 | }
33 | }
34 | @media (max-width: 500px) {
35 | .github-corner:hover .github-corner-svg-arm {
36 | animation: none;
37 | }
38 | .github-corner .github-corner-svg-arm {
39 | animation: github-corner-svg-wave 560ms ease-in-out;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/examples/basic/main.tsx:
--------------------------------------------------------------------------------
1 | import "sanitize.css";
2 | import "uno.css";
3 | import AgoraRTC from "agora-rtc-react";
4 | import { StrictMode } from "react";
5 | import React from "react";
6 | import ReactDOM from "react-dom/client";
7 | import { HashRouter as Router } from "react-router-dom";
8 |
9 | import App from "./src/App";
10 | AgoraRTC.setLogLevel(/* DEBUG */ 0);
11 |
12 | ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
13 |
14 |
15 |
16 |
17 | ,
18 | );
19 |
--------------------------------------------------------------------------------
/examples/basic/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "agora-rtc-react-example",
3 | "private": true,
4 | "type": "module",
5 | "main": "./main.tsx",
6 | "scripts": {
7 | "start": "vite --host --open",
8 | "build": "vite build",
9 | "preview": "vite preview",
10 | "renew": "esbuild-dev ../../scripts/renew.ts --write"
11 | },
12 | "dependencies": {
13 | "@ant-design/icons": "^5.2.4",
14 | "agora-rtc-react": "workspace:*",
15 | "agora-rtc-react-ui": "workspace:*",
16 | "antd": "^5.6.4",
17 | "clsx": "^1.2.1",
18 | "crypto-js": "^4.2.0",
19 | "react": "^18.2.0",
20 | "react-dom": "^18.2.0",
21 | "react-router-dom": "^6.16.0",
22 | "react-select": "^5.7.3",
23 | "sanitize.css": "^13.0.0",
24 | "zustand": "^4.3.9"
25 | },
26 | "devDependencies": {
27 | "@iconify-json/lucide": "^1.1.110",
28 | "@iconify-json/mdi": "^1.1.53",
29 | "@ngneat/falso": "^6.4.0",
30 | "@types/crypto-js": "^4.1.1",
31 | "sass": "^1.64.0",
32 | "sass-loader": "^13.3.2",
33 | "unocss": "^0.53.5"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/examples/basic/src/components/AutoLayout.tsx:
--------------------------------------------------------------------------------
1 | import { clsx } from "clsx";
2 | import type { PropsWithChildren } from "react";
3 | import { forwardRef } from "react";
4 |
5 | const Item = /* @__PURE__ */ forwardRef>(
6 | ({ className, children }, ref) => (
7 |
12 | {children}
13 |
14 | ),
15 | );
16 |
17 | export const AutoLayout = /* @__PURE__ */ Object.assign(
18 | /* @__PURE__ */ forwardRef>(
19 | ({ className, children }, ref) => (
20 |
21 | {children}
22 |
23 | ),
24 | ),
25 | { Item },
26 | );
27 |
--------------------------------------------------------------------------------
/examples/basic/src/components/Client.tsx:
--------------------------------------------------------------------------------
1 | import type { ClientConfig } from "agora-rtc-react";
2 | import AgoraRTC, { AgoraRTCProvider } from "agora-rtc-react";
3 | import type { ReactNode } from "react";
4 | import { useState } from "react";
5 |
6 | interface ClientProps {
7 | children: ReactNode;
8 | clientConfig?: ClientConfig;
9 | }
10 |
11 | export const Client = ({ children, clientConfig = { mode: "rtc", codec: "vp8" } }: ClientProps) => {
12 | const [client] = useState(() => AgoraRTC.createClient(clientConfig));
13 | return {children} ;
14 | };
15 |
16 | export default Client;
17 |
--------------------------------------------------------------------------------
/examples/basic/src/components/Container.tsx:
--------------------------------------------------------------------------------
1 | import type { PropsWithChildren } from "react";
2 |
3 | export const Container = ({ children }: PropsWithChildren) => (
4 | {children}
5 | );
6 |
--------------------------------------------------------------------------------
/examples/basic/src/components/Label.tsx:
--------------------------------------------------------------------------------
1 | import type { PropsWithChildren } from "react";
2 |
3 | export const Label = ({ children }: PropsWithChildren) => (
4 |
5 | {children}
6 |
7 | );
8 |
--------------------------------------------------------------------------------
/examples/basic/src/components/LocalTracks.tsx:
--------------------------------------------------------------------------------
1 | import type { ICameraVideoTrack, IMicrophoneAudioTrack, UID } from "agora-rtc-react";
2 | import { CameraVideoTrack, MicrophoneAudioTrack } from "agora-rtc-react-ui";
3 |
4 | export interface LocalTracksProps {
5 | uid: UID;
6 | micOn: boolean;
7 | cameraOn: boolean;
8 | audioTrack?: IMicrophoneAudioTrack | null;
9 | videoTrack?: ICameraVideoTrack | null;
10 | }
11 |
12 | export const LocalTracks = ({ micOn, audioTrack, cameraOn, videoTrack, uid }: LocalTracksProps) => (
13 |
14 | {micOn &&
}
15 | {cameraOn &&
}
16 |
17 | {uid}
18 | {micOn && }
19 |
20 |
21 | );
22 |
--------------------------------------------------------------------------------
/examples/basic/src/components/MediaControl.tsx:
--------------------------------------------------------------------------------
1 | import { SVGCamera, SVGCameraMute, SVGMicrophone, SVGMicrophoneMute } from "agora-rtc-react-ui";
2 | import clsx from "clsx";
3 |
4 | interface MediaControlProps {
5 | calling?: boolean;
6 | micOn?: boolean;
7 | cameraOn?: boolean;
8 | setMic?: () => void;
9 | setCamera?: () => void;
10 | setCalling?: () => void;
11 | }
12 | /* Camera and Microphone Controls */
13 | export const MediaControl = ({
14 | calling,
15 | micOn,
16 | cameraOn,
17 | setMic,
18 | setCamera,
19 | setCalling,
20 | }: MediaControlProps) => (
21 | <>
22 |
23 |
24 | {setMic && (
25 | setMic()}>
26 | {micOn ? : }
27 |
28 | )}
29 | {setCamera && (
30 | setCamera()}>
31 | {cameraOn ? : }
32 |
33 | )}
34 |
35 | {setCalling && (
36 |
setCalling()}
39 | >
40 | {calling ? : }
41 |
42 | )}
43 |
44 | >
45 | );
46 |
--------------------------------------------------------------------------------
/examples/basic/src/components/RemoteUsers.tsx:
--------------------------------------------------------------------------------
1 | import type { IRemoteVideoTrack } from "agora-rtc-react";
2 | import { RemoteVideoPlayer } from "agora-rtc-react-ui";
3 |
4 | import { fakeAvatar, fakeName } from "../utils";
5 |
6 | import { AutoLayout } from "./AutoLayout";
7 | import { Label } from "./Label";
8 |
9 | export function RenderRemoteUsers({ videoTracks }: { videoTracks: IRemoteVideoTrack[] }) {
10 | return (
11 | <>
12 | {videoTracks.map((track: IRemoteVideoTrack) => (
13 |
14 |
15 | {`${fakeName(track.getUserId())}{${track.getUserId()}}`}
16 |
17 | ))}
18 | >
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/examples/basic/src/components/ToolBox.tsx:
--------------------------------------------------------------------------------
1 | import { Button, Drawer } from "antd";
2 | import { type ReactNode, useState } from "react";
3 |
4 | interface ToolBoxProps {
5 | children?: ReactNode;
6 | }
7 | export const ToolBox = ({ children }: ToolBoxProps) => {
8 | const [open, setOpen] = useState(false);
9 | const showDrawer = () => {
10 | setOpen(true);
11 | };
12 |
13 | const onClose = () => {
14 | setOpen(false);
15 | };
16 |
17 | const confirm = () => {
18 | setOpen(false);
19 | };
20 | return (
21 | <>
22 | {children ? (
23 | children
24 | ) : (
25 | <>
26 |
41 |
42 |
43 |
44 |
45 |
46 | Confirm
47 |
48 |
49 |
50 | >
51 | )}
52 | >
53 | );
54 | };
55 |
--------------------------------------------------------------------------------
/examples/basic/src/components/UsersInfo.tsx:
--------------------------------------------------------------------------------
1 | export const UsersInfo = ({ published, total }: { published: number; total: number }) => (
2 |
3 |
4 |
5 |
6 | {published}/{total}
7 |
8 |
9 |
10 | );
11 |
--------------------------------------------------------------------------------
/examples/basic/src/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./AutoLayout";
2 | export * from "./Container";
3 | export * from "./Label";
4 | export * from "./LocalTracks";
5 | export * from "./UsersInfo";
6 | export * from "./Room";
7 | export * from "./Client";
8 | export * from "./MediaControl";
9 | export * from "./ToolBox";
10 | export * from "./ScreenShare";
11 |
--------------------------------------------------------------------------------
/examples/basic/src/pages/advanced/index.ts:
--------------------------------------------------------------------------------
1 | import MultiChannel from "./multi-channel";
2 | import ShareScreen from "./share-screen";
3 | import SwitchLayout from "./switch-layout";
4 |
5 | const Advanced = [
6 | {
7 | label: "switch-layout",
8 | component: SwitchLayout,
9 | },
10 | {
11 | label: "multi-channel",
12 | component: MultiChannel,
13 | },
14 | {
15 | label: "share-screen",
16 | component: ShareScreen,
17 | },
18 | ];
19 |
20 | export { Advanced };
21 |
--------------------------------------------------------------------------------
/examples/basic/src/pages/advanced/multi-channel/index.scss:
--------------------------------------------------------------------------------
1 | .room-selector-item {
2 | width: 100%;
3 | height: 50px;
4 | overflow: hidden;
5 | display: flex;
6 | align-items: center;
7 | flex-wrap: nowrap;
8 | gap: 10px;
9 | padding: 0 10px;
10 | border-bottom-width: 1px;
11 | border-bottom-style: solid;
12 | border-color: #6b72801a;
13 | cursor: pointer;
14 | &:hover {
15 | opacity: 1;
16 | background-color: #6b728005;
17 | }
18 | }
19 |
20 | .user {
21 | position: relative;
22 | width: 54px;
23 | height: 40px;
24 | overflow: hidden;
25 | border-radius: 5px;
26 | }
27 |
28 | .mask {
29 | position: absolute;
30 | top: 0;
31 | left: 0;
32 | width: 100%;
33 | height: 100%;
34 | background: linear-gradient(
35 | rgba(0, 0, 0, 0.1) 60%,
36 | rgba(0, 0, 0, 0.3) 80%,
37 | rgba(0, 0, 0, 0.8) 100%
38 | );
39 | }
40 |
41 | .label {
42 | position: absolute;
43 | z-index: 1;
44 | left: 2px;
45 | bottom: -2px;
46 | width: 100%;
47 | overflow: hidden;
48 | text-overflow: ellipsis;
49 | white-space: nowrap;
50 | word-break: keep-all;
51 | font-size: 12px;
52 | color: #fff;
53 | }
54 |
--------------------------------------------------------------------------------
/examples/basic/src/pages/advanced/switch-layout/index.scss:
--------------------------------------------------------------------------------
1 | .btn {
2 | display: inline-flex;
3 | flex-direction: column;
4 | background-color: transparent;
5 | border: 1px solid rgba(255, 255, 255, 0.1);
6 | border-radius: 0.25rem;
7 | padding: 0.25rem 0.5rem;
8 | appearance: none;
9 | cursor: pointer;
10 | align-items: center;
11 | font-size: 0;
12 | color: inherit;
13 | &:hover {
14 | background-color: rgba(255, 255, 255, 0.05);
15 | }
16 | &:disabled {
17 | background-color: transparent;
18 | cursor: not-allowed;
19 | }
20 | > i {
21 | font-size: 1.5rem;
22 | }
23 | > span {
24 | font-size: 0.75rem;
25 | }
26 | &-phone {
27 | border-radius: 0.55rem;
28 | padding: 0.5rem 4rem;
29 | color: #fff;
30 | background-color: #16a34a;
31 | &:hover {
32 | background-color: #22c55e;
33 | }
34 | &-active,
35 | &-active:hover {
36 | background-color: #dc2626;
37 | }
38 | }
39 | &-switch-layout {
40 | border: 1px solid;
41 | cursor: pointer;
42 | &-active {
43 | background: red;
44 | }
45 | }
46 | }
47 | .remote {
48 | > .label {
49 | > i {
50 | font-size: 1rem;
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/examples/basic/src/pages/basic/index.ts:
--------------------------------------------------------------------------------
1 | import Overview from "./overview";
2 |
3 | const Basic = [
4 | {
5 | label: "overview",
6 | component: Overview,
7 | },
8 | ];
9 |
10 | export { Basic };
11 |
--------------------------------------------------------------------------------
/examples/basic/src/pages/basic/overview/index.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | useJoin,
3 | useRemoteAudioTracks,
4 | useRemoteUsers,
5 | useRemoteVideoTracks,
6 | } from "agora-rtc-react";
7 | import { useState } from "react";
8 |
9 | import { Container, MediaControl, Room } from "../../../components";
10 | import { RenderRemoteUsers } from "../../../components/RemoteUsers";
11 | import { appConfig } from "../../../utils";
12 |
13 | export const Overview = () => {
14 | const [calling, setCalling] = useState(false);
15 |
16 | useJoin(
17 | {
18 | appid: appConfig.appId,
19 | channel: appConfig.channel,
20 | token: appConfig.token,
21 | },
22 | calling,
23 | );
24 |
25 | const [micOn, setMic] = useState(false);
26 | const [cameraOn, setCamera] = useState(false);
27 |
28 | const remoteUsers = useRemoteUsers();
29 | const { videoTracks } = useRemoteVideoTracks(remoteUsers);
30 | const { audioTracks } = useRemoteAudioTracks(remoteUsers);
31 | audioTracks.map(track => track.play());
32 |
33 | return (
34 |
35 | {calling ? (
36 | }
40 | />
41 | ) : (
42 |
43 | )}
44 | {
49 | setCalling(a => !a);
50 | }}
51 | setCamera={() => {
52 | setCamera(a => !a);
53 | }}
54 | setMic={() => {
55 | setMic(a => !a);
56 | }}
57 | />
58 |
59 | );
60 | };
61 |
62 | export default Overview;
63 |
--------------------------------------------------------------------------------
/examples/basic/src/pages/component/LocalAudioTrack/index.tsx:
--------------------------------------------------------------------------------
1 | import { LocalAudioTrack, useJoin, useLocalMicrophoneTrack, usePublish } from "agora-rtc-react";
2 | import { Typography } from "antd";
3 | import { useEffect, useState } from "react";
4 |
5 | import { Container, MediaControl } from "../../../components";
6 | import { appConfig } from "../../../utils";
7 | const { Title, Paragraph } = Typography;
8 |
9 | export const LocalAudioTrackComponent = () => {
10 | useJoin(
11 | {
12 | appid: appConfig.appId,
13 | channel: appConfig.channel,
14 | token: appConfig.token,
15 | },
16 | true,
17 | );
18 |
19 | const [micOn, setMic] = useState(false);
20 | const [audioTrackState, setAudioTrackState] = useState<{
21 | muted: boolean;
22 | isPlaying: boolean;
23 | enabled: boolean;
24 | }>();
25 | const { localMicrophoneTrack } = useLocalMicrophoneTrack(micOn);
26 |
27 | usePublish([localMicrophoneTrack]);
28 | useEffect(() => {
29 | if (localMicrophoneTrack) {
30 | setAudioTrackState({
31 | muted: localMicrophoneTrack.muted,
32 | isPlaying: localMicrophoneTrack.isPlaying,
33 | enabled: localMicrophoneTrack.enabled,
34 | });
35 | }
36 | }, [micOn, localMicrophoneTrack]);
37 | return (
38 |
39 |
40 |
local audio track status
41 | {audioTrackState && (
42 | <>
43 |
{`muted: ${audioTrackState?.muted}`}
44 |
{`isPlaying: ${audioTrackState.isPlaying}`}
45 |
{`enabled: ${audioTrackState?.enabled}`}
46 | >
47 | )}
48 |
49 |
50 | {
53 | setMic(a => !a);
54 | }}
55 | />
56 |
57 | );
58 | };
59 |
60 | export default LocalAudioTrackComponent;
61 |
--------------------------------------------------------------------------------
/examples/basic/src/pages/component/LocalVideoTrack/index.tsx:
--------------------------------------------------------------------------------
1 | import { LocalVideoTrack, useJoin, useLocalCameraTrack, usePublish } from "agora-rtc-react";
2 | import { Typography } from "antd";
3 | import { useState } from "react";
4 |
5 | import { Container, MediaControl } from "../../../components";
6 | import { appConfig } from "../../../utils";
7 | const { Title } = Typography;
8 |
9 | export const LocalVideoTrackComponent = () => {
10 | useJoin(
11 | {
12 | appid: appConfig.appId,
13 | channel: appConfig.channel,
14 | token: appConfig.token,
15 | },
16 | true,
17 | );
18 |
19 | const [cameraOn, setCamera] = useState(false);
20 | const { localCameraTrack } = useLocalCameraTrack();
21 | usePublish([localCameraTrack]);
22 | return (
23 |
24 |
25 |
local video track
26 |
33 |
34 | {
37 | setCamera(a => !a);
38 | }}
39 | />
40 |
41 | );
42 | };
43 |
44 | export default LocalVideoTrackComponent;
45 |
--------------------------------------------------------------------------------
/examples/basic/src/pages/component/index.ts:
--------------------------------------------------------------------------------
1 | import LocalAudioTrackComponent from "./LocalAudioTrack";
2 | import LocalUserComponent from "./LocalUser";
3 | import LocalVideoTrackComponent from "./LocalVideoTrack";
4 | import RemoteAudioTrackComponent from "./RemoteAudioTrack";
5 | import RemoteUserComponent from "./RemoteUser";
6 | import RemoteVideoTrackComponent from "./RemoteVideoTrack";
7 |
8 | const Components = [
9 | {
10 | label: "LocalAudioTrack",
11 | component: LocalAudioTrackComponent,
12 | },
13 | {
14 | label: "RemoteAudioTrack",
15 | component: RemoteAudioTrackComponent,
16 | },
17 | {
18 | label: "LocalVideoTrack",
19 | component: LocalVideoTrackComponent,
20 | },
21 | {
22 | label: "RemoteVideoTrack",
23 | component: RemoteVideoTrackComponent,
24 | },
25 | {
26 | label: "LocalUser",
27 | component: LocalUserComponent,
28 | },
29 | {
30 | label: "RemoteUser",
31 | component: RemoteUserComponent,
32 | },
33 | ];
34 |
35 | export { Components };
36 |
--------------------------------------------------------------------------------
/examples/basic/src/pages/hook/index.ts:
--------------------------------------------------------------------------------
1 | import UseAutoPlayAudioTrack from "./useAutoPlayAudioTrack";
2 | import UseAutoPlayVideoTrack from "./useAutoPlayVideoTrack";
3 | import UseClientEvent from "./useClientEvent";
4 | import UseConnectionState from "./useConnectionState";
5 | import UseCurrentUID from "./useCurrentUID";
6 | import UseIsConnected from "./useIsConnected";
7 | import UseJoin from "./useJoin";
8 | import UseLocalScreenTrack from "./useLocalScreenTrack";
9 | import UseNetworkQuality from "./useNetworkQuality";
10 | import UsePublish from "./usePublish";
11 | import UseRemoteUsers from "./useRemoteUsers";
12 | const Hooks = [
13 | {
14 | label: "useJoin",
15 | component: UseJoin,
16 | },
17 | {
18 | label: "usePublish",
19 | component: UsePublish,
20 | },
21 | {
22 | label: "useRemoteUsers",
23 | component: UseRemoteUsers,
24 | },
25 | {
26 | label: "useConnectionState",
27 | component: UseConnectionState,
28 | },
29 | {
30 | label: "useIsConnected",
31 | component: UseIsConnected,
32 | },
33 | {
34 | label: "useCurrentUID",
35 | component: UseCurrentUID,
36 | },
37 | {
38 | label: "useAutoPlayAudioTrack",
39 | component: UseAutoPlayAudioTrack,
40 | },
41 | {
42 | label: "useAutoPlayVideoTrack",
43 | component: UseAutoPlayVideoTrack,
44 | },
45 | {
46 | label: "useNetworkQuality",
47 | component: UseNetworkQuality,
48 | },
49 | {
50 | label: "useLocalScreenTrack",
51 | component: UseLocalScreenTrack,
52 | },
53 | {
54 | label: "UseClientEvent",
55 | component: UseClientEvent,
56 | },
57 | ];
58 |
59 | export { Hooks };
60 |
--------------------------------------------------------------------------------
/examples/basic/src/pages/hook/useAutoPlayAudioTrack/index.tsx:
--------------------------------------------------------------------------------
1 | import { useAutoPlayAudioTrack, useJoin, useLocalMicrophoneTrack } from "agora-rtc-react";
2 | import { Typography } from "antd";
3 | import { useState } from "react";
4 |
5 | import { Container, MediaControl } from "../../../components";
6 | import { appConfig } from "../../../utils";
7 | const { Title } = Typography;
8 |
9 | export const UseAutoPlayAudioTrack = () => {
10 | useJoin(
11 | {
12 | appid: appConfig.appId,
13 | channel: appConfig.channel,
14 | token: appConfig.token,
15 | },
16 | true,
17 | );
18 |
19 | const [micOn, setMic] = useState(false);
20 | const { localMicrophoneTrack } = useLocalMicrophoneTrack();
21 |
22 | useAutoPlayAudioTrack(localMicrophoneTrack, micOn);
23 |
24 | return (
25 |
26 |
27 |
useAutoPlayAudioTrack
28 |
29 | {
32 | setMic(a => !a);
33 | }}
34 | />
35 |
36 | );
37 | };
38 |
39 | export default UseAutoPlayAudioTrack;
40 |
--------------------------------------------------------------------------------
/examples/basic/src/pages/hook/useAutoPlayVideoTrack/index.tsx:
--------------------------------------------------------------------------------
1 | import { useAutoPlayVideoTrack, useJoin, useLocalCameraTrack } from "agora-rtc-react";
2 | import { Typography } from "antd";
3 | import { useRef, useState } from "react";
4 |
5 | import { Container, MediaControl } from "../../../components";
6 | import { appConfig } from "../../../utils";
7 | const { Title } = Typography;
8 |
9 | export const UseAutoPlayVideoTrack = () => {
10 | useJoin(
11 | {
12 | appid: appConfig.appId,
13 | channel: appConfig.channel,
14 | token: appConfig.token,
15 | },
16 | true,
17 | );
18 |
19 | const [cameraOn, setCamera] = useState(false);
20 |
21 | const div = useRef(null);
22 | const { localCameraTrack } = useLocalCameraTrack();
23 | useAutoPlayVideoTrack(localCameraTrack, cameraOn, div.current);
24 |
25 | return (
26 |
27 |
28 |
useAutoPlayVideoTrack
29 |
30 |
(div.current = ref)} style={{ width: "150px", height: "100px" }} />
31 |
32 |
33 |
{
36 | setCamera(a => !a);
37 | }}
38 | />
39 |
40 | );
41 | };
42 |
43 | export default UseAutoPlayVideoTrack;
44 |
--------------------------------------------------------------------------------
/examples/basic/src/pages/hook/useConnectionState/index.tsx:
--------------------------------------------------------------------------------
1 | import { useConnectionState, useJoin } from "agora-rtc-react";
2 | import { Button, Typography } from "antd";
3 | import { useState } from "react";
4 |
5 | import { Container } from "../../../components";
6 | import { appConfig } from "../../../utils";
7 | const { Title, Paragraph, Text } = Typography;
8 |
9 | export const UseConnectionState = () => {
10 | const [calling, setCalling] = useState(false);
11 | const connectionState = useConnectionState();
12 | useJoin(
13 | {
14 | appid: appConfig.appId,
15 | channel: appConfig.channel,
16 | token: appConfig.token,
17 | },
18 | calling,
19 | );
20 |
21 | return (
22 |
23 |
24 |
useConnectionState
25 |
26 | By using useConnectionState hook to get the current connection state
27 | of the client.
28 |
29 |
{`result: ${connectionState}`}
30 |
31 | setCalling(true)} type="primary">
32 | Join
33 |
34 | setCalling(false)} type="default">
35 | Leave
36 |
37 |
38 |
39 |
40 | );
41 | };
42 |
43 | export default UseConnectionState;
44 |
--------------------------------------------------------------------------------
/examples/basic/src/pages/hook/useCurrentUID/index.tsx:
--------------------------------------------------------------------------------
1 | import { useCurrentUID, useJoin } from "agora-rtc-react";
2 | import { Button, Typography } from "antd";
3 | import { useState } from "react";
4 |
5 | import { Container } from "../../../components";
6 | import { appConfig } from "../../../utils";
7 | const { Title, Paragraph, Text } = Typography;
8 |
9 | export const UseCurrentUID = () => {
10 | const [calling, setCalling] = useState(false);
11 | const uid = useCurrentUID();
12 | useJoin(
13 | {
14 | appid: appConfig.appId,
15 | channel: appConfig.channel,
16 | token: appConfig.token,
17 | },
18 | calling,
19 | );
20 |
21 | return (
22 |
23 |
24 |
useCurrentUID
25 |
26 | By using useCurrentUID hook to get the UID of the current user.
27 |
28 |
{`result: ${uid}`}
29 |
30 | setCalling(true)} type="primary">
31 | Join
32 |
33 | setCalling(false)} type="default">
34 | Leave
35 |
36 |
37 |
38 |
39 | );
40 | };
41 |
42 | export default UseCurrentUID;
43 |
--------------------------------------------------------------------------------
/examples/basic/src/pages/hook/useIsConnected/index.tsx:
--------------------------------------------------------------------------------
1 | import { useIsConnected, useJoin } from "agora-rtc-react";
2 | import { Button, Typography } from "antd";
3 | import { useState } from "react";
4 |
5 | import { Container } from "../../../components";
6 | import { appConfig } from "../../../utils";
7 | const { Title, Paragraph, Text } = Typography;
8 |
9 | export const UseIsConnected = () => {
10 | const [calling, setCalling] = useState(false);
11 | const isConnected = useIsConnected();
12 | useJoin(
13 | {
14 | appid: appConfig.appId,
15 | channel: appConfig.channel,
16 | token: appConfig.token,
17 | },
18 | calling,
19 | );
20 |
21 | return (
22 |
23 |
24 |
useIsConnected
25 |
26 | By using useIsConnected hook to determines whether the client is
27 | successfully connected to Agora server.
28 |
29 |
{`result: ${isConnected}`}
30 |
31 | setCalling(true)} type="primary">
32 | Join
33 |
34 | setCalling(false)} type="default">
35 | Leave
36 |
37 |
38 |
39 |
40 | );
41 | };
42 |
43 | export default UseIsConnected;
44 |
--------------------------------------------------------------------------------
/examples/basic/src/pages/hook/useNetworkQuality/index.tsx:
--------------------------------------------------------------------------------
1 | import { useJoin, useNetworkQuality } from "agora-rtc-react";
2 | import { Typography } from "antd";
3 |
4 | import { Container } from "../../../components";
5 | import { appConfig } from "../../../utils";
6 | const { Title, Paragraph } = Typography;
7 |
8 | export const UseNetworkQuality = () => {
9 | const networkQuality = useNetworkQuality();
10 | useJoin(
11 | {
12 | appid: appConfig.appId,
13 | channel: appConfig.channel,
14 | token: appConfig.token,
15 | },
16 | true,
17 | );
18 |
19 | return (
20 |
21 |
22 |
useNetworkQuality
23 |
{`result: ${JSON.stringify(networkQuality)}`}
24 |
25 |
26 | );
27 | };
28 |
29 | export default UseNetworkQuality;
30 |
--------------------------------------------------------------------------------
/examples/basic/src/pages/index.ts:
--------------------------------------------------------------------------------
1 | import MultiChannel from "./advanced/multi-channel";
2 | import SwitchLayout from "./advanced/switch-layout";
3 | import Overview from "./basic/overview";
4 | import UseJoin from "./hook/useJoin";
5 | import UsePublish from "./hook/usePublish";
6 |
7 | const Pages = [
8 | {
9 | label: "switch-layout",
10 | component: SwitchLayout,
11 | },
12 | {
13 | label: "overview",
14 | component: Overview,
15 | },
16 | {
17 | label: "multi-channel",
18 | component: MultiChannel,
19 | },
20 | {
21 | label: "useJoin",
22 | component: UseJoin,
23 | },
24 | {
25 | label: "usePublish",
26 | component: UsePublish,
27 | },
28 | ];
29 |
30 | export { Pages };
31 |
--------------------------------------------------------------------------------
/examples/basic/src/pages/setting/index.tsx:
--------------------------------------------------------------------------------
1 | import { Button, Form, Input, notification } from "antd";
2 | import { useState } from "react";
3 |
4 | import { Container } from "../../components";
5 | import { appConfig } from "../../utils";
6 |
7 | type NotificationType = "success" | "info" | "warning" | "error";
8 |
9 | export const Setting = () => {
10 | const [channel, setChannel] = useState(appConfig.channel);
11 |
12 | const handleChannelNameChange = (e: React.ChangeEvent) => {
13 | setChannel(e.target.value);
14 | };
15 | const [api, contextHolder] = notification.useNotification();
16 |
17 | const openNotificationWithIcon = (type: NotificationType) => {
18 | api[type]({
19 | message: "Configuration Saved",
20 | description: "Go to other pages and try it now!",
21 | });
22 | };
23 |
24 | const confirm = () => {
25 | appConfig.channel = channel;
26 | openNotificationWithIcon("success");
27 | };
28 |
29 | const buttonItemLayout = { wrapperCol: { span: 14, offset: 4 } };
30 | return (
31 |
32 | {contextHolder}
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | Save configuration
41 |
42 |
43 |
44 | );
45 | };
46 |
47 | export default Setting;
48 |
--------------------------------------------------------------------------------
/examples/basic/src/utils/fake.ts:
--------------------------------------------------------------------------------
1 | import { randFirstName, seed } from "@ngneat/falso";
2 | import type { UID } from "agora-rtc-react";
3 |
4 | import { appConfig } from "./const";
5 |
6 | export const fakeName = (uid: UID): string => {
7 | seed(String(uid));
8 | const name = randFirstName();
9 | seed();
10 | return name;
11 | };
12 |
13 | export const fakeAvatar = (): string => {
14 | return "https://www.agora.io/en/wp-content/uploads/2022/10/3d-spatial-audio-icon.svg";
15 | };
16 |
17 | export const fakeFetch = (url: string): Promise => {
18 | return new Promise(resolve => {
19 | let responseText = "";
20 | if (url === "/get-token") {
21 | responseText = JSON.stringify({
22 | appid: appConfig.appId,
23 | channel: appConfig.channel,
24 | token: appConfig.token,
25 | });
26 | } else {
27 | responseText = "404 Not Found.";
28 | }
29 | resolve(responseText);
30 | });
31 | };
32 |
--------------------------------------------------------------------------------
/examples/basic/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./fake";
2 | export * from "./const";
3 |
--------------------------------------------------------------------------------
/examples/basic/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": ["src", "src/App.tsx", "vite-env.d.ts"],
20 | "references": [{ "path": "./tsconfig.node.json" }]
21 | }
22 |
--------------------------------------------------------------------------------
/examples/basic/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/examples/basic/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | interface ImportMetaEnv {
4 | readonly AGORA_APPID: string;
5 | readonly AGORA_CHANNEL: string;
6 | readonly AGORA_TOKEN: string;
7 | readonly AGORA_AES_SALT: string;
8 | }
9 |
--------------------------------------------------------------------------------
/examples/basic/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from "@vitejs/plugin-react";
2 | import presetIcons from "unocss/preset-icons";
3 | import presetUno from "unocss/preset-uno";
4 | import uno from "unocss/vite";
5 | import { defineConfig } from "vite";
6 |
7 | // https://vitejs.dev/config/
8 | export default defineConfig({
9 | base: "./",
10 | plugins: [
11 | // https://uno.antfu.me/
12 | uno({
13 | presets: [presetUno(), presetIcons()],
14 | }),
15 | react(),
16 | ],
17 | envPrefix: "AGORA_",
18 | });
19 |
--------------------------------------------------------------------------------
/examples/mobx/.env.example:
--------------------------------------------------------------------------------
1 | # https://docs.agora.io/en/video-calling/reference/manage-agora-account?platform=web#get-the-app-id
2 | AGORA_APPID=
3 |
4 | # https://docs.agora.io/en/video-calling/reference/manage-agora-account?platform=web#generate-a-temporary-token
5 | AGORA_CHANNEL=test
6 |
7 | # if Security is Disabled in the Agora Console, leave this to blank
8 | AGORA_TOKEN=
9 |
--------------------------------------------------------------------------------
/examples/mobx/README.md:
--------------------------------------------------------------------------------
1 | # Agora RTC React MobX Example
2 |
3 | - MobX for state management.
4 | - Custom user name and avatar.
5 | - Create tracks on demand.
6 | - import React & SDK by CDN.
7 |
8 |
9 |
10 | ## Develop
11 |
12 | 1. Add a `.env.local` file to this directory and fill in the Agora account info following the format of `.env.example`.
13 | - You can also add a `.env.local` at monorepo root with `AGORA_APPID` and `AGORA_CERTIFICATE`, then `pnpm renew` to auto-renew tokens.
14 | 2. `pnpm run start`.
15 |
--------------------------------------------------------------------------------
/examples/mobx/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Agora RTC React MobX Example
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/examples/mobx/index.scss:
--------------------------------------------------------------------------------
1 | .github-corner {
2 | position: fixed;
3 | top: 0;
4 | right: 0;
5 | }
6 | .github-corner:hover .github-corner-svg-arm {
7 | animation: github-corner-svg-wave 560ms ease-in-out;
8 | }
9 | .github-corner-svg {
10 | fill: #151513;
11 | color: #fff;
12 | position: absolute;
13 | top: 0;
14 | border: 0;
15 | right: 0;
16 | &-arm {
17 | transform-origin: 130px 106px;
18 | }
19 | }
20 | @keyframes github-corner-svg-wave {
21 | 0%,
22 | 100% {
23 | transform: rotate(0);
24 | }
25 | 20%,
26 | 60% {
27 | transform: rotate(-25deg);
28 | }
29 | 40%,
30 | 80% {
31 | transform: rotate(10deg);
32 | }
33 | }
34 | @media (max-width: 500px) {
35 | .github-corner:hover .github-corner-svg-arm {
36 | animation: none;
37 | }
38 | .github-corner .github-corner-svg-arm {
39 | animation: github-corner-svg-wave 560ms ease-in-out;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/examples/mobx/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-mobx",
3 | "private": true,
4 | "type": "module",
5 | "scripts": {
6 | "start": "vite --host --open",
7 | "build": "vite build",
8 | "preview": "vite preview",
9 | "renew": "esbuild-dev ../../scripts/renew.ts --write"
10 | },
11 | "dependencies": {
12 | "@ngneat/falso": "^6.4.0",
13 | "agora-rtc-react": "workspace:*",
14 | "agora-rtc-react-ui": "workspace:*",
15 | "clsx": "^1.2.1",
16 | "crypto-js": "^4.2.0",
17 | "mobx": "^6.10.0",
18 | "mobx-react-lite": "^4.0.3",
19 | "react": "^18.2.0",
20 | "react-dom": "^18.2.0",
21 | "sanitize.css": "^13.0.0",
22 | "side-effect-manager": "^1.2.2"
23 | },
24 | "devDependencies": {
25 | "@iconify-json/mdi": "^1.1.53",
26 | "@types/crypto-js": "^4.1.1",
27 | "unocss": "^0.53.5",
28 | "vite-plugin-externals": "^0.6.2"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/examples/mobx/src/components/Controls.tsx:
--------------------------------------------------------------------------------
1 | import { clsx } from "clsx";
2 | import { observer } from "mobx-react-lite";
3 |
4 | import { appStore } from "../stores/app.store";
5 | import type { MyLocalUser } from "../stores/local-user.store";
6 |
7 | interface ControlsProps {
8 | localUser: MyLocalUser;
9 | }
10 |
11 | export const Controls = observer(function Controls({ localUser }: ControlsProps) {
12 | const { shareScreen } = appStore;
13 |
14 | return (
15 |
16 |
localUser.setMic(!localUser.micOn)}
19 | >
20 | {localUser.micOn ? (
21 |
22 | ) : (
23 |
24 | )}
25 | {localUser.micOn ? "Mute" : "Unmute"}
26 |
27 |
localUser.setCamera(!localUser.cameraOn)}
30 | >
31 | {localUser.cameraOn ? : }
32 | Video
33 |
34 |
35 |
(shareScreen.enabled ? shareScreen.disable() : shareScreen.enable())}
39 | >
40 |
41 | Share Screen
42 |
43 |
44 |
appStore.leave()}>
45 |
46 | Quit
47 |
48 |
49 | );
50 | });
51 |
--------------------------------------------------------------------------------
/examples/mobx/src/components/Home.tsx:
--------------------------------------------------------------------------------
1 | import { observer } from "mobx-react-lite";
2 | import { useState } from "react";
3 |
4 | import { appId, channel, token } from "../constants";
5 | import { appStore } from "../stores/app.store";
6 | import { useSafePromise } from "../utils";
7 |
8 | import { RoomInfo } from "./RoomInfo";
9 | import { JoinButton, NewButton, ScheduleButton, ScreenShareButton } from "./buttons";
10 |
11 | export const Home = observer(function Home() {
12 | const sp = useSafePromise();
13 | const [isLoading, setLoading] = useState(false);
14 | const joinChannel = () => {
15 | setLoading(true);
16 | sp(appStore.join(appId, channel, token)).then(() => setLoading(false));
17 | };
18 |
19 | return (
20 |
21 |
22 |
Agora
23 |
RTC
24 |
React
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | );
35 | });
36 |
--------------------------------------------------------------------------------
/examples/mobx/src/components/LocalUser.tsx:
--------------------------------------------------------------------------------
1 | import { LocalMicrophoneAndCameraUser, MicControl } from "agora-rtc-react-ui";
2 | import { observer } from "mobx-react-lite";
3 |
4 | import type { MyLocalUser } from "../stores/local-user.store";
5 |
6 | interface LocalUserProps {
7 | className?: string;
8 | localUser: MyLocalUser;
9 | }
10 |
11 | export const LocalUser = observer(function LocalUser({ className, localUser }: LocalUserProps) {
12 | return (
13 |
14 |
22 | {localUser.name}
23 | {localUser.micOn && }
24 |
25 |
26 | );
27 | });
28 |
--------------------------------------------------------------------------------
/examples/mobx/src/components/Room.tsx:
--------------------------------------------------------------------------------
1 | import { RemoteUser } from "agora-rtc-react";
2 | import { MicControl } from "agora-rtc-react-ui";
3 | import { observer } from "mobx-react-lite";
4 |
5 | import { appStore } from "../stores/app.store";
6 |
7 | import { Controls } from "./Controls";
8 | import { LocalUser } from "./LocalUser";
9 | import { ShareScreenTracks } from "./ShareScreenTracks";
10 | import { Users } from "./Users";
11 |
12 | export const Room = observer(function Room() {
13 | const { localUser, remoteUsers } = appStore.users;
14 |
15 | return (
16 |
17 |
18 |
19 |
20 | {localUser &&
}
21 | {remoteUsers.map(user => (
22 |
23 |
29 | {user.name}
30 | {user.micOn && }
31 |
32 |
33 | ))}
34 |
35 |
36 | {localUser &&
}
37 |
38 |
39 |
40 |
41 | );
42 | });
43 |
--------------------------------------------------------------------------------
/examples/mobx/src/components/RoomInfo.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 |
3 | export function RoomInfo({ onJoin }: { onJoin?: () => void }) {
4 | const [now, setNow] = useState(Date.now());
5 |
6 | useEffect(() => {
7 | const ticket = setInterval(() => setNow(Date.now()), 1000);
8 | return () => clearInterval(ticket);
9 | }, []);
10 |
11 | const datetime = new Date(now);
12 | const time =
13 | datetime &&
14 | datetime.toLocaleTimeString("en-US", {
15 | hour: "2-digit",
16 | minute: "2-digit",
17 | hour12: false,
18 | });
19 | const date =
20 | datetime &&
21 | datetime.toLocaleDateString("en-US", {
22 | year: "numeric",
23 | month: "numeric",
24 | day: "numeric",
25 | weekday: "long",
26 | });
27 |
28 | return (
29 |
30 |
31 |
{time}
32 |
{date}
33 |
34 |
35 |
36 | Example Meeting
37 | 11:00~16:00
38 |
39 |
40 | Start
41 |
42 |
43 |
44 | );
45 | }
46 |
--------------------------------------------------------------------------------
/examples/mobx/src/components/ShareScreenTracks.tsx:
--------------------------------------------------------------------------------
1 | import { RemoteVideoTrack } from "agora-rtc-react";
2 | import { observer } from "mobx-react-lite";
3 |
4 | import { appStore } from "../stores/app.store";
5 |
6 | export const ShareScreenTracks = observer(function ShareScreenTracks() {
7 | const { shareScreen } = appStore;
8 | return ;
9 | });
10 |
--------------------------------------------------------------------------------
/examples/mobx/src/components/Users.tsx:
--------------------------------------------------------------------------------
1 | import { observer } from "mobx-react-lite";
2 |
3 | import { appStore } from "../stores/app.store";
4 |
5 | export const Users = observer(function Users() {
6 | const { localUser, remoteUsers } = appStore.users;
7 |
8 | return (
9 |
10 |
Users ({remoteUsers.length + 1})
11 |
12 |
13 |
14 | {localUser?.name} (You)
15 | {localUser?.micOn && }
16 | {localUser?.cameraOn && }
17 |
18 | {remoteUsers.map(user => (
19 |
20 |
21 | {user.name}
22 | {user.micOn && }
23 | {user.cameraOn && }
24 |
25 | ))}
26 |
27 |
28 | );
29 | });
30 |
--------------------------------------------------------------------------------
/examples/mobx/src/components/buttons.tsx:
--------------------------------------------------------------------------------
1 | export function JoinButton({ onClick, isLoading }: { onClick?: () => void; isLoading?: boolean }) {
2 | return (
3 |
4 |
5 | {isLoading ? : }
6 |
7 | Join
8 |
9 | );
10 | }
11 |
12 | export function NewButton() {
13 | return (
14 |
15 |
16 |
17 |
18 | New
19 |
20 | );
21 | }
22 |
23 | export function ScheduleButton() {
24 | return (
25 |
26 |
27 |
28 |
29 | Schedule
30 |
31 | );
32 | }
33 |
34 | export function ScreenShareButton() {
35 | return (
36 |
37 |
38 |
39 |
40 | Share Screen
41 |
42 | );
43 | }
44 |
--------------------------------------------------------------------------------
/examples/mobx/src/constants.ts:
--------------------------------------------------------------------------------
1 | import CryptoJS from "crypto-js";
2 |
3 | let id = import.meta.env.AGORA_APPID;
4 | if (import.meta.env.AGORA_AES_SALT) {
5 | // only for github-pages demo
6 | const bytes = CryptoJS.AES.decrypt(import.meta.env.AGORA_APPID, import.meta.env.AGORA_AES_SALT);
7 | id = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
8 | }
9 | export const appId = id;
10 | export const channel = import.meta.env.AGORA_CHANNEL || "test";
11 | export const token = import.meta.env.AGORA_TOKEN ? import.meta.env.AGORA_TOKEN : null;
12 |
--------------------------------------------------------------------------------
/examples/mobx/src/global.d.ts:
--------------------------------------------------------------------------------
1 | import type { AppStore } from "./stores/app.store";
2 | declare global {
3 | interface Window {
4 | appStore: AppStore;
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/examples/mobx/src/main.tsx:
--------------------------------------------------------------------------------
1 | import "uno.css";
2 | import "./styles.css";
3 |
4 | import { StrictMode } from "react";
5 | import { createRoot } from "react-dom/client";
6 |
7 | import { App } from "./components/App";
8 |
9 | createRoot(document.getElementById("root") as HTMLElement).render(
10 |
11 |
12 | ,
13 | );
14 |
--------------------------------------------------------------------------------
/examples/mobx/src/stores/app.store.ts:
--------------------------------------------------------------------------------
1 | import type { IAgoraRTCClient } from "agora-rtc-react";
2 | import AgoraRTC from "agora-rtc-react";
3 | import { makeAutoObservable } from "mobx";
4 |
5 | import { ShareScreen } from "./share-screen.store";
6 | import { Users } from "./users.store";
7 |
8 | AgoraRTC.setLogLevel(/* warning */ 2);
9 |
10 | class AppStore {
11 | client: IAgoraRTCClient | null = null;
12 | users = new Users();
13 | shareScreen = new ShareScreen();
14 |
15 | get uid() {
16 | return this.client?.uid;
17 | }
18 |
19 | constructor() {
20 | this.users.localUIDs.push(this.shareScreen.uid);
21 | makeAutoObservable(this);
22 | }
23 |
24 | async join(appid: string, channel: string, token: string | null): Promise {
25 | const client = AgoraRTC.createClient({ mode: "rtc", codec: "vp8" });
26 | await client.join(appid, channel, token, null);
27 | this._updateClient(client);
28 | }
29 |
30 | async leave(): Promise {
31 | this.users.dispose();
32 | await this.shareScreen.dispose();
33 | const client = this.client;
34 | if (client) {
35 | await client.leave();
36 | this._updateClient(null);
37 | }
38 | }
39 |
40 | private _updateClient(client: IAgoraRTCClient | null): void {
41 | this.client = client;
42 | this.users.updateClient(client);
43 | this.shareScreen.updateMainClient(client);
44 | }
45 | }
46 |
47 | export type { AppStore };
48 |
49 | export const appStore = new AppStore();
50 |
51 | if (import.meta.env.DEV) {
52 | window.appStore = appStore;
53 | }
54 |
--------------------------------------------------------------------------------
/examples/mobx/src/stores/remote-user.store.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | IAgoraRTCRemoteUser,
3 | IRemoteAudioTrack,
4 | IRemoteVideoTrack,
5 | UID,
6 | } from "agora-rtc-react";
7 | import { makeAutoObservable } from "mobx";
8 |
9 | import { fakeAvatar, fakeName } from "../utils";
10 |
11 | /**
12 | * This class extracts fields from the IAgoraRTCRemoteUser object so that mobx can track them.
13 | *
14 | * `uid`, `audioTrack`, `videoTrack` → same\
15 | * `hasAudio` → `micOn`\
16 | * `hasVideo` → `cameraOn`
17 | */
18 | export class MyRemoteUser {
19 | uid: UID;
20 | name: string;
21 | avatar: string;
22 | rtcUser: IAgoraRTCRemoteUser;
23 | cameraOn: boolean;
24 | micOn: boolean;
25 | videoTrack?: IRemoteVideoTrack;
26 | audioTrack?: IRemoteAudioTrack;
27 |
28 | constructor(rtcUser: IAgoraRTCRemoteUser) {
29 | this.uid = rtcUser.uid;
30 | this.name = fakeName(rtcUser.uid);
31 | this.avatar = fakeAvatar();
32 | this.rtcUser = rtcUser;
33 | this.micOn = rtcUser.hasAudio;
34 | this.cameraOn = rtcUser.hasVideo;
35 | this.audioTrack = rtcUser.audioTrack;
36 | this.videoTrack = rtcUser.videoTrack;
37 |
38 | makeAutoObservable(this);
39 | }
40 |
41 | update(rtcUser: IAgoraRTCRemoteUser) {
42 | this.rtcUser = rtcUser;
43 | this.micOn = rtcUser.hasAudio;
44 | this.cameraOn = rtcUser.hasVideo;
45 | this.audioTrack = rtcUser.audioTrack;
46 | this.videoTrack = rtcUser.videoTrack;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/examples/mobx/src/utils.ts:
--------------------------------------------------------------------------------
1 | import { randFirstName, seed } from "@ngneat/falso";
2 | import type { UID } from "agora-rtc-react";
3 | import type { RefObject } from "react";
4 | import { useCallback, useEffect, useRef } from "react";
5 |
6 | export const fakeName = (uid: UID): string => {
7 | seed(String(uid));
8 | const name = randFirstName();
9 | seed();
10 | return name;
11 | };
12 |
13 | export const fakeAvatar = (): string => {
14 | return "https://www.agora.io/en/wp-content/uploads/2022/10/3d-spatial-audio-icon.svg";
15 | };
16 |
17 | export function useIsUnmounted(): RefObject {
18 | const isUnmountRef = useRef(false);
19 | useEffect(() => {
20 | isUnmountRef.current = false;
21 | return () => {
22 | isUnmountRef.current = true;
23 | };
24 | }, []);
25 | return isUnmountRef;
26 | }
27 |
28 | export function useSafePromise() {
29 | const isUnmountRef = useIsUnmounted();
30 |
31 | function safePromise(
32 | promise: PromiseLike,
33 | onUnmountedError?: (error: E) => void,
34 | ) {
35 | // the async promise executor is intended
36 | // eslint-disable-next-line no-async-promise-executor
37 | return new Promise(async (resolve, reject) => {
38 | try {
39 | const result = await promise;
40 | if (!isUnmountRef.current) {
41 | resolve(result);
42 | }
43 | // unresolved promises will be garbage collected.
44 | } catch (error) {
45 | if (!isUnmountRef.current) {
46 | reject(error);
47 | } else if (onUnmountedError) {
48 | onUnmountedError(error as E);
49 | } else {
50 | if (process.env.NODE_ENV === "development") {
51 | console.error("An error occurs from a promise after a component is unmounted", error);
52 | }
53 | }
54 | }
55 | });
56 | }
57 |
58 | return useCallback(safePromise, [isUnmountRef]);
59 | }
60 |
--------------------------------------------------------------------------------
/examples/mobx/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | interface ImportMetaEnv {
4 | readonly AGORA_APPID: string;
5 | readonly AGORA_CHANNEL: string;
6 | readonly AGORA_TOKEN: string;
7 | readonly AGORA_AES_SALT: string;
8 | }
9 |
--------------------------------------------------------------------------------
/examples/mobx/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": ["src"],
20 | "references": [{ "path": "./tsconfig.node.json" }]
21 | }
22 |
--------------------------------------------------------------------------------
/examples/mobx/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/examples/mobx/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from "@vitejs/plugin-react";
2 | import presetIcons from "unocss/preset-icons";
3 | import uno from "unocss/vite";
4 | import { defineConfig } from "vite";
5 | import { viteExternalsPlugin } from "vite-plugin-externals";
6 |
7 | // https://vitejs.dev/config/
8 | export default defineConfig({
9 | base: "./",
10 | plugins: [
11 | react(),
12 | uno({ presets: [presetIcons()] }),
13 | viteExternalsPlugin({
14 | "react": "React",
15 | "react-dom": "ReactDOM",
16 | "react-dom/client": "ReactDOM",
17 | "agora-rtc-react": "AgoraRTC",
18 | }),
19 | ],
20 | envPrefix: "AGORA_",
21 | });
22 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/.storybook/main.js:
--------------------------------------------------------------------------------
1 | const { mergeConfig } = require("vite");
2 |
3 | module.exports = {
4 | stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
5 | addons: [
6 | "@storybook/addon-links",
7 | "@storybook/addon-essentials",
8 | "@storybook/addon-interactions",
9 | ],
10 | framework: {
11 | name: "@storybook/react-vite",
12 | options: {},
13 | },
14 | docs: {
15 | autodocs: "tag",
16 | },
17 | core: {
18 | disableTelemetry: true,
19 | },
20 | typescript: {
21 | reactDocgen: "react-docgen",
22 | skipBabel: true,
23 | check: false,
24 | },
25 | async viteFinal(config) {
26 | return mergeConfig(config, {
27 | resolve: {
28 | alias: {
29 | "agora-rtc-sdk-ng": require.resolve("agora-rtc-sdk-ng-fake/src/index.ts"),
30 | },
31 | },
32 | });
33 | },
34 | };
35 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/.storybook/preview-head.html:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/.storybook/preview.js:
--------------------------------------------------------------------------------
1 | import "../../shared/assets/storybook/global.scss";
2 |
3 | export const parameters = {
4 | backgrounds: {
5 | default: "white",
6 | values: [
7 | {
8 | name: "white",
9 | value: "#fff",
10 | },
11 | {
12 | name: "light",
13 | value: "rgb(248, 248, 248)",
14 | },
15 | {
16 | name: "grey",
17 | value: "#555",
18 | },
19 | {
20 | name: "dark",
21 | value: "rgb(25, 27, 31)",
22 | },
23 | ],
24 | },
25 | actions: { argTypesRegex: "^on[A-Z].*" },
26 | controls: {
27 | expanded: true,
28 | matchers: {
29 | color: /(background|color)$/i,
30 | date: /Date$/,
31 | },
32 | },
33 | options: {
34 | showPanel: true,
35 | },
36 | docs: {
37 | canvas: {
38 | sourceState: "shown",
39 | },
40 | },
41 | };
42 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "agora-rtc-react-ui",
3 | "version": "0.0.1",
4 | "private": false,
5 | "description": "Agora RTC React SDK UI",
6 | "license": "MIT",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/AgoraIO-Extensions/agora-rtc-react.git",
10 | "directory": "packages/agora-rtc-react-ui"
11 | },
12 | "keywords": [
13 | "react",
14 | "agora-rtc-react-ui"
15 | ],
16 | "bugs": {
17 | "url": "https://github.com/AgoraIO-Extensions/agora-rtc-react/issues"
18 | },
19 | "contributors": [
20 | "hyrious",
21 | "LichKing-2234",
22 | "CRIMX",
23 | "guoxianzhe"
24 | ],
25 | "main": "src/index.ts",
26 | "publishConfig": {
27 | "main": "dist/agora-rtc-react-ui.js",
28 | "module": "dist/agora-rtc-react-ui.mjs",
29 | "types": "dist/agora-rtc-react-ui.d.ts"
30 | },
31 | "source": "src/index.ts",
32 | "files": [
33 | "dist"
34 | ],
35 | "engines": {
36 | "node": ">=10"
37 | },
38 | "scripts": {
39 | "prepublishOnly": "pnpm run build",
40 | "build": "tsup",
41 | "release": "release-it",
42 | "start": "pnpm run -F example-overview start",
43 | "storybook": "storybook dev -p 6006",
44 | "build-storybook": "storybook build",
45 | "storybook-docs": "storybook dev --docs",
46 | "build-storybook-docs": "storybook build --docs",
47 | "test": "vitest run --coverage",
48 | "test:watch": "vitest --ui"
49 | },
50 | "peerDependencies": {
51 | "agora-rtc-react": ">=2",
52 | "react": ">=16.8"
53 | },
54 | "devDependencies": {
55 | "agora-rtc-react": "workspace:*"
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/src/assets/UserControl.css:
--------------------------------------------------------------------------------
1 | .arr-user-control {
2 | overflow: hidden;
3 | width: 24px;
4 | height: 24px;
5 | margin: 0;
6 | padding: 0;
7 | border: none;
8 | outline: none;
9 | border-radius: 50%;
10 | color: #fff;
11 | background: rgba(0, 0, 0, 0.5);
12 | transition: background-color 0.4s;
13 | cursor: pointer;
14 | user-select: none;
15 | }
16 |
17 | .arr-user-control:hover,
18 | .arr-user-control:focus,
19 | .arr-user-control:active {
20 | background-color: rgba(0, 0, 0, 0.6);
21 | }
22 |
23 | .arr-user-control:disabled {
24 | cursor: default;
25 | }
26 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/src/components/CameraControl.tsx:
--------------------------------------------------------------------------------
1 | import "../assets/UserControl.css";
2 |
3 | import type { ButtonHTMLAttributes, MouseEvent } from "react";
4 | import { useCallback } from "react";
5 |
6 | import { SVGCamera } from "./icons/SVGCamera";
7 | import { SVGCameraMute } from "./icons/SVGCameraMute";
8 |
9 | export interface CameraControlProps extends ButtonHTMLAttributes {
10 | /**
11 | * Camera is on.
12 | */
13 | cameraOn?: boolean;
14 | /**
15 | * Callback when camera is on/off.
16 | */
17 | onCameraChange?: (cameraOn: boolean) => void;
18 | }
19 |
20 | /**
21 | * A button with camera icon.
22 | */
23 | export function CameraControl({
24 | cameraOn,
25 | onCameraChange,
26 | onClick,
27 | className = "",
28 | ...props
29 | }: CameraControlProps) {
30 | const handleClick = useCallback(
31 | (evt: MouseEvent) => {
32 | onCameraChange?.(!cameraOn);
33 | onClick?.(evt);
34 | },
35 | [onCameraChange, onClick, cameraOn],
36 | );
37 |
38 | return (
39 |
40 | {cameraOn ? : }
41 |
42 | );
43 | }
44 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/src/components/CameraVideoTrack.tsx:
--------------------------------------------------------------------------------
1 | import type { LocalVideoTrackProps, VideoPlayerConfig } from "agora-rtc-react";
2 | import type { ICameraVideoTrack } from "agora-rtc-react";
3 | import { LocalVideoTrack } from "agora-rtc-react";
4 | import { useAwaited } from "agora-rtc-react/src/hooks/tools";
5 | import type { MaybePromiseOrNull } from "agora-rtc-react/src/misc/utils";
6 | import { useEffect } from "react";
7 |
8 | export interface CameraVideoTrackProps extends LocalVideoTrackProps {
9 | /**
10 | * A camera video track which can be created by `createCameraVideoTrack()`.
11 | */
12 | readonly track?: MaybePromiseOrNull;
13 | /**
14 | * Device ID, which can be retrieved by calling `getDevices()`.
15 | */
16 | readonly deviceId?: string;
17 |
18 | /**
19 | * Playback configurations for a video track. Set the playback configurations for a video track when calling [ILocalVideoTrack.play]{@link ILocalVideoTrack.play}.
20 | */
21 | readonly videoPlayerConfig?: VideoPlayerConfig;
22 | }
23 |
24 | /**
25 | * A component which renders a camera video track, with device options.
26 | *
27 | * ```jsx
28 | * const track = useMemo(() => AgoraRTC.createCameraVideoTrack(), [])
29 | * return
30 | * ```
31 | */
32 | export function CameraVideoTrack({
33 | track: maybeTrack,
34 | deviceId,
35 | videoPlayerConfig,
36 | ...props
37 | }: CameraVideoTrackProps) {
38 | const track = useAwaited(maybeTrack);
39 |
40 | useEffect(() => {
41 | if (track && deviceId != null) {
42 | track.setDevice(deviceId).catch(console.warn);
43 | }
44 | }, [deviceId, track]);
45 |
46 | return ;
47 | }
48 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/src/components/MicControl.tsx:
--------------------------------------------------------------------------------
1 | import "../assets/UserControl.css";
2 |
3 | import type { ILocalAudioTrack, IRemoteAudioTrack } from "agora-rtc-react";
4 | import { useVolumeLevel } from "agora-rtc-react";
5 | import type { ButtonHTMLAttributes, MouseEvent } from "react";
6 | import { useCallback } from "react";
7 |
8 | import { SVGMicrophone } from "./icons/SVGMicrophone";
9 | import { SVGMicrophoneMute } from "./icons/SVGMicrophoneMute";
10 |
11 | export interface MicControlProps extends ButtonHTMLAttributes {
12 | /**
13 | * Audio track to subscribe volume level.
14 | */
15 | audioTrack?: ILocalAudioTrack | IRemoteAudioTrack;
16 | /**
17 | * Microphone is on.
18 | */
19 | micOn?: boolean;
20 | /**
21 | * Callback when microphone is on/off.
22 | */
23 | onMicChange?: (micOn: boolean) => void;
24 | /**
25 | * Add noise to volume level for a more organic effect.
26 | */
27 | noise?: number;
28 | }
29 |
30 | /**
31 | * A button with microphone icon.
32 | * Display realtime volume level when `audioTrack` is provided.
33 | */
34 | export function MicControl({
35 | noise,
36 | audioTrack,
37 | micOn,
38 | onMicChange,
39 | onClick,
40 | className = "",
41 | ...props
42 | }: MicControlProps) {
43 | const volumeLevel = useVolumeLevel(audioTrack);
44 |
45 | const handleClick = useCallback(
46 | (evt: MouseEvent) => {
47 | onMicChange?.(!micOn);
48 | onClick?.(evt);
49 | },
50 | [onMicChange, onClick, micOn],
51 | );
52 |
53 | return (
54 |
55 | {micOn ? : }
56 |
57 | );
58 | }
59 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/src/components/MicrophoneAudioTrack.tsx:
--------------------------------------------------------------------------------
1 | import type { LocalAudioTrackProps } from "agora-rtc-react";
2 | import type { IMicrophoneAudioTrack } from "agora-rtc-react";
3 | import { LocalAudioTrack } from "agora-rtc-react";
4 | import { useAwaited } from "agora-rtc-react/src/hooks/tools";
5 | import type { MaybePromiseOrNull } from "agora-rtc-react/src/misc/utils";
6 | import type { ReactNode } from "react";
7 | import { useEffect } from "react";
8 |
9 | export interface MicrophoneAudioTrackProps extends LocalAudioTrackProps {
10 | /**
11 | * A microphone audio track which can be created by `createMicrophoneAudioTrack()`.
12 | */
13 | readonly track?: MaybePromiseOrNull;
14 | /**
15 | * Device ID, which can be retrieved by calling `getDevices()`.
16 | */
17 | readonly deviceId?: string;
18 |
19 | readonly children?: ReactNode;
20 | }
21 |
22 | /**
23 | * A component which renders a microphone audio track, with device options.
24 | *
25 | * ```jsx
26 | * const track = useMemo(() => AgoraRTC.createMicrophoneAudioTrack(), [])
27 | * return
28 | * ```
29 | */
30 | export function MicrophoneAudioTrack({
31 | track: maybeTrack,
32 | deviceId,
33 | ...props
34 | }: MicrophoneAudioTrackProps) {
35 | const track = useAwaited(maybeTrack);
36 |
37 | useEffect(() => {
38 | if (track && deviceId != null) {
39 | track.setDevice(deviceId).catch(console.warn);
40 | }
41 | }, [deviceId, track]);
42 |
43 | return ;
44 | }
45 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/src/components/icons/SVGCamera.tsx:
--------------------------------------------------------------------------------
1 | import type { SVGProps } from "react";
2 | import { memo } from "react";
3 |
4 | export type SVGCameraProps = SVGProps;
5 |
6 | export const SVGCamera = /* @__PURE__ */ memo(function SVGCamera(props) {
7 | return (
8 |
16 |
17 |
18 |
19 |
25 |
26 | );
27 | });
28 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/src/components/icons/SVGCameraMute.tsx:
--------------------------------------------------------------------------------
1 | import type { SVGProps } from "react";
2 | import { memo } from "react";
3 |
4 | export type SVGCameraMuteProps = SVGProps;
5 |
6 | export const SVGCameraMute = /* @__PURE__ */ memo(function SVGCameraMute(
7 | props,
8 | ) {
9 | return (
10 |
18 |
24 |
30 |
31 | );
32 | });
33 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/src/components/icons/SVGMicrophoneMute.tsx:
--------------------------------------------------------------------------------
1 | import type { SVGProps } from "react";
2 | import { memo } from "react";
3 |
4 | export type SVGMicrophoneMuteProps = SVGProps;
5 |
6 | export const SVGMicrophoneMute = /* @__PURE__ */ memo(
7 | function SVGMicrophoneMute(props) {
8 | return (
9 |
17 |
23 |
29 |
30 | );
31 | },
32 | );
33 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/src/components/icons/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./SVGMicrophone";
2 | export * from "./SVGMicrophoneMute";
3 | export * from "./SVGCamera";
4 | export * from "./SVGCameraMute";
5 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/src/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./icons";
2 | export * from "./MicControl";
3 | export * from "./CameraControl";
4 | export * from "./MicrophoneAudioTrack";
5 | export * from "./CameraVideoTrack";
6 | export * from "./LocalMicrophoneAndCameraUser";
7 | export * from "./RemoteVideoPlayer";
8 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./components";
2 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/src/stories/CameraControl.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from "@storybook/react";
2 |
3 | import type { CameraControlProps } from "../components";
4 | import { CameraControl } from "../components";
5 |
6 | const meta: Meta = {
7 | title: "Controls/CameraControl",
8 | component: CameraControl,
9 | tags: ["autodocs"],
10 | parameters: {
11 | backgrounds: { default: "light" },
12 | },
13 | };
14 |
15 | export default meta;
16 |
17 | export const CameraOn: StoryObj = {
18 | args: {
19 | cameraOn: true,
20 | },
21 | };
22 |
23 | export const CameraOff: StoryObj = {
24 | args: {},
25 | };
26 |
27 | export const RemoteCameraOn: StoryObj = {
28 | args: {
29 | cameraOn: true,
30 | disabled: true,
31 | },
32 | };
33 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/src/stories/CameraVideoTrack.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from "@storybook/react";
2 | import { FakeCameraVideoTrack } from "agora-rtc-sdk-ng-fake";
3 | import { useState } from "react";
4 |
5 | import type { CameraVideoTrackProps } from "../components";
6 | import { CameraVideoTrack } from "../components";
7 |
8 | const meta: Meta = {
9 | title: "Track/CameraVideoTrack",
10 | component: CameraVideoTrack,
11 | tags: ["autodocs"],
12 | parameters: {
13 | layout: "fullscreen",
14 | },
15 | argTypes: {
16 | track: {
17 | control: {
18 | type: null,
19 | },
20 | },
21 | },
22 | render: function RenderCameraVideoTrack(args) {
23 | const [track] = useState(() => (args.track ? FakeCameraVideoTrack.create() : undefined));
24 | return ;
25 | },
26 | };
27 |
28 | export default meta;
29 |
30 | export const Enabled: StoryObj = {
31 | args: {
32 | track: FakeCameraVideoTrack.create(),
33 | play: true,
34 | },
35 | };
36 |
37 | export const EmptyTrack: StoryObj = {
38 | args: {
39 | play: true,
40 | children: An Empty Track
,
41 | },
42 | };
43 |
44 | export const Disabled: StoryObj = {
45 | args: {
46 | track: FakeCameraVideoTrack.create(),
47 | play: true,
48 | disabled: true,
49 | },
50 | };
51 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/src/stories/MicControl.stories.tsx:
--------------------------------------------------------------------------------
1 | import { randNumber } from "@ngneat/falso";
2 | import type { Meta, StoryObj } from "@storybook/react";
3 | import { FakeLocalAudioTrack, FakeRemoteAudioTrack } from "agora-rtc-sdk-ng-fake";
4 | import { useEffect } from "react";
5 |
6 | import type { MicControlProps } from "../components";
7 | import { MicControl } from "../components";
8 |
9 | const meta: Meta = {
10 | title: "Controls/MicControl",
11 | component: MicControl,
12 | tags: ["autodocs"],
13 | parameters: {
14 | backgrounds: { default: "light" },
15 | },
16 | decorators: [
17 | (Story, context) => {
18 | const audioTrack = context.args.audioTrack;
19 | useEffect(() => {
20 | if (audioTrack) {
21 | const ticket = setInterval(() => {
22 | audioTrack.setVolume(randNumber({ min: 0, max: 100 }));
23 | }, 2000);
24 | return () => clearInterval(ticket);
25 | }
26 | }, [audioTrack]);
27 | return Story();
28 | },
29 | ],
30 | };
31 |
32 | export default meta;
33 |
34 | export const MicOn: StoryObj = {
35 | args: {
36 | audioTrack: FakeLocalAudioTrack.create(),
37 | micOn: true,
38 | },
39 | };
40 |
41 | export const MicOff: StoryObj = {
42 | args: {
43 | audioTrack: FakeLocalAudioTrack.create(),
44 | },
45 | };
46 |
47 | export const RemoteMicOn: StoryObj = {
48 | args: {
49 | audioTrack: FakeRemoteAudioTrack.create(),
50 | micOn: true,
51 | disabled: true,
52 | },
53 | };
54 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/src/stories/MicrophoneAudioTrack.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from "@storybook/react";
2 | import { FakeMicrophoneAudioTrack } from "agora-rtc-sdk-ng-fake";
3 |
4 | import type { MicrophoneAudioTrackProps } from "../components";
5 | import { MicrophoneAudioTrack } from "../components";
6 |
7 | const meta: Meta = {
8 | title: "Track/MicrophoneAudioTrack",
9 | component: MicrophoneAudioTrack,
10 | tags: ["autodocs"],
11 | parameters: {
12 | layout: "fullscreen",
13 | },
14 | argTypes: {
15 | track: {
16 | control: {
17 | type: null,
18 | },
19 | },
20 | },
21 | render(args) {
22 | return (
23 |
24 | An Example Microphone Audio Track
25 |
26 | );
27 | },
28 | };
29 |
30 | export default meta;
31 |
32 | export const Enabled: StoryObj = {
33 | args: {
34 | track: FakeMicrophoneAudioTrack.create(),
35 | play: true,
36 | },
37 | };
38 |
39 | export const EmptyTrack: StoryObj = {
40 | args: {
41 | play: true,
42 | },
43 | };
44 |
45 | export const Disabled: StoryObj = {
46 | args: {
47 | track: FakeMicrophoneAudioTrack.create(),
48 | play: true,
49 | disabled: true,
50 | },
51 | };
52 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/test/MicrophoneAudioTrack.test.tsx:
--------------------------------------------------------------------------------
1 | import { composeStories } from "@storybook/react";
2 | import { render } from "@testing-library/react";
3 | import type { IMicrophoneAudioTrack } from "agora-rtc-react";
4 | import { useAwaited } from "agora-rtc-react/src/hooks/tools";
5 | import type { Mock } from "vitest";
6 | import { describe, expect, test, vi } from "vitest";
7 |
8 | import { MicrophoneAudioTrack } from "../src/components";
9 | import * as stories from "../src/stories/MicrophoneAudioTrack.stories";
10 | const { Enabled } = composeStories(stories);
11 |
12 | vi.mock("agora-rtc-react/src/hooks/tools", () => ({
13 | useAwaited: vi.fn(),
14 | useIsomorphicLayoutEffect: vi.fn(),
15 | }));
16 | const mockTrack: IMicrophoneAudioTrack = {
17 | setDevice: vi.fn().mockReturnValue(Promise.resolve()),
18 | } as unknown as IMicrophoneAudioTrack;
19 | const mockUseAwaited = useAwaited as Mock;
20 |
21 | describe("MicrophoneAudioTrack component", () => {
22 | test("renders without crashing", () => {
23 | mockUseAwaited.mockReturnValueOnce(mockTrack);
24 | const { container } = render( );
25 | expect(container).toBeInTheDocument();
26 | vi.clearAllMocks();
27 | });
28 |
29 | test("sets device ID on audio track", () => {
30 | mockUseAwaited.mockReturnValueOnce(mockTrack);
31 | const deviceId = "mockDeviceId";
32 | render( );
33 | expect(mockTrack.setDevice).toHaveBeenCalledTimes(1);
34 | expect(mockTrack.setDevice).toHaveBeenCalledWith(deviceId);
35 | vi.clearAllMocks();
36 | });
37 | });
38 |
39 | describe("MicrophoneAudioTrack component stories", () => {
40 | test("renders Enabled stories", async () => {
41 | const { getByText } = render( );
42 | expect(getByText("An Example Microphone Audio Track")).toBeInTheDocument();
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/test/setup.tsx:
--------------------------------------------------------------------------------
1 | import "@testing-library/jest-dom";
2 | import type { ReactNode } from "react";
3 | import { vi } from "vitest";
4 | import "vitest-canvas-mock";
5 |
6 | /**
7 | * started agora-rtc-sdk-ng@17.0.0, need mock RTCPeerConnection.
8 | * RTCPeerConnection does not implement global
9 | */
10 | // @ts-expect-error type
11 | global.RTCPeerConnection = vi.fn();
12 |
13 | /**
14 | * JSDOM does not implement global "HTMLMediaElement.prototype.play" function
15 | */
16 | HTMLMediaElement.prototype.play = vi.fn().mockReturnValue(Promise.resolve());
17 | HTMLMediaElement.prototype.pause = vi.fn().mockReturnValue(Promise.resolve());
18 |
19 | /**
20 | * navigator does not implement global "mediaDevices.prototype.getUserMedia" function
21 | * navigator does not implement global "mediaDevices.prototype.enumerateDevices" function
22 | *
23 | */
24 | const mockPromise = vi.fn(async () => {
25 | return new Promise(resolve => {
26 | resolve();
27 | });
28 | });
29 | Object.defineProperty(global.navigator, "mediaDevices", {
30 | value: {
31 | getUserMedia: mockPromise,
32 | enumerateDevices: mockPromise,
33 | },
34 | });
35 |
36 | export interface Props {
37 | children: ReactNode;
38 | }
39 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "noEmit": true,
4 | "strict": true,
5 | "noFallthroughCasesInSwitch": true,
6 | "forceConsistentCasingInFileNames": true,
7 | "noUnusedParameters": true,
8 | "noImplicitOverride": true,
9 | "module": "ESNext",
10 | "target": "ESNext",
11 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
12 | "moduleResolution": "Node",
13 | "esModuleInterop": true,
14 | "resolveJsonModule": true,
15 | "skipLibCheck": true,
16 | "jsx": "react-jsx"
17 | },
18 | "include": ["src", "test"]
19 | }
20 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "tsup";
2 |
3 | import setGlobals from "../../scripts/tsup/set-globals";
4 |
5 | import pkg from "./package.json";
6 |
7 | const banner = `
8 | /**
9 | * @license ${pkg.name}
10 | * @version ${pkg.version}
11 | *
12 | * Copyright (c) Agora, Inc.
13 | *
14 | * This source code is licensed under the MIT license.
15 | */
16 | `;
17 |
18 | export default defineConfig([
19 | {
20 | entry: {
21 | [pkg.name]: "src/index.ts",
22 | },
23 | banner: () => {
24 | return {
25 | js: banner,
26 | };
27 | },
28 | format: ["cjs", "esm"],
29 | splitting: false,
30 | sourcemap: false,
31 | clean: true,
32 | treeshake: true,
33 | dts: true,
34 | minify: false,
35 | },
36 | {
37 | entry: {
38 | [pkg.name]: "src/index.ts",
39 | },
40 | banner: () => {
41 | return {
42 | js: banner,
43 | };
44 | },
45 | outExtension: () => {
46 | return {
47 | js: `.iife.js`,
48 | };
49 | },
50 | format: ["iife"],
51 | sourcemap: false,
52 | splitting: false,
53 | clean: true,
54 | minify: true,
55 | external: Object.keys(pkg.peerDependencies),
56 | define: {
57 | "process.env.NODE_ENV": JSON.stringify("production"),
58 | },
59 | globalName: "AgoraRTCReactUIKit",
60 | esbuildPlugins: [
61 | setGlobals({
62 | "react": "React",
63 | "react-dom": "ReactDOM",
64 | "agora-rtc-sdk-ng": "AgoraRTC",
65 | }),
66 | ],
67 | platform: "browser",
68 | },
69 | ]);
70 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react-ui/vite.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 |
3 | ///
4 |
5 | import react from "@vitejs/plugin-react";
6 | import { defineConfig } from "vite";
7 |
8 | export default defineConfig({
9 | plugins: [react()],
10 | test: {
11 | onConsoleLog(log) {
12 | if (log.includes("Agora RTC client not found")) return false;
13 | if (log.includes("Agora-SDK [DEBUG]: ")) return false;
14 | if (log.includes("Agora-SDK [WARNING]: ")) return false;
15 | if (log.includes("Agora-SDK [ERROR]: ")) return false;
16 | if (log.includes("Agora-SDK [INFO]: ")) return false;
17 | if (log.includes("Agora-RTC-REACT [ERROR_TEST_MSG]")) return false;
18 | },
19 | environment: "jsdom",
20 | globals: true,
21 | coverage: {
22 | provider: "v8",
23 | reporter: ["text", "json", "html", "lcov", "json-summary"],
24 | exclude: ["test/**", "src/stories/*", "src/**/index.ts"],
25 | },
26 | exclude: ["**/node_modules/**"],
27 | deps: {
28 | inline: ["vitest-canvas-mock"],
29 | },
30 | setupFiles: ["../shared/test/setup.tsx"],
31 | },
32 | });
33 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/.storybook/main.ts:
--------------------------------------------------------------------------------
1 | const { mergeConfig } = require("vite");
2 |
3 | module.exports = {
4 | stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
5 | addons: [
6 | "@storybook/addon-essentials",
7 | "@storybook/addon-links",
8 | "@storybook/addon-interactions",
9 | ],
10 | framework: {
11 | name: "@storybook/react-vite",
12 | options: {},
13 | },
14 | docs: {
15 | autodocs: "tag",
16 | defaultName: "Docs", // doc name
17 | },
18 | core: {
19 | disableTelemetry: true,
20 | },
21 | typescript: {
22 | reactDocgen: "react-docgen",
23 | skipBabel: true,
24 | check: false,
25 | },
26 | async viteFinal(config) {
27 | return mergeConfig(config, {
28 | resolve: {
29 | alias: {
30 | "agora-rtc-sdk-ng": require.resolve("agora-rtc-sdk-ng-fake/src/index.ts"),
31 | },
32 | },
33 | });
34 | },
35 | };
36 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/.storybook/preview-head.html:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/.storybook/preview.ts:
--------------------------------------------------------------------------------
1 | import "../../shared/assets/storybook/global.scss";
2 |
3 | export const parameters = {
4 | backgrounds: {
5 | default: "white",
6 | values: [
7 | {
8 | name: "white",
9 | value: "#fff",
10 | },
11 | {
12 | name: "light",
13 | value: "rgb(248, 248, 248)",
14 | },
15 | {
16 | name: "grey",
17 | value: "#555",
18 | },
19 | {
20 | name: "dark",
21 | value: "rgb(25, 27, 31)",
22 | },
23 | ],
24 | },
25 | actions: { argTypesRegex: "^on[A-Z].*" },
26 | controls: {
27 | expanded: true,
28 | matchers: {
29 | color: /(background|color)$/i,
30 | date: /Date$/,
31 | },
32 | },
33 | options: {
34 | showPanel: true,
35 | },
36 | docs: {
37 | canvas: {
38 | sourceState: "shown",
39 | },
40 | },
41 | };
42 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/components/AgoraRTCProvider.en-US.mdx:
--------------------------------------------------------------------------------
1 | ## AgoraRTCProvider
2 |
3 | This component is a [context provider](https://react.dev/learn/passing-data-deeply-with-context), which lets all of the components inside `children` read the `client` prop you pass.
4 |
5 | #### Props
6 |
7 | | Prop | Type | Default value | Description |
8 | | ---------- | -------------------------------------------------------------------------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
9 | | `client` | [`IAgoraRTCClient`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartcclient.html) | None | Created using the Web SDK's [`IAgoraRTC.createClient`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartc.html#createclient) method. |
10 | | `children` | `ReactNode` | None | The React nodes to be rendered. |
11 |
12 | #### Sample code
13 |
14 | ```jsx
15 | import AgoraRTC, { AgoraRTCProvider } from "agora-rtc-react";
16 |
17 | function App({ children }) {
18 | const [client] = useState(() => AgoraRTC.createClient({ mode: "rtc", codec: "vp8" }));
19 | return {children} ;
20 | }
21 | ```
22 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/components/AgoraRTCProvider.zh-CN.mdx:
--------------------------------------------------------------------------------
1 | ## AgoraRTCProvider
2 |
3 | 该组件向子组件[提供 Context](https://react.dev/learn/passing-data-deeply-with-context),让 `children` 内的各个组件都能读取你传入的 `client` 属性。
4 |
5 | #### Props
6 |
7 | | 属性名 | 类型 | 默认值 | 描述 |
8 | | ---------- | ----------------------------------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------ |
9 | | `client` | [`IAgoraRTCClient`](/api-ref/rtc/react/interfaces/iagorartcclient.html) | 无 | 通过 Web SDK 的 [`IAgoraRTC.createClient`](/api-ref/rtc/react/interfaces/iagorartc.html#createclient) 创建。 |
10 | | `children` | `ReactNode` | 无 | 需要展示的 React 节点。 |
11 |
12 | #### 使用示例
13 |
14 | ```jsx
15 | import AgoraRTC, { AgoraRTCProvider } from "agora-rtc-react";
16 |
17 | function App({ children }) {
18 | const [client] = useState(() => AgoraRTC.createClient({ mode: "rtc", codec: "vp8" }));
19 | return {children} ;
20 | }
21 | ```
22 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/components/AgoraRTCScreenShareProvider.en-US.mdx:
--------------------------------------------------------------------------------
1 | ## AgoraRTCScreenShareProvider
2 |
3 | This component is a [context provider](https://react.dev/learn/passing-data-deeply-with-context), which lets all of the components inside `children` read the `client` prop you pass for screen sharing.
4 |
5 | #### Props
6 |
7 | | Prop | Type | Default value | Description |
8 | | ---------- | -------------------------------------------------------------------------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
9 | | `client` | [`IAgoraRTCClient`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartcclient.html) | None | Created using the Web SDK's [`IAgoraRTC.createClient`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartc.html#createclient) method. |
10 | | `children` | `ReactNode` | None | The React nodes to be rendered. |
11 |
12 | #### Caveats
13 |
14 | You can use `AgoraRTCScreenShareProvider` and `AgoraRTCProvider` together, but they do not share the `client` prop.
15 |
16 | #### Sample code
17 |
18 | ```jsx
19 | import AgoraRTC, { AgoraRTCScreenShareProvider } from "agora-rtc-react";
20 |
21 | function App({ children }) {
22 | const [client] = useState(() => AgoraRTC.createClient({ mode: "rtc", codec: "vp8" }));
23 | return {children} ;
24 | }
25 | ```
26 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/components/AgoraRTCScreenShareProvider.zh-CN.mdx:
--------------------------------------------------------------------------------
1 | ## AgoraRTCScreenShareProvider
2 |
3 | 该组件向子组件[提供 Context](https://react.dev/learn/passing-data-deeply-with-context),让 `children` 内的各个组件都能读取你传入的用于屏幕共享的 `client` 属性。
4 |
5 | #### Props
6 |
7 | | 属性名 | 类型 | 默认值 | 描述 |
8 | | ---------- | ----------------------------------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------ |
9 | | `client` | [`IAgoraRTCClient`](/api-ref/rtc/react/interfaces/iagorartcclient.html) | 无 | 通过 Web SDK 的 [`IAgoraRTC.createClient`](/api-ref/rtc/react/interfaces/iagorartc.html#createclient) 创建。 |
10 | | `children` | `ReactNode` | 无 | 需要展示的 React 节点。 |
11 |
12 | #### 注意事项
13 |
14 | 你可以同时使用 `AgoraRTCScreenShareProvider` 和 `AgoraRTCProvider` ,但二者不会共享 `client` 属性。
15 |
16 | #### 使用示例
17 |
18 | ```jsx
19 | import AgoraRTC, { AgoraRTCScreenShareProvider } from "agora-rtc-react";
20 |
21 | function App({ children }) {
22 | const [client] = useState(() => AgoraRTC.createClient({ mode: "rtc", codec: "vp8" }));
23 | return {children} ;
24 | }
25 | ```
26 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/components/RemoteVideoTrack.zh-CN.mdx:
--------------------------------------------------------------------------------
1 | ## RemoteVideoTrack
2 |
3 | 该组件用于播放远端用户的视频轨道,并且不支持指定播放设备。
4 |
5 | #### Props
6 |
7 | | 属性名 | 类型 | 默认值 | 描述 |
8 | | ------------------- | --------------------------------------------------------------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
9 | | `track` | [`IRemoteVideoTrack`](/api-ref/rtc/react/interfaces/iremotevideotrack.html) | 无 | 远端视频轨道对象。 |
10 | | `play` | `boolean` | 无 | `true`:播放该轨道。 `false`:停止播放该轨道。 |
11 | | `videoPlayerConfig` | `VideoPlayerConfig` | 无 | 视频轨道的播放配置。可以设置播放参数(镜像/显示模式),详见 [`VideoPlayerConfig`](/api-ref/rtc/react/interfaces/videoplayerconfig.html)。 |
12 |
13 | #### 使用示例
14 |
15 | ```jsx
16 | import { RemoteAudioTrack, useJoin, useRemoteAudioTracks, useRemoteUsers } from "agora-rtc-react";
17 |
18 | function App() {
19 | const remoteUsers = useRemoteUsers();
20 | const audioTracks = useRemoteAudioTracks(remoteUsers);
21 |
22 | return (
23 | <>
24 | {audioTracks.map(track => (
25 |
26 | ))}
27 | >
28 | );
29 | }
30 | ```
31 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/data-types/AgoraRTCReactError.en-US.mdx:
--------------------------------------------------------------------------------
1 | ## AgoraRTCReactError
2 |
3 | Thrown errors.
4 |
5 | `AgoraRTCReactError` extends the browser's [Error object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error). When you directly print the `AgoraRTCReactError` object, you can see the error message.
6 |
7 | | Property | Type | Required | Description |
8 | | ----------- | -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
9 | | `rtcMethod` | `string` | Yes | The Web SDK method that throws the error, which helps you determine the corresponding Hook where the error occurred. See the table below. |
10 |
11 | **Mapping of `rtcMethod` to Hooks**
12 |
13 | | `rtcMethod` | Corresponding Hook |
14 | | ------------------------------------------------------------------------------------------------------- | ------------------------- |
15 | | `"IAgoraRTCClient.join"` | `useJoin` |
16 | | `"IAgoraRTC.createCameraVideoTrack" ` | `useLocalCameraTrack` |
17 | | `"IAgoraRTC.createMicrophoneAudioTrack"` | `useLocalMicrophoneTrack` |
18 | | `"IAgoraRTCClient.publish"` | `usePublish` |
19 | | `"IAgoraRTCClient.unsubscribe"` or `"IAgoraRTCClient.subscribe"` | `useRemoteUserTrack` |
20 | | `"IAgoraRTCClient.unsubscribe"` or `"IAgoraRTCClient.subscribe"` or `"IAgoraRTCClient.massUnsubscribe"` | `useRemoteVideoTracks ` |
21 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/data-types/AgoraRTCReactError.zh-CN.mdx:
--------------------------------------------------------------------------------
1 | ## AgoraRTCReactError
2 |
3 | 抛出的错误。
4 |
5 | `AgoraRTCReactError` 继承自浏览器的 [Error 对象](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)。直接打印 `AgoraRTCReactError` 对象可以看到错误信息。
6 |
7 | | 属性 | 类型 | 是否必选 | 描述 |
8 | | ----------- | -------- | -------- | ----------------------------------------------------------------------------------------- |
9 | | `rtcMethod` | `string` | 必选 | 发生错误的 Web SDK 方法名。根据 Web SDK 的方法名可以定位发生错误的 Hook,对应关系见下表。 |
10 |
11 | **`rtcMethod` 与 Hook 的关系表**
12 |
13 | | `rtcMethod` | 发生错误的 Hook |
14 | | ------------------------------------------------------------------------------------------------------- | ------------------------- |
15 | | `"IAgoraRTCClient.join"` | `useJoin` |
16 | | `"IAgoraRTC.createCameraVideoTrack" ` | `useLocalCameraTrack` |
17 | | `"IAgoraRTC.createMicrophoneAudioTrack"` | `useLocalMicrophoneTrack` |
18 | | `"IAgoraRTCClient.publish"` | `usePublish` |
19 | | `"IAgoraRTCClient.unsubscribe"` 或 `"IAgoraRTCClient.subscribe"` | `useRemoteUserTrack` |
20 | | `"IAgoraRTCClient.unsubscribe"` 或 `"IAgoraRTCClient.subscribe"` 或 `"IAgoraRTCClient.massUnsubscribe"` | `useRemoteVideoTracks ` |
21 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/data-types/JoinOptions.zh-CN.mdx:
--------------------------------------------------------------------------------
1 | ## JoinOptions
2 |
3 | 加入频道所需参数。
4 |
5 | | 属性 | 类型 | 是否必选 | 描述 |
6 | | --------- | ------------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
7 | | `appid` | `string` | 必选 | 声网项目的 App ID。 |
8 | | `channel` | `string` | Yes | 要加入的频道名称。详见 [`IAgoraRTCClient.join`](/api-ref/rtc/react/interfaces/iagorartcclient.html#join) 的参数说明。 |
9 | | `token` | `string` | `null` | Yes | 用于鉴权的 Token,如果你的声网项目启用了 Token 鉴权机制,则需要提供有效的 Token;如果没有启用 Token 鉴权,则传入 `null`。详见 [`IAgoraRTCClient.join`](/api-ref/rtc/react/interfaces/iagorartcclient.html#join) 的参数说明。 |
10 | | `uid` | `UID` |`null` | No | 用户 ID,如果不提供则由服务器自动生成一个 number 型的 `uid`。详见 [`IAgoraRTCClient.join`](/api-ref/rtc/react/interfaces/iagorartcclient.html#join) 的参数说明。 |
11 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/data-types/NetworkQuality.en-US.mdx:
--------------------------------------------------------------------------------
1 | ## NetworkQuality
2 |
3 | The last-mile network quality.
4 |
5 | This interface inherits [`NetworkQuality`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/networkquality.html) from the Web SDK and adds the following additional properties:
6 |
7 | | Property | Type | Required | Description |
8 | | -------- | -------- | -------- | ------------------------------------------------------------------------------------------------------- |
9 | | `delay` | `number` | Yes | The average Round-Trip Time (RTT) from the SDK to the Agora edge server, measured in milliseconds (ms). |
10 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/data-types/NetworkQualityEx.zh-CN.mdx:
--------------------------------------------------------------------------------
1 | ## NetworkQualityEx
2 |
3 | 上下行 last mile 网络质量。
4 |
5 | 该接口类继承自 Web SDK 的 [`NetworkQuality`](/api-ref/rtc/react/interfaces/networkquality.html),并新增以下属性:
6 |
7 | | 属性 | 类型 | 是否必选 | 描述 |
8 | | ------- | -------- | -------- | --------------------------------------------------------------- |
9 | | `delay` | `number` | 必选 | SDK 到声网边缘服务器的平均往返时延(Round-Trip Time),单位 ms。 |
10 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/hooks/useAutoPlayAudioTrack.en-US.mdx:
--------------------------------------------------------------------------------
1 | ## useAutoPlayAudioTrack
2 |
3 | This hook lets you automatically play a local or remote audio track.
4 |
5 | - When the component is mounted, this hook determines whether to automatically play the track according to the `play` parameter.
6 | - When the component is unmounted, this hook stops playing the `track`.
7 |
8 | #### Parameters
9 |
10 | | Parameter | Type | Required | Description |
11 | | --------- | ----------------------------------------------------- | -------- | ------------------------------------------------------------------------- |
12 | | `track` | `IRemoteAudioTrack` | `ILocalAudioTrack` | Yes | The local or remote audio track. |
13 | | `play` | `boolean` | No | `true`: Play the track. `false`: Stop playing the track. |
14 |
15 | #### Returns
16 |
17 | None.
18 |
19 | #### Sample code
20 |
21 | ```jsx
22 | import { useAutoPlayAudioTrack, useLocalMicrophoneTrack } from "agora-rtc-react";
23 |
24 | function App() {
25 | const audioTrack = useLocalMicrophoneTrack();
26 | useAutoPlayAudioTrack(track, play);
27 |
28 | return <>>;
29 | }
30 | ```
31 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/hooks/useAutoPlayAudioTrack.zh-CN.mdx:
--------------------------------------------------------------------------------
1 | ## useAutoPlayAudioTrack
2 |
3 | 用于自动播放本地或远端音频轨道。
4 |
5 | - 当组件挂载时,该 Hook 会根据传入的 `play` 判断是否自动播放。
6 | - 当组件卸载时,该 Hook 会停止播放 `track` 对应的音频轨道。
7 |
8 | #### 参数
9 |
10 | | 参数名 | 类型 | 是否必选 | 描述 |
11 | | ------- | ----------------------------------------------------- | -------- | --------------------------------------------------------------- |
12 | | `track` | `IRemoteAudioTrack` | `ILocalAudioTrack` | 必选 | 远端音频轨道对象或本地音频轨道对象。 |
13 | | `play` | `boolean` | 可选 | `true`:播放该轨道。 `false`:停止播放该轨道。 |
14 |
15 | #### 返回值
16 |
17 | 无。
18 |
19 | #### 使用示例
20 |
21 | ```jsx
22 | import { useAutoPlayAudioTrack, useLocalMicrophoneTrack } from "agora-rtc-react";
23 |
24 | function App() {
25 | const audioTrack = useLocalMicrophoneTrack();
26 | useAutoPlayAudioTrack(track, play);
27 |
28 | return <>>;
29 | }
30 | ```
31 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/hooks/useAutoPlayVideoTrack.zh-CN.mdx:
--------------------------------------------------------------------------------
1 | ## useAutoPlayVideoTrack
2 |
3 | 用于自动播放本地或远端视频轨道。
4 |
5 | - 当组件挂载时,该 Hook 会根据传入的 `play` 判断是否自动播放。
6 | - 当组件卸载时,该 Hook 会停止播放 `track` 对应的视频轨道。
7 |
8 | #### 参数
9 |
10 | | 参数名 | 类型 | 是否必选 | 描述 |
11 | | ------------------- | ----------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
12 | | `track` | `IRemoteVideoTrack` | `ILocalVideoTrack` | 必选 | 远端视频轨道对象或本地视频轨道对象。 |
13 | | `play` | `boolean` | 可选 | `true`:播放该轨道。 `false`:停止播放该轨道。 |
14 | | `videoPlayerConfig` | `VideoPlayerConfig` | 可选 | 视频轨道的播放配置。可以设置播放参数(镜像/显示模式),详见 [`VideoPlayerConfig`](/api-ref/rtc/react/interfaces/videoplayerconfig.html)。对于本地视频轨道,镜像模式默认开启。 |
15 | | `div` | `HTMLElement` | `null` | 可选 | 用于渲染视频轨道的 HTML 元素。只有在 `play` 为 `true` 并且传入 `div` 参数的情况下,视频才会在该元素中自动播放。其它情况则不会自动播放视频。 |
16 |
17 | #### 返回值
18 |
19 | 无。
20 |
21 | #### 使用示例
22 |
23 | ```jsx
24 | import { useAutoPlayVideoTrack, useLocalCameraTrack } from "agora-rtc-react";
25 |
26 | function App() {
27 | const videoTrack = useLocalCameraTrack();
28 | useAutoPlayVideoTrack(track, play, div);
29 |
30 | return <>>;
31 | }
32 | ```
33 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/hooks/useClientEvent.zh-CN.mdx:
--------------------------------------------------------------------------------
1 | ## useClientEvent
2 |
3 | 用于监听 `IAgoraRTCClient` 对象的指定事件。
4 |
5 | - 当组件挂载时,该 Hook 会注册对应的事件监听器。
6 | - 当组件卸载时,该 Hook 会销毁对应的事件监听器。
7 |
8 | #### 参数
9 |
10 | | 参数名 | 类型 | 是否必选 | 描述 |
11 | | ---------- | ----------------------------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
12 | | `client` | [`IAgoraRTCClient`](/api-ref/rtc/react/interfaces/iagorartcclient.html) | 必选 | 通过 Web SDK 的 [`IAgoraRTC.createClient`](/api-ref/rtc/react/interfaces/iagorartc.html#createclient) 创建。 |
13 | | `event` | `string` | 必选 | 事件名称。支持的事件详见[`IAgoraRTCClient.on`](/api-ref/rtc/react/interfaces/iagorartcclient.html?platform=All%20Platforms#on)。 |
14 | | `listener` | `Function` | 必选 | 回调函数。当传入的事件触发时,该函数会被调用。支持的回调详见 [`IAgoraRTCClient.on`](/api-ref/rtc/react/interfaces/iagorartcclient.html?platform=All%20Platforms#on)。 |
15 |
16 | #### 返回值
17 |
18 | 无。
19 |
20 | #### 使用示例
21 |
22 | ```jsx
23 | import { useRTCClient, useClientEvent } from "agora-rtc-react";
24 |
25 | function App() {
26 | const client = useRTCClient();
27 | useClientEvent(client, "connection-state-change", () => {});
28 |
29 | return <>>;
30 | }
31 | ```
32 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/hooks/useConnectionState.en-US.mdx:
--------------------------------------------------------------------------------
1 | ## useConnectionState
2 |
3 | Returns the detailed connection state of the SDK.
4 |
5 | #### Parameters
6 |
7 | | Parameter | Type | Required | Description |
8 | | --------- | ------------------------------------------------------------------------------------------------------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
9 | | `client` | [`IAgoraRTCClient`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartcclient.html) | `null` | No | Created using the Web SDK's [`IAgoraRTC.createClient`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartc.html#createclient) method. |
10 |
11 | #### Returns
12 |
13 | | Type | Description |
14 | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
15 | | `ConnectionState` | The connection state between the SDK and Agora's edge server. See [`ConnectionState`](https://api-ref.agora.io/en/video-sdk/web/4.x/globals.html#connectionstate) for details. |
16 |
17 | #### Sample code
18 |
19 | ```jsx
20 | import { useConnectionState } from "agora-rtc-react";
21 |
22 | function App() {
23 | const connectionState = useConnectionState();
24 |
25 | return {connectionState}
;
26 | }
27 | ```
28 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/hooks/useConnectionState.zh-CN.mdx:
--------------------------------------------------------------------------------
1 | ## useConnectionState
2 |
3 | 用于获取详细的 SDK 连接状态。
4 |
5 | #### 参数
6 |
7 | | 参数名 | 类型 | 是否必选 | 描述 |
8 | | -------- | --------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------ |
9 | | `client` | [`IAgoraRTCClient`](/api-ref/rtc/react/interfaces/iagorartcclient.html) | `null` | 可选 | 通过 Web SDK 的 [`IAgoraRTC.createClient`](/api-ref/rtc/react/interfaces/iagorartc.html#createclient) 创建。 |
10 |
11 | #### 返回值
12 |
13 | | 类型 | 描述 |
14 | | ----------------- | --------------------------------------------------------------------------------------------------- |
15 | | `ConnectionState` | SDK 与服务器的连接状态。详见 [`ConnectionState`](/api-ref/rtc/react/globals.html#connectionstate)。 |
16 |
17 | #### 使用示例
18 |
19 | ```jsx
20 | import { useConnectionState } from "agora-rtc-react";
21 |
22 | function App() {
23 | const connectionState = useConnectionState();
24 |
25 | return {connectionState}
;
26 | }
27 | ```
28 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/hooks/useCurrentUID.en-US.mdx:
--------------------------------------------------------------------------------
1 | ## useCurrentUID
2 |
3 | Returns the current user ID.
4 |
5 | #### Parameters
6 |
7 | | Parameter | Type | Required | Description |
8 | | --------- | ------------------------------------------------------------------------------------------------------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
9 | | `client` | [`IAgoraRTCClient`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartcclient.html) | `null` | No | Created using the Web SDK's [`IAgoraRTC.createClient`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartc.html#createclient) method. |
10 |
11 | #### Returns
12 |
13 | | Type | Description |
14 | | -------------------------------- | --------------------------------------------------------------------------------------------------------- |
15 | | `UID` | `undefined` | The user ID of the current user. If the current user has not joined any channel, `undefined` is returned. |
16 |
17 | #### Sample code
18 |
19 | ```jsx
20 | import { useCurrentUID } from "agora-rtc-react";
21 |
22 | function App() {
23 | const uid = useCurrentUID();
24 |
25 | return {uid}
;
26 | }
27 | ```
28 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/hooks/useCurrentUID.zh-CN.mdx:
--------------------------------------------------------------------------------
1 | ## useCurrentUID
2 |
3 | 用于获取当前用户 UID 。
4 |
5 | #### 参数
6 |
7 | | 参数名 | 类型 | 是否必选 | 描述 |
8 | | -------- | --------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------ |
9 | | `client` | [`IAgoraRTCClient`](/api-ref/rtc/react/interfaces/iagorartcclient.html) | `null` | 可选 | 通过 Web SDK 的 [`IAgoraRTC.createClient`](/api-ref/rtc/react/interfaces/iagorartc.html#createclient) 创建。 |
10 |
11 | #### 返回值
12 |
13 | | 类型 | 描述 |
14 | | -------------------------------- | ------------------------------------------------------------------ |
15 | | `UID` | `undefined` | 当前用户的 UID。如果当前用户没有加入任何频道,则返回 `undefined`。 |
16 |
17 | #### 使用示例
18 |
19 | ```jsx
20 | import { useCurrentUID } from "agora-rtc-react";
21 |
22 | function App() {
23 | const uid = useCurrentUID();
24 |
25 | return {uid}
;
26 | }
27 | ```
28 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/hooks/useIsConnected.en-US.mdx:
--------------------------------------------------------------------------------
1 | ## useIsConnected
2 |
3 | Returns whether the SDK is connected to Agora's server.
4 |
5 | #### Parameters
6 |
7 | | Parameter | Type | Required | Description |
8 | | --------- | ------------------------------------------------------------------------------------------------------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
9 | | `client` | [`IAgoraRTCClient`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartcclient.html) | `null` | No | Created using the Web SDK's [`IAgoraRTC.createClient`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartc.html#createclient) method. |
10 |
11 | #### Returns
12 |
13 | | Type | Description |
14 | | --------- | ------------------------------------------------------------------------------------------------------------- |
15 | | `boolean` | `true`: The SDK is connected to the server. `false`: The SDK is not connected to the server. |
16 |
17 | #### Sample code
18 |
19 | ```jsx
20 | import { useIsConnected } from "agora-rtc-react";
21 |
22 | function App() {
23 | const isConnected = useIsConnected();
24 |
25 | return {isConnected}
;
26 | }
27 | ```
28 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/hooks/useIsConnected.zh-CN.mdx:
--------------------------------------------------------------------------------
1 | ## useIsConnected
2 |
3 | 用于获取 SDK 是否连接到服务器。
4 |
5 | #### 参数
6 |
7 | | 参数名 | 类型 | 是否必选 | 描述 |
8 | | -------- | --------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------ |
9 | | `client` | [`IAgoraRTCClient`](/api-ref/rtc/react/interfaces/iagorartcclient.html) | `null` | 可选 | 通过 Web SDK 的 [`IAgoraRTC.createClient`](/api-ref/rtc/react/interfaces/iagorartc.html#createclient) 创建。 |
10 |
11 | #### 返回值
12 |
13 | | 类型 | 描述 |
14 | | --------- | ----------------------------------------------------------------------------- |
15 | | `boolean` | `true`:SDK 已连接到服务器。 `false`:SDK 没有连接到服务器。 |
16 |
17 | #### 使用示例
18 |
19 | ```jsx
20 | import { useIsConnected } from "agora-rtc-react";
21 |
22 | function App() {
23 | const isConnected = useIsConnected();
24 |
25 | return {isConnected}
;
26 | }
27 | ```
28 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/hooks/useNetworkQuality.en-US.mdx:
--------------------------------------------------------------------------------
1 | ## useNetworkQuality
2 |
3 | Returns the network quality of the local user.
4 |
5 | #### Parameters
6 |
7 | | Parameter | Type | Required | Description |
8 | | --------- | ------------------------------------------------------------------------------------------------------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
9 | | `client` | [`IAgoraRTCClient`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartcclient.html) | `null` | No | Created using the Web SDK's [`IAgoraRTC.createClient`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartc.html#createclient) method. |
10 |
11 | #### Returns
12 |
13 | | Type | Description |
14 | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
15 | | `NetworkQuality` | The network quality of the local user. See [`NetworkQuality`](https://api-ref.agora.io/en/video-sdk/reactjs/2.x/interfaces/NetworkQuality.html) for details. |
16 |
17 | #### Sample code
18 |
19 | ```jsx
20 | import { useNetworkQuality } from "agora-rtc-react";
21 |
22 | function App() {
23 | const networkQuality = useNetworkQuality();
24 |
25 | return {networkQuality}
;
26 | }
27 | ```
28 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/hooks/useNetworkQuality.zh-CN.mdx:
--------------------------------------------------------------------------------
1 | ## useNetworkQuality
2 |
3 | 用于获取本地用户网络质量。
4 |
5 | #### 参数
6 |
7 | | 参数名 | 类型 | 是否必选 | 描述 |
8 | | -------- | --------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------ |
9 | | `client` | [`IAgoraRTCClient`](/api-ref/rtc/react/interfaces/iagorartcclient.html) | `null` | 可选 | 通过 Web SDK 的 [`IAgoraRTC.createClient`](/api-ref/rtc/react/interfaces/iagorartc.html#createclient) 创建。 |
10 |
11 | #### 返回值
12 |
13 | | 类型 | 描述 |
14 | | ---------------- | --------------------------------------------------------------------------------------------------------------------------------- |
15 | | `NetworkQuality` | 本地用户的网络质量信息。详见 [`NetworkQuality`](https://doc.shengwang.cn/api-ref/rtc/react/react-sdk/data-types#networkquality)。 |
16 |
17 | #### 使用示例
18 |
19 | ```jsx
20 | import { useNetworkQuality } from "agora-rtc-react";
21 |
22 | function App() {
23 | const networkQuality = useNetworkQuality();
24 |
25 | return {networkQuality}
;
26 | }
27 | ```
28 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/hooks/useRTCClient.en-US.mdx:
--------------------------------------------------------------------------------
1 | ## useRTCClient
2 |
3 | Returns the `IAgoraRTCClient` object.
4 |
5 | #### Parameters
6 |
7 | | Parameter | Type | Required | Description |
8 | | --------- | ------------------------------------------------------------------------------------------------------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
9 | | `client` | [`IAgoraRTCClient`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartcclient.html) | `null` | No | If provided, the passed `IAgoraRTCClient` object is returned. If not provided, the `IAgoraRTCClient` object obtained from the [parent component's context](https://api-ref.agora.io/en/video-sdk/reactjs/2.x/functions/AgoraRTCProvider.html) is returned. |
10 |
11 | #### Returns
12 |
13 | | Type | Description |
14 | | -------------------------------------------------------------------------------------------------- | ----------------------------- |
15 | | [`IAgoraRTCClient`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartcclient.html) | The `IAgoraRTCClient` client. |
16 |
17 | #### Sample code
18 |
19 | ```jsx
20 | import { useRTCClient } from "agora-rtc-react";
21 |
22 | function App() {
23 | const client = useRTCClient();
24 |
25 | return <>>;
26 | }
27 | ```
28 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/hooks/useRTCClient.zh-CN.mdx:
--------------------------------------------------------------------------------
1 | ## useRTCClient
2 |
3 | 用于获取 `IAgoraRTCClient` 对象。
4 |
5 | #### 参数
6 |
7 | | 参数名 | 类型 | 是否必选 | 描述 |
8 | | -------- | --------------------------------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
9 | | `client` | [`IAgoraRTCClient`](/api-ref/rtc/react/interfaces/iagorartcclient.html) | `null` | 可选 | 如果传入该参数,则使用传入的 `IAgoraRTCClient` 对象;如果不传入该参数,则使用从[父组件的 Context](https://doc.shengwang.cn/api-ref/rtc/react/react-sdk/components#agorartcprovider)中获取的 `IAgoraRTCClient` 对象。 |
10 |
11 | #### 返回值
12 |
13 | | 类型 | 描述 |
14 | | ----------------------------------------------------------------------- | ------------------------ |
15 | | [`IAgoraRTCClient`](/api-ref/rtc/react/interfaces/iagorartcclient.html) | `IAgoraRTCClient` 对象。 |
16 |
17 | #### 使用示例
18 |
19 | ```jsx
20 | import { useRTCClient } from "agora-rtc-react";
21 |
22 | function App() {
23 | const client = useRTCClient();
24 |
25 | return <>>;
26 | }
27 | ```
28 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/hooks/useRemoteUsers.en-US.mdx:
--------------------------------------------------------------------------------
1 | ## useRemoteUsers
2 |
3 | This hook lets you retrieve the list of remote users.
4 |
5 | The return value of this hook is updated in the following cases:
6 |
7 | - When a remote user joins or leaves the channel.
8 | - When the role of a remote user changes (for example, from broadcaster to audience).
9 | - When a remote user publishes or unpublishes the audio or video track.
10 |
11 | #### Parameters
12 |
13 | | Parameter | Type | Required | Description |
14 | | --------- | ------------------------------------------------------------------------------------------------------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
15 | | `client` | [`IAgoraRTCClient`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartcclient.html) | `null` | No | Created using the Web SDK's [`IAgoraRTC.createClient`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartc.html#createclient) method. |
16 |
17 | #### Returns
18 |
19 | | Type | Description |
20 | | ----------------------- | ------------------------- |
21 | | `IAgoraRTCRemoteUser[]` | The list of remote users. |
22 |
23 | #### Sample code
24 |
25 | ```jsx
26 | import { useRemoteUsers } from "agora-rtc-react";
27 |
28 | function App() {
29 | const remoteUsers = useRemoteUsers();
30 |
31 | return <>>;
32 | }
33 | ```
34 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/hooks/useRemoteUsers.zh-CN.mdx:
--------------------------------------------------------------------------------
1 | ## useRemoteUsers
2 |
3 | 用于获取远端用户列表。
4 |
5 | 发生以下情况时,该 Hook 的返回值会更新:
6 |
7 | - 远端用户加入或离开频道。
8 | - 远端用户的角色改变(比如从主播变为观众)。
9 | - 远端用户发布或取消发布音频或视频轨道。
10 |
11 | #### 参数
12 |
13 | | 参数名 | 类型 | 是否必选 | 描述 |
14 | | -------- | --------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------ |
15 | | `client` | [`IAgoraRTCClient`](/api-ref/rtc/react/interfaces/iagorartcclient.html) | `null` | 可选 | 通过 Web SDK 的 [`IAgoraRTC.createClient`](/api-ref/rtc/react/interfaces/iagorartc.html#createclient) 创建。 |
16 |
17 | #### 返回值
18 |
19 | | 类型 | 描述 |
20 | | ----------------------- | -------------- |
21 | | `IAgoraRTCRemoteUser[]` | 远端用户列表。 |
22 |
23 | #### 使用示例
24 |
25 | ```jsx
26 | import { useRemoteUsers } from "agora-rtc-react";
27 |
28 | function App() {
29 | const remoteUsers = useRemoteUsers();
30 |
31 | return <>>;
32 | }
33 | ```
34 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/hooks/useVolumeLevel.en-US.mdx:
--------------------------------------------------------------------------------
1 | ## useVolumeLevel
2 |
3 | Returns the volume level of an audio track at a frequency of once per second.
4 |
5 | #### Parameters
6 |
7 | | Parameter | Type | Required | Description |
8 | | ------------ | -------------------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
9 | | `audioTrack` | `IRemoteAudioTrack` | `ILocalAudioTrack` | `undefined` | No | The local or remote audio track. The local audio track can be created by calling [`useLocalMicrophoneTrack`](https://api-ref.agora.io/en/video-sdk/reactjs/2.x/functions/useLocalMicrophoneTrack.html). If undefined, the volume level is 0. |
10 |
11 | #### Returns
12 |
13 | | Type | Description |
14 | | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
15 | | `number` | The volume level. The value range is [0,1]. 1 is the highest volume level. Usually a user with a volume level above 0.6 is a speaking user. |
16 |
17 | #### Sample code
18 |
19 | ```jsx
20 | import { useVolumeLevel, useLocalMicrophoneTrack } from "agora-rtc-react";
21 |
22 | function App() {
23 | const audioTrack = useLocalMicrophoneTrack();
24 | const volumeLevel = useVolumeLevel(audioTrack);
25 |
26 | return {volumeLevel}
;
27 | }
28 | ```
29 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/docs/hooks/useVolumeLevel.zh-CN.mdx:
--------------------------------------------------------------------------------
1 | ## useVolumeLevel
2 |
3 | 用于自动获取音频轨道音量级别,自动获取的频率为每秒一次。
4 |
5 | #### 参数
6 |
7 | | 参数名 | 类型 | 是否必选 | 描述 |
8 | | ------------ | -------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
9 | | `audioTrack` | `IRemoteAudioTrack` | `ILocalAudioTrack` | `undefined` | 可选 | 本地或远端音频轨道,其中本地音频轨道通过 [`useLocalMicrophoneTrack`](https://doc.shengwang.cn/api-ref/rtc/react/react-sdk/hooks#uselocalmicrophonetrack) 创建。如果未定义,则音量级别为 0。 |
10 |
11 | #### 返回值
12 |
13 | | 类型 | 描述 |
14 | | -------- | ---------------------------------------------------------------------------------------------- |
15 | | `number` | 音频轨道的音量级别。取值范围 [0, 1],1 代表理论最大音量。通常该值大于 0.6 代表用户在持续说话。 |
16 |
17 | #### 使用示例
18 |
19 | ```jsx
20 | import { useVolumeLevel, useLocalMicrophoneTrack } from "agora-rtc-react";
21 |
22 | function App() {
23 | const audioTrack = useLocalMicrophoneTrack();
24 | const volumeLevel = useVolumeLevel(audioTrack);
25 |
26 | return {volumeLevel}
;
27 | }
28 | ```
29 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/assets/styles.ts:
--------------------------------------------------------------------------------
1 | import type { CSSProperties } from "react";
2 | import { useMemo } from "react";
3 |
4 | export const VideoTrackWrapperStyle: CSSProperties = {
5 | position: "relative",
6 | width: "100%",
7 | height: "100%",
8 | overflow: "hidden",
9 | background: "#000",
10 | };
11 |
12 | export const VideoTrackStyle: CSSProperties = {
13 | width: "100%",
14 | height: "100%",
15 | };
16 |
17 | export const FloatBoxStyle: CSSProperties = {
18 | position: "absolute",
19 | top: 0,
20 | left: 0,
21 | width: "100%",
22 | height: "100%",
23 | overflow: "hidden",
24 | zIndex: 2,
25 | };
26 |
27 | export const useMergedStyle = (s1?: CSSProperties, s2?: CSSProperties): CSSProperties =>
28 | useMemo(() => ({ ...s1, ...s2 }), [s1, s2]);
29 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/components/UserCover.tsx:
--------------------------------------------------------------------------------
1 | import type { CSSProperties, ReactNode } from "react";
2 |
3 | import { FloatBoxStyle } from "../assets/styles";
4 |
5 | const CoverBlurStyle: CSSProperties = {
6 | width: "100%",
7 | height: "100%",
8 | background: "#1a1e21 center/cover no-repeat",
9 | filter: "blur(16px) brightness(0.4)",
10 | };
11 |
12 | const CoverImgStyle: CSSProperties = {
13 | position: "absolute",
14 | top: "50%",
15 | left: "50%",
16 | maxWidth: "50%",
17 | maxHeight: "50%",
18 | aspectRatio: "1",
19 | transform: "translate(-50%, -50%)",
20 | borderRadius: "50%",
21 | overflow: "hidden",
22 | objectFit: "cover",
23 | };
24 |
25 | export interface UserCoverProps {
26 | /**
27 | * Cover image url or a custom render function.
28 | */
29 | cover: string | (() => ReactNode);
30 | }
31 |
32 | /**
33 | * User Cover image with blur background
34 | */
35 | export function UserCover({ cover }: UserCoverProps) {
36 | return (
37 |
38 | {typeof cover === "string" ? (
39 | <>
40 |
41 |
42 | >
43 | ) : (
44 | cover()
45 | )}
46 |
47 | );
48 | }
49 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./LocalAudioTrack";
2 | export * from "./LocalVideoTrack";
3 | export * from "./LocalUser";
4 | export * from "./RemoteAudioTrack";
5 | export * from "./RemoteVideoTrack";
6 | export * from "./RemoteUser";
7 | export * from "./TrackBoundary";
8 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/error.ts:
--------------------------------------------------------------------------------
1 | import type { IAgoraRTCError } from "agora-rtc-sdk-ng";
2 |
3 | type printType = "log" | "warn" | "error" | "info";
4 |
5 | interface IAgoraRTCReactError extends Error {
6 | readonly rtcMethod: string;
7 | readonly rtcError: IAgoraRTCError | string;
8 | log: (type: printType) => void;
9 | }
10 |
11 | export class AgoraRTCReactError extends Error implements IAgoraRTCReactError {
12 | public readonly rtcMethod: string;
13 | public readonly rtcError: IAgoraRTCError | string;
14 | public override readonly name: string = "AgoraRTCReactException";
15 |
16 | public constructor(rtcMethod: string, rtcError: IAgoraRTCError | string) {
17 | if (typeof rtcError === "string") {
18 | super(rtcError);
19 | } else {
20 | super(rtcError.message);
21 | }
22 | this.rtcMethod = rtcMethod;
23 | this.rtcError = rtcError;
24 | }
25 |
26 | public log(type: printType) {
27 | console[type](this);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/hooks/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./events";
2 | export * from "./useRTCClient";
3 | export * from "./useRTCScreenShareClient";
4 | export * from "./useConnectionState";
5 | export * from "./useIsConnected";
6 | export * from "./useCurrentUID";
7 | export * from "./useJoin";
8 | export * from "./useNetworkQuality";
9 | export * from "./usePublish";
10 | export * from "./useVolumeLevel";
11 | export * from "./useLocalMicrophoneTrack";
12 | export * from "./useLocalCameraTrack";
13 | export * from "./useRemoteAudioTracks";
14 | export * from "./useRemoteVideoTracks";
15 | export * from "./useRemoteUserTrack";
16 | export * from "./useRemoteUsers";
17 | export * from "./useLocalScreenTrack";
18 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/hooks/types.ts:
--------------------------------------------------------------------------------
1 | import type { IAgoraRTCClient, IAgoraRTCRemoteUser } from "agora-rtc-sdk-ng";
2 | import type { ReactNode } from "react";
3 |
4 | export interface massUserProps {
5 | user: IAgoraRTCRemoteUser;
6 | mediaType: "audio" | "video";
7 | }
8 |
9 | export interface AgoraRTCProviderProps {
10 | readonly client: IAgoraRTCClient;
11 | readonly children?: ReactNode;
12 | }
13 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/hooks/useConnectionState.ts:
--------------------------------------------------------------------------------
1 | import type { ConnectionState, IAgoraRTCClient } from "agora-rtc-sdk-ng";
2 | import { useEffect, useState } from "react";
3 |
4 | import { useRTCClient } from "../hooks/useRTCClient";
5 | import { listen } from "../misc/listen";
6 | import { joinDisposers, timeout } from "../misc/utils";
7 |
8 | /**
9 | * Returns the detailed connection state of the SDK.
10 | *
11 | * @param client - Created using the Web SDK's [`IAgoraRTC.createClient`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartc.html#createclient) method.
12 | * @example
13 | * ```jsx
14 | * import { useConnectionState } from "agora-rtc-react";
15 | *
16 | * function App() {
17 | * const connectionState = useConnectionState();
18 | *
19 | * return {connectionState}
;
20 | * }
21 | * ```
22 | */
23 | export function useConnectionState(client?: IAgoraRTCClient | null): ConnectionState {
24 | const resolvedClient = useRTCClient(client);
25 |
26 | const [connectionState, setConnectionState] = useState(
27 | resolvedClient ? resolvedClient.connectionState : "DISCONNECTED",
28 | );
29 | useEffect(() => {
30 | if (resolvedClient) {
31 | setConnectionState(resolvedClient.connectionState);
32 | let dispose: (() => void) | undefined;
33 | return joinDisposers([
34 | listen(resolvedClient, "connection-state-change", state => {
35 | dispose?.();
36 | if (state === "CONNECTED") {
37 | // RTC is really connected after a short delay
38 | dispose = timeout(() => setConnectionState(state), 0);
39 | } else {
40 | setConnectionState(state);
41 | }
42 | }),
43 | () => dispose?.(),
44 | ]);
45 | } else {
46 | setConnectionState("DISCONNECTED");
47 | }
48 | }, [resolvedClient]);
49 |
50 | return connectionState;
51 | }
52 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/hooks/useCurrentUID.ts:
--------------------------------------------------------------------------------
1 | import type { IAgoraRTCClient, UID } from "agora-rtc-sdk-ng";
2 | import { useEffect, useState } from "react";
3 |
4 | import { useRTCClient } from "../hooks/useRTCClient";
5 | import { listen } from "../misc/listen";
6 | import { timeout } from "../misc/utils";
7 |
8 | /**
9 | * Returns the current user ID.
10 | *
11 | * @param client - Created using the Web SDK's [`IAgoraRTC.createClient`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartc.html#createclient) method.
12 | * @example
13 | * ```jsx
14 | * import { useCurrentUID } from "agora-rtc-react";
15 | *
16 | * function App() {
17 | * const uid = useCurrentUID();
18 | *
19 | * return {uid}
;
20 | * }
21 | * ```
22 | */
23 | export function useCurrentUID(client?: IAgoraRTCClient | null): UID | undefined {
24 | const resolvedClient = useRTCClient(client);
25 |
26 | const [uid, setUID] = useState(resolvedClient?.uid);
27 | useEffect(() => {
28 | if (resolvedClient) {
29 | return listen(resolvedClient, "connection-state-change", state => {
30 | if (state === "CONNECTED") {
31 | // RTC is really connected after a short delay
32 | return timeout(() => setUID(resolvedClient.uid), 0);
33 | } else if (state === "DISCONNECTED") {
34 | setUID(void 0);
35 | }
36 | });
37 | }
38 | }, [resolvedClient]);
39 |
40 | return uid;
41 | }
42 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/hooks/useIsConnected.ts:
--------------------------------------------------------------------------------
1 | import type { IAgoraRTCClient } from "agora-rtc-sdk-ng";
2 | import { useEffect, useState } from "react";
3 |
4 | import { useRTCClient } from "../hooks/useRTCClient";
5 | import { listen } from "../misc/listen";
6 | import { joinDisposers, timeout } from "../misc/utils";
7 |
8 | /**
9 | * Returns whether the SDK is connected to Agora's server.
10 | *
11 | * @param client - Created using the Web SDK's [`IAgoraRTC.createClient`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartc.html#createclient) method.
12 | * @example
13 | * ```jsx
14 | * import { useIsConnected } from "agora-rtc-react";
15 | *
16 | * function App() {
17 | * const isConnected = useIsConnected();
18 | *
19 | * return {isConnected}
;
20 | * }
21 | * ```
22 | */
23 | export function useIsConnected(client?: IAgoraRTCClient | null): boolean {
24 | const resolvedClient = useRTCClient(client);
25 |
26 | const [isConnected, setConnected] = useState(
27 | resolvedClient ? resolvedClient.connectionState === "CONNECTED" : false,
28 | );
29 | useEffect(() => {
30 | if (resolvedClient) {
31 | setConnected(resolvedClient.connectionState === "CONNECTED");
32 | let dispose: (() => void) | undefined;
33 | return joinDisposers([
34 | listen(resolvedClient, "connection-state-change", state => {
35 | dispose?.();
36 | // RTC is really connected after a short delay
37 | dispose = timeout(() => setConnected(state === "CONNECTED"), 0);
38 | }),
39 | () => dispose?.(),
40 | ]);
41 | } else {
42 | setConnected(false);
43 | }
44 | }, [resolvedClient]);
45 |
46 | return isConnected;
47 | }
48 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/hooks/useNetworkQuality.ts:
--------------------------------------------------------------------------------
1 | import type { IAgoraRTCClient } from "agora-rtc-sdk-ng";
2 | import { useEffect, useState } from "react";
3 |
4 | import { useRTCClient } from "../hooks/useRTCClient";
5 | import { listen } from "../misc/listen";
6 | import type { NetworkQualityEx } from "../types";
7 |
8 | const initQuality = (): NetworkQualityEx => ({
9 | uplinkNetworkQuality: 0,
10 | downlinkNetworkQuality: 0,
11 | delay: 0,
12 | });
13 |
14 | /**
15 | * Returns the network quality of the local user.
16 | *
17 | * @param client - Created using the Web SDK's [`IAgoraRTC.createClient`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartc.html#createclient) method.
18 | * @example
19 | * ```jsx
20 | * import { useNetworkQuality } from "agora-rtc-react";
21 | *
22 | * function App() {
23 | * const networkQuality = useNetworkQuality();
24 | *
25 | * return {networkQuality}
;
26 | * }
27 | * ```
28 | */
29 | export function useNetworkQuality(client?: IAgoraRTCClient | null): NetworkQualityEx {
30 | const resolvedClient = useRTCClient(client);
31 |
32 | const [networkQuality, setNetworkQuality] = useState(initQuality);
33 | useEffect(() => {
34 | if (resolvedClient) {
35 | return listen(resolvedClient, "network-quality", q =>
36 | setNetworkQuality({
37 | uplinkNetworkQuality: q.uplinkNetworkQuality,
38 | downlinkNetworkQuality: q.downlinkNetworkQuality,
39 | delay: resolvedClient.getRTCStats().RTT ?? 0,
40 | }),
41 | );
42 | } else {
43 | setNetworkQuality(initQuality());
44 | }
45 | }, [resolvedClient]);
46 |
47 | return networkQuality;
48 | }
49 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/hooks/useRTCClient.tsx:
--------------------------------------------------------------------------------
1 | import type { IAgoraRTCClient } from "agora-rtc-sdk-ng";
2 | import { createContext, useContext } from "react";
3 |
4 | import type { AgoraRTCProviderProps } from "./types";
5 |
6 | const AgoraRTCContext = /* @__PURE__ */ createContext(null);
7 |
8 | export function AgoraRTCProvider({ client, children }: AgoraRTCProviderProps) {
9 | return {children} ;
10 | }
11 | /**
12 | * @ignore
13 | */
14 | function useOptionalRTCClient(client?: IAgoraRTCClient | null): IAgoraRTCClient | null {
15 | const clientFromContext = useContext(AgoraRTCContext);
16 | return client || clientFromContext;
17 | }
18 |
19 | /**
20 | * Returns the IAgoraRTCClient object.
21 | *
22 | * @param client - If provided, the passed `IAgoraRTCClient` object is returned. If not provided, the `IAgoraRTCClient` object obtained from the [parent component's context](https://api-ref.agora.io/en/video-sdk/reactjs/2.x/functions/AgoraRTCProvider.html) is returned.
23 | * @example
24 | * ```jsx
25 | * import { useRTCClient } from "agora-rtc-react";
26 | *
27 | * function App() {
28 | * const client = useRTCClient();
29 | *
30 | * return <>>;
31 | * }
32 | * ```
33 | */
34 | export function useRTCClient(client?: IAgoraRTCClient | null): IAgoraRTCClient {
35 | const resolvedClient = useOptionalRTCClient(client);
36 |
37 | if (!resolvedClient) {
38 | throw new Error(
39 | "Agora RTC client not found. Should be wrapped in ",
40 | );
41 | }
42 |
43 | return resolvedClient;
44 | }
45 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/hooks/useRTCScreenShareClient.tsx:
--------------------------------------------------------------------------------
1 | import type { IAgoraRTCClient } from "agora-rtc-sdk-ng";
2 | import type { ReactNode } from "react";
3 | import { createContext, useContext } from "react";
4 |
5 | const AgoraRTCScreenShareContext = /* @__PURE__ */ createContext(null);
6 |
7 | export interface AgoraRTCScreenShareProviderProps {
8 | readonly client: IAgoraRTCClient;
9 | readonly children?: ReactNode;
10 | }
11 |
12 | export function AgoraRTCScreenShareProvider({
13 | client,
14 | children,
15 | }: AgoraRTCScreenShareProviderProps) {
16 | return (
17 |
18 | {children}
19 |
20 | );
21 | }
22 |
23 | export function useRTCScreenShareClient(client?: IAgoraRTCClient | null): IAgoraRTCClient | null {
24 | const clientFromContext = useContext(AgoraRTCScreenShareContext);
25 | return client || clientFromContext;
26 | }
27 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/hooks/useRemoteUsers.ts:
--------------------------------------------------------------------------------
1 | import type { IAgoraRTCClient, IAgoraRTCRemoteUser } from "agora-rtc-sdk-ng";
2 | import { useEffect, useState } from "react";
3 |
4 | import { useRTCClient } from "../hooks/useRTCClient";
5 | import { listen } from "../misc/listen";
6 | import { joinDisposers } from "../misc/utils";
7 |
8 | /**
9 | * This hook lets you retrieve the list of remote users.
10 | * The return value of this hook is updated in the following cases:
11 | * When a remote user joins or leaves the channel.
12 | * When the role of a remote user changes (for example, from broadcaster to audience).
13 | * When a remote user publishes or unpublishes the audio or video track.
14 | *
15 | * @param client - Created using the Web SDK's [`IAgoraRTC.createClient`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartc.html#createclient) method.
16 | * @example
17 | * ```jsx
18 | * import { useRemoteUsers } from "agora-rtc-react";
19 | *
20 | * function App() {
21 | * const remoteUsers = useRemoteUsers();
22 | *
23 | * return <>>;
24 | * }
25 | * ```
26 | */
27 | export function useRemoteUsers(client?: IAgoraRTCClient | null): IAgoraRTCRemoteUser[] {
28 | const resolvedClient = useRTCClient(client);
29 | const [users, setUsers] = useState(resolvedClient ? resolvedClient.remoteUsers : []);
30 |
31 | useEffect(() => {
32 | if (resolvedClient) {
33 | // .slice(): make sure the array reference is updated
34 | const update = () => setUsers(resolvedClient.remoteUsers.slice());
35 | return joinDisposers([
36 | listen(resolvedClient, "user-joined", update),
37 | listen(resolvedClient, "user-left", update),
38 | listen(resolvedClient, "user-published", update),
39 | listen(resolvedClient, "user-unpublished", update),
40 | ]);
41 | }
42 | }, [resolvedClient]);
43 |
44 | return users;
45 | }
46 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/hooks/useVolumeLevel.ts:
--------------------------------------------------------------------------------
1 | import type { ILocalAudioTrack, IRemoteAudioTrack } from "agora-rtc-sdk-ng";
2 | import { useEffect, useState } from "react";
3 |
4 | import { interval } from "../misc/utils";
5 |
6 | /**
7 | * Returns the volume level of an audio track at a frequency of once per second.
8 | *
9 | * @param audioTrack - The local or remote audio track. The local audio track can be created by calling [`useLocalMicrophoneTrack`](https://api-ref.agora.io/en/video-sdk/reactjs/2.x/functions/useLocalMicrophoneTrack.html). If undefined, the volume level is 0.
10 | * @example
11 | * ```jsx
12 | * import { useVolumeLevel, useLocalMicrophoneTrack } from "agora-rtc-react";
13 | *
14 | * function App() {
15 | * const audioTrack = useLocalMicrophoneTrack();
16 | * const volumeLevel = useVolumeLevel(audioTrack);
17 | *
18 | * return {volumeLevel}
;
19 | * }
20 | * ```
21 | */
22 | export function useVolumeLevel(audioTrack?: IRemoteAudioTrack | ILocalAudioTrack): number {
23 | const [volumeLevel, setVolumeLevel] = useState(0);
24 |
25 | useEffect(() => {
26 | if (audioTrack) {
27 | return interval(() => {
28 | setVolumeLevel(audioTrack.getVolumeLevel());
29 | }, 1000);
30 | }
31 | }, [audioTrack]);
32 |
33 | return volumeLevel;
34 | }
35 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/index.ts:
--------------------------------------------------------------------------------
1 | import AgoraRTC from "agora-rtc-sdk-ng";
2 |
3 | export * from "./hooks";
4 | export * from "./components";
5 | export * from "./rtc";
6 | export * from "./error";
7 | export * from "./types";
8 |
9 | export default AgoraRTC;
10 | export * from "agora-rtc-sdk-ng";
11 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/misc/store.ts:
--------------------------------------------------------------------------------
1 | import type { Disposer } from "./utils";
2 |
3 | /** Callback to inform of a value updates. */
4 | export type Subscriber = (value: T) => void;
5 |
6 | /** Start and stop notification callbacks. */
7 | export type StartStopNotifier = (set: Subscriber) => Disposer | void;
8 |
9 | /** Readable interface for subscribing. */
10 | export interface Readable {
11 | /**
12 | * Subscribe on value changes.
13 | * @param run subscription callback
14 | */
15 | subscribe: (this: void, run: Subscriber) => Disposer;
16 | }
17 |
18 | export function readable(value: T, start: StartStopNotifier): Readable {
19 | let stop: Disposer | null | undefined;
20 | const subscribers = new Set>();
21 |
22 | function set(newValue: T) {
23 | if (!Object.is(value, newValue)) {
24 | value = newValue;
25 | if (stop) {
26 | for (const subscriber of subscribers) {
27 | subscriber(value);
28 | }
29 | }
30 | }
31 | }
32 |
33 | function subscribe(run: Subscriber) {
34 | subscribers.add(run);
35 | if (subscribers.size === 1) {
36 | stop = start(set) || noop;
37 | }
38 | run(value);
39 | return () => {
40 | subscribers.delete(run);
41 | if (subscribers.size === 0 && stop) {
42 | stop();
43 | stop = null;
44 | }
45 | };
46 | }
47 |
48 | return { subscribe };
49 | }
50 |
51 | function noop() {
52 | // noop
53 | }
54 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/rtc.ts:
--------------------------------------------------------------------------------
1 | import AgoraRTC from "agora-rtc-sdk-ng";
2 |
3 | interface IAgoraRTCReact {
4 | readonly appType: number;
5 | }
6 |
7 | class AgoraRTCReact implements IAgoraRTCReact {
8 | readonly appType = 1001;
9 |
10 | public constructor() {
11 | AgoraRTC.setAppType(this.appType);
12 | }
13 | }
14 |
15 | new AgoraRTCReact();
16 |
17 | export const VERSION = "2.4.0";
18 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/stories/LocalAudioTrack.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from "@storybook/react";
2 | import { FakeLocalAudioTrack } from "agora-rtc-sdk-ng-fake";
3 |
4 | import type { LocalAudioTrackProps } from "../components";
5 | import { LocalAudioTrack } from "../components";
6 |
7 | const meta: Meta = {
8 | title: "Track/LocalAudioTrack",
9 | component: LocalAudioTrack,
10 | parameters: {
11 | layout: "fullscreen",
12 | },
13 | argTypes: {
14 | track: {
15 | control: {
16 | type: null,
17 | },
18 | },
19 | },
20 | render(args) {
21 | return (
22 |
23 | An Example Local Audio Track
24 |
25 | );
26 | },
27 | };
28 |
29 | export default meta;
30 |
31 | export const Enabled: StoryObj = {
32 | args: {
33 | track: FakeLocalAudioTrack.create(),
34 | play: true,
35 | },
36 | };
37 |
38 | export const EmptyTrack: StoryObj = {
39 | args: {
40 | play: true,
41 | },
42 | };
43 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/stories/LocalVideoTrack.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from "@storybook/react";
2 | import { FakeLocalVideoTrack } from "agora-rtc-sdk-ng-fake";
3 | import { useState } from "react";
4 |
5 | import type { LocalVideoTrackProps } from "../components";
6 | import { LocalVideoTrack } from "../components";
7 |
8 | const meta: Meta = {
9 | title: "Track/LocalVideoTrack",
10 | component: LocalVideoTrack,
11 | parameters: {
12 | layout: "fullscreen",
13 | },
14 | argTypes: {
15 | track: {
16 | control: {
17 | type: null,
18 | },
19 | },
20 | },
21 | render: function RenderLocalVideoTrack(args) {
22 | const [track] = useState(() => (args.track ? FakeLocalVideoTrack.create() : undefined));
23 | return ;
24 | },
25 | };
26 |
27 | export default meta;
28 |
29 | export const Enabled: StoryObj = {
30 | args: {
31 | track: FakeLocalVideoTrack.create(),
32 | play: true,
33 | },
34 | };
35 |
36 | export const EmptyTrack: StoryObj = {
37 | args: {
38 | play: true,
39 | children: An Empty Local Video Track
,
40 | },
41 | };
42 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/stories/RemoteAudioTrack.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from "@storybook/react";
2 | import { FakeRemoteAudioTrack } from "agora-rtc-sdk-ng-fake";
3 |
4 | import type { RemoteAudioTrackProps } from "../components";
5 | import { RemoteAudioTrack } from "../components";
6 |
7 | const meta: Meta = {
8 | title: "Track/RemoteAudioTrack",
9 | component: RemoteAudioTrack,
10 | parameters: {
11 | layout: "fullscreen",
12 | },
13 | argTypes: {
14 | track: {
15 | control: {
16 | type: null,
17 | },
18 | },
19 | },
20 | render(args) {
21 | return (
22 |
23 | An Example Remote Audio Track
24 |
25 | );
26 | },
27 | };
28 |
29 | export default meta;
30 |
31 | export const Enabled: StoryObj = {
32 | args: {
33 | track: FakeRemoteAudioTrack.create(),
34 | play: true,
35 | },
36 | };
37 |
38 | export const EmptyTrack: StoryObj = {
39 | args: {
40 | play: true,
41 | },
42 | };
43 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/stories/RemoteVideoTrack.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from "@storybook/react";
2 | import { FakeRemoteVideoTrack } from "agora-rtc-sdk-ng-fake";
3 | import { useState } from "react";
4 |
5 | import type { RemoteVideoTrackProps } from "../components";
6 | import { RemoteVideoTrack } from "../components";
7 |
8 | const meta: Meta = {
9 | title: "Track/RemoteVideoTrack",
10 | component: RemoteVideoTrack,
11 | parameters: {
12 | layout: "fullscreen",
13 | },
14 | argTypes: {
15 | track: {
16 | control: {
17 | type: null,
18 | },
19 | },
20 | },
21 | render: function RenderRemoteVideoTrack(args) {
22 | const [track] = useState(() => (args.track ? FakeRemoteVideoTrack.create() : undefined));
23 | return ;
24 | },
25 | };
26 |
27 | export default meta;
28 |
29 | export const Enabled: StoryObj = {
30 | args: {
31 | track: FakeRemoteVideoTrack.create(),
32 | play: true,
33 | },
34 | };
35 |
36 | export const EmptyTrack: StoryObj = {
37 | args: {
38 | play: true,
39 | children: An Empty Remote Video Track
,
40 | },
41 | };
42 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/stories/hooks/useAutoPlayAudioTrack.en-US.mdx:
--------------------------------------------------------------------------------
1 | import Readme from "../../../docs/hooks/useAutoPlayAudioTrack.en-US.mdx?raw";
import { Meta, Markdown } from "@storybook/blocks";
{Readme}
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/stories/hooks/useAutoPlayVideoTrack.en-US.mdx:
--------------------------------------------------------------------------------
1 | import Readme from "../../../docs/hooks/useAutoPlayVideoTrack.en-US.mdx?raw";
import { Meta, Markdown } from "@storybook/blocks";
{Readme}
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/stories/hooks/useClientEvent.en-US.mdx:
--------------------------------------------------------------------------------
1 | import Readme from "../../../docs/hooks/useClientEvent.en-US.mdx?raw";
import { Meta, Markdown } from "@storybook/blocks";
{Readme}
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/stories/hooks/useConnectionState.en-US.mdx:
--------------------------------------------------------------------------------
1 | import Readme from "../../../docs/hooks/useConnectionState.en-US.mdx?raw";
import { Meta, Markdown } from "@storybook/blocks";
{Readme}
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/stories/hooks/useCurrentUID.en-US.mdx:
--------------------------------------------------------------------------------
1 | import Readme from "../../../docs/hooks/useCurrentUID.en-US.mdx?raw";
import { Meta, Markdown } from "@storybook/blocks";
{Readme}
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/stories/hooks/useIsConnected.en-US.mdx:
--------------------------------------------------------------------------------
1 | import Readme from "../../../docs/hooks/useIsConnected.en-US.mdx?raw";
import { Meta, Markdown } from "@storybook/blocks";
{Readme}
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/stories/hooks/useJoin.en-US.mdx:
--------------------------------------------------------------------------------
1 | import Readme from "../../../docs/hooks/useJoin.en-US.mdx?raw";
import { Meta, Markdown } from "@storybook/blocks";
{Readme}
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/stories/hooks/useLocalCameraTrack.en-US.mdx:
--------------------------------------------------------------------------------
1 | import Readme from "../../../docs/hooks/useLocalCameraTrack.en-US.mdx?raw";
import { Meta, Markdown } from "@storybook/blocks";
{Readme}
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/stories/hooks/useLocalMicrophoneTrack.en-US.mdx:
--------------------------------------------------------------------------------
1 | import Readme from "../../../docs/hooks/useLocalMicrophoneTrack.en-US.mdx?raw";
import { Meta, Markdown } from "@storybook/blocks";
{Readme}
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/stories/hooks/useLocalScreenTrack.en-US.mdx:
--------------------------------------------------------------------------------
1 | import Readme from "../../../docs/hooks/useLocalScreenTrack.en-US.mdx?raw";
import { Meta, Markdown } from "@storybook/blocks";
{Readme}
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/stories/hooks/useNetworkQuality.en-US.mdx:
--------------------------------------------------------------------------------
1 | import Readme from "../../../docs/hooks/useNetworkQuality.en-US.mdx?raw";
import { Meta, Markdown } from "@storybook/blocks";
{Readme}
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/stories/hooks/usePublish.en-US.mdx:
--------------------------------------------------------------------------------
1 | import Readme from "../../../docs/hooks/usePublish.en-US.mdx?raw";
import { Meta, Markdown } from "@storybook/blocks";
{Readme}
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/stories/hooks/useRTCClient.en-US.mdx:
--------------------------------------------------------------------------------
1 | import Readme from "../../../docs/hooks/useRTCClient.en-US.mdx?raw";
import { Meta, Markdown } from "@storybook/blocks";
{Readme}
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/stories/hooks/useRemoteAudioTracks.en-US.mdx:
--------------------------------------------------------------------------------
1 | import Readme from "../../../docs/hooks/useRemoteAudioTracks.en-US.mdx?raw";
import { Meta, Markdown } from "@storybook/blocks";
{Readme}
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/stories/hooks/useRemoteUserTrack.en-US.mdx:
--------------------------------------------------------------------------------
1 | import Readme from "../../../docs/hooks/useRemoteUserTrack.en-US.mdx?raw";
import { Meta, Markdown } from "@storybook/blocks";
{Readme}
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/stories/hooks/useRemoteUsers.en-US.mdx:
--------------------------------------------------------------------------------
1 | import Readme from "../../../docs/hooks/useRemoteUsers.en-US.mdx?raw";
import { Meta, Markdown } from "@storybook/blocks";
{Readme}
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/stories/hooks/useRemoteVideoTracks.en-US.mdx:
--------------------------------------------------------------------------------
1 | import Readme from "../../../docs/hooks/useRemoteVideoTracks.en-US.mdx?raw";
import { Meta, Markdown } from "@storybook/blocks";
{Readme}
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/stories/hooks/useTrackEvent.en-US.mdx:
--------------------------------------------------------------------------------
1 | import Readme from "../../../docs/hooks/useTrackEvent.en-US.mdx?raw";
import { Meta, Markdown } from "@storybook/blocks";
{Readme}
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/stories/hooks/useVolumeLevel.en-US.mdx:
--------------------------------------------------------------------------------
1 | import Readme from "../../../docs/hooks/useVolumeLevel.en-US.mdx?raw";
import { Meta, Markdown } from "@storybook/blocks";
{Readme}
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/types.ts:
--------------------------------------------------------------------------------
1 | import type { NetworkQuality, UID } from "agora-rtc-sdk-ng";
2 |
3 | export type FetchArgs = (() => Promise) | JoinOptions;
4 |
5 | /**
6 | * Parameters used to join a channel.
7 | */
8 | export interface JoinOptions {
9 | /**
10 | * The App ID of your Agora project.
11 | */
12 | appid: string;
13 |
14 | /**
15 | * The name of the channel to join. See [`IAgoraRTCClient.join`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartcclient.html#join) for details.
16 | */
17 | channel: string;
18 |
19 | /**
20 | * The token used for authentication. If token-based authentication is enabled for your project, a valid token must be provided. If token-based authentication is not enabled, you can pass `null`. See [`IAgoraRTCClient.join`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartcclient.html#join) for details.
21 | */
22 | token: string | null;
23 |
24 | /**
25 | * The user ID. If not provided, the Agora server assigns a number `uid` for you. See [`IAgoraRTCClient.join`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartcclient.html#join) for details.
26 | */
27 | uid?: UID | null;
28 | }
29 |
30 | /**
31 | * The last-mile network quality.
32 | */
33 | export interface NetworkQualityEx extends NetworkQuality {
34 | /**
35 | * The average Round-Trip Time (RTT) from the SDK to the Agora edge server, measured in milliseconds (ms).
36 | */
37 | delay: number;
38 | }
39 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/test/component/RemoteVideoTrack.test.tsx:
--------------------------------------------------------------------------------
1 | import { composeStories } from "@storybook/react";
2 | import { render } from "@testing-library/react";
3 | import type { VideoPlayerConfig } from "agora-rtc-sdk-ng";
4 | import { describe, expect, test, vi } from "vitest";
5 |
6 | import { RemoteVideoTrack } from "../../src/components";
7 | import * as fun from "../../src/components/TrackBoundary";
8 | import * as stories from "../../src/stories/RemoteVideoTrack.stories";
9 | const { Enabled, EmptyTrack } = composeStories(stories);
10 |
11 | vi.mock("../../src/components/TrackBoundary", () => ({
12 | useAutoPlayVideoTrack: vi.fn(),
13 | }));
14 |
15 | describe("RemoteVideoTrack component", () => {
16 | test("renders without crashing", () => {
17 | const { container } = render( );
18 | expect(container).toBeInTheDocument();
19 | vi.clearAllMocks();
20 | });
21 |
22 | test("config videoPlayerConfig on RemoteVideoTrack", () => {
23 | vi.spyOn(fun, "useAutoPlayVideoTrack");
24 | const videoPlayerConfig: VideoPlayerConfig = { mirror: false, fit: "cover" };
25 | render( );
26 | expect(fun.useAutoPlayVideoTrack).toBeCalledWith(
27 | undefined,
28 | true,
29 | videoPlayerConfig,
30 | expect.anything(),
31 | );
32 | vi.clearAllMocks();
33 | });
34 | });
35 |
36 | describe("RemoteVideoTrack component stories", () => {
37 | test("renders Enabled stories", () => {
38 | const { container } = render( );
39 | expect(container).toBeInTheDocument();
40 | });
41 |
42 | test("renders EmptyTrack stories", () => {
43 | const { getByText } = render( );
44 | expect(getByText(/An Empty Remote Video Track/i)).toBeInTheDocument();
45 | });
46 | });
47 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/test/hook/useConnectionState.test.ts:
--------------------------------------------------------------------------------
1 | import { act, renderHook } from "@testing-library/react";
2 | import type { IAgoraRTCClient } from "agora-rtc-sdk-ng";
3 | import { FakeRTCClient, dispatchRTCEvent } from "agora-rtc-sdk-ng-fake";
4 | import { expect, test } from "vitest";
5 |
6 | import { useConnectionState } from "../../src/hooks/index";
7 |
8 | const setUp = (client?: IAgoraRTCClient | null) => renderHook(() => useConnectionState(client));
9 |
10 | describe("useConnectionState", () => {
11 | test("should return client connection state", () => {
12 | const client = FakeRTCClient.create();
13 | const { result } = setUp(client);
14 | expect(result.current).toBe(client.connectionState);
15 | });
16 |
17 | test("should update state on connection-state-change event", () => {
18 | const client = FakeRTCClient.create();
19 | const { result } = setUp(client);
20 | expect(result.current).toBe(client.connectionState);
21 | act(() => {
22 | dispatchRTCEvent(client, "connection-state-change", "CONNECTING");
23 | });
24 | expect(result.current).toBe("CONNECTING");
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/test/hook/useCurrentUID.test.ts:
--------------------------------------------------------------------------------
1 | import { act, renderHook, waitFor } from "@testing-library/react";
2 | import type { IAgoraRTCClient } from "agora-rtc-sdk-ng";
3 | import { FakeRTCClient, dispatchRTCEvent } from "agora-rtc-sdk-ng-fake";
4 | import { expect } from "vitest";
5 |
6 | import { useCurrentUID } from "../../src/hooks/index";
7 | const setUp = (client?: IAgoraRTCClient | null) => renderHook(() => useCurrentUID(client));
8 |
9 | describe("useCurrentUID", () => {
10 | test("returns the client's uid when connected", () => {
11 | const client = FakeRTCClient.create();
12 | const { result } = setUp(client);
13 | expect(result.current).toBe(client.uid);
14 | });
15 |
16 | test("returns 0 when disConnected", async () => {
17 | const client = FakeRTCClient.create();
18 | const { result } = setUp(client);
19 | act(() => {
20 | dispatchRTCEvent(client, "connection-state-change", "DISCONNECTED");
21 | });
22 | await waitFor(() => {
23 | expect(result.current).toBe(void 0);
24 | });
25 | });
26 |
27 | test("returns uid when CONNECTED", async () => {
28 | const client = FakeRTCClient.create();
29 | (client.uid as IAgoraRTCClient["uid"]) = 33;
30 | (client.connectionState as IAgoraRTCClient["connectionState"]) = "CONNECTED";
31 | const { result } = setUp(client);
32 | await waitFor(() => {
33 | expect(result.current).toBe(33);
34 | });
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/test/hook/useIsConnected.test.ts:
--------------------------------------------------------------------------------
1 | import { renderHook } from "@testing-library/react";
2 | import type { IAgoraRTCClient } from "agora-rtc-sdk-ng";
3 | import { FakeRTCClient } from "agora-rtc-sdk-ng-fake";
4 | import { expect } from "vitest";
5 |
6 | import { useIsConnected } from "../../src/hooks/useIsConnected";
7 | const setUp = (client?: IAgoraRTCClient | null) => renderHook(() => useIsConnected(client));
8 |
9 | describe("useIsConnected", () => {
10 | test("returns false when client is not connected", () => {
11 | const client = FakeRTCClient.create();
12 | (client.connectionState as IAgoraRTCClient["connectionState"]) = "DISCONNECTED";
13 | const { result } = setUp(client);
14 | expect(result.current).toBe(false);
15 | });
16 |
17 | test("returns true when client is connected", () => {
18 | const client = FakeRTCClient.create();
19 | (client.connectionState as IAgoraRTCClient["connectionState"]) = "CONNECTED";
20 | const { result } = setUp(client);
21 | expect(result.current).toBe(true);
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/test/hook/useNetworkQuality.test.ts:
--------------------------------------------------------------------------------
1 | import { act, renderHook } from "@testing-library/react";
2 | import type { IAgoraRTCClient } from "agora-rtc-sdk-ng";
3 | import { FakeRTCClient, dispatchRTCEvent } from "agora-rtc-sdk-ng-fake";
4 | import { expect, vi } from "vitest";
5 |
6 | import { useNetworkQuality } from "../../src/hooks/index";
7 | import type { NetworkQualityEx } from "../../src/types";
8 | const setUp = (client?: IAgoraRTCClient | null) => renderHook(() => useNetworkQuality(client));
9 |
10 | describe("useNetworkQuality", () => {
11 | test("default value", () => {
12 | const initQuality = (): NetworkQualityEx => ({
13 | uplinkNetworkQuality: 0,
14 | downlinkNetworkQuality: 0,
15 | delay: 0,
16 | });
17 |
18 | const client = FakeRTCClient.create();
19 | const { result } = setUp(client);
20 | expect(result.current).toStrictEqual(initQuality());
21 | client.getRTCStats = vi.fn().mockReturnValue({ RTT: 3 });
22 | const newQuality = {
23 | uplinkNetworkQuality: 1,
24 | downlinkNetworkQuality: 2,
25 | };
26 |
27 | act(() => {
28 | dispatchRTCEvent(client, "network-quality", newQuality);
29 | });
30 | expect(result.current).toStrictEqual({
31 | uplinkNetworkQuality: 1,
32 | downlinkNetworkQuality: 2,
33 | delay: 3,
34 | });
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/test/hook/useRTCClient.test.ts:
--------------------------------------------------------------------------------
1 | import { renderHook } from "@testing-library/react";
2 | import type { IAgoraRTCClient } from "agora-rtc-sdk-ng";
3 | import { FakeRTCClient } from "agora-rtc-sdk-ng-fake";
4 | import { expect } from "vitest";
5 |
6 | import { useRTCClient } from "../../src/hooks/index";
7 | const setUp = (client?: IAgoraRTCClient | null) => renderHook(() => useRTCClient(client));
8 |
9 | describe("useRTCClient", () => {
10 | test("should throw an error if no client is provided", () => {
11 | expect(() => setUp(null)).toThrow(/^Agora RTC client not found/);
12 | });
13 |
14 | test("should return the provided client", () => {
15 | const client = FakeRTCClient.create();
16 | const { result } = setUp(client);
17 | expect(result.current).toBe(client);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/test/hook/useVolumeLevel.test.ts:
--------------------------------------------------------------------------------
1 | import { renderHook, waitFor } from "@testing-library/react";
2 | import type { ILocalAudioTrack, IRemoteAudioTrack } from "agora-rtc-sdk-ng";
3 | import { FakeLocalAudioTrack } from "agora-rtc-sdk-ng-fake";
4 | import { act } from "react-dom/test-utils";
5 | import { expect, vi } from "vitest";
6 |
7 | import { useVolumeLevel } from "../../src/hooks/index";
8 | const setUp = (audioTrack?: IRemoteAudioTrack | ILocalAudioTrack) =>
9 | renderHook(() => useVolumeLevel(audioTrack));
10 |
11 | describe("useVolumeLevel", () => {
12 | test("should return volumeLevel by getVolumeLevel", async () => {
13 | const audioTrack = FakeLocalAudioTrack.create();
14 | vi.spyOn(audioTrack, "getVolumeLevel");
15 | vi.useFakeTimers();
16 | const { result } = setUp(audioTrack);
17 | act(() => {
18 | vi.advanceTimersByTime(1000);
19 | });
20 | await waitFor(() => {
21 | expect(audioTrack.getVolumeLevel).toHaveBeenCalled();
22 | expect(result.current).toEqual(audioTrack.getVolumeLevel());
23 | });
24 | jest.useRealTimers();
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "noEmit": true,
4 | "strict": true,
5 | "noFallthroughCasesInSwitch": true,
6 | "forceConsistentCasingInFileNames": true,
7 | "noUnusedParameters": true,
8 | "noImplicitOverride": true,
9 | "module": "ESNext",
10 | "target": "ESNext",
11 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
12 | "moduleResolution": "Node",
13 | "esModuleInterop": true,
14 | "resolveJsonModule": true,
15 | "skipLibCheck": true,
16 | "jsx": "react-jsx"
17 | },
18 | "include": ["src", "test", "../shared/test/setup.tsx", "../shared/test/setup"]
19 | }
20 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "tsup";
2 |
3 | import setGlobals from "../../scripts/tsup/set-globals";
4 |
5 | import pkg from "./package.json";
6 |
7 | const banner = `
8 | /**
9 | * @license ${pkg.name}
10 | * @version ${pkg.version}
11 | *
12 | * Copyright (c) Agora, Inc.
13 | *
14 | * This source code is licensed under the MIT license.
15 | */
16 | `;
17 |
18 | export default defineConfig([
19 | {
20 | entry: {
21 | [pkg.name]: "src/index.ts",
22 | },
23 | banner: () => {
24 | return {
25 | js: banner,
26 | };
27 | },
28 | format: ["cjs", "esm"],
29 | splitting: false,
30 | sourcemap: false,
31 | clean: true,
32 | treeshake: true,
33 | dts: {
34 | resolve: ["agora-rtc-sdk-ng"],
35 | },
36 | minify: true,
37 | },
38 | {
39 | entry: {
40 | [pkg.name]: "src/index.ts",
41 | },
42 | banner: () => {
43 | return {
44 | js: banner,
45 | };
46 | },
47 | outExtension: () => {
48 | return {
49 | js: `.iife.js`,
50 | };
51 | },
52 | format: ["iife"],
53 | sourcemap: false,
54 | splitting: false,
55 | clean: true,
56 | minify: true,
57 | external: Object.keys(pkg.peerDependencies),
58 | define: {
59 | "process.env.NODE_ENV": JSON.stringify("production"),
60 | },
61 | globalName: "AgoraRTC",
62 | esbuildPlugins: [
63 | setGlobals({
64 | "react": "React",
65 | "react-dom": "ReactDOM",
66 | }),
67 | ],
68 | platform: "browser",
69 | },
70 | ]);
71 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/typedoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "out": "../../typedoc",
3 | "name": "Video SDK for React",
4 | "includeVersion": true,
5 | "hideGenerator": true,
6 | "githubPages": false
7 | }
8 |
--------------------------------------------------------------------------------
/packages/agora-rtc-react/vite.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 |
3 | ///
4 |
5 | import react from "@vitejs/plugin-react";
6 | import { defineConfig } from "vite";
7 |
8 | export default defineConfig({
9 | plugins: [react()],
10 | test: {
11 | onConsoleLog(log) {
12 | if (log.includes("Agora RTC client not found")) return false;
13 | if (log.includes("Agora-SDK [DEBUG]: ")) return false;
14 | if (log.includes("Agora-SDK [WARNING]: ")) return false;
15 | if (log.includes("Agora-SDK [ERROR]: ")) return false;
16 | if (log.includes("Agora-SDK [INFO]: ")) return false;
17 | if (log.includes("Agora-RTC-REACT [ERROR_TEST_MSG]")) return false;
18 | },
19 | environment: "jsdom",
20 | globals: true,
21 | coverage: {
22 | provider: "v8",
23 | reporter: ["text", "json", "html", "lcov", "json-summary"],
24 | exclude: [
25 | "src/misc/*",
26 | "src/stories/*",
27 | "src/assets/*",
28 | "src/*/index.ts",
29 | "src/hooks/tools.ts",
30 | "test/**",
31 | "src/rtc.ts",
32 | ],
33 | },
34 | exclude: ["**/node_modules/**"],
35 | deps: {
36 | inline: ["vitest-canvas-mock"],
37 | },
38 | setupFiles: ["../shared/test/setup.tsx"],
39 | },
40 | });
41 |
--------------------------------------------------------------------------------
/packages/shared/assets/storybook/global.scss:
--------------------------------------------------------------------------------
1 | .css-1kwwth4 {
2 | color: inherit !important;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/shared/test/setup.tsx:
--------------------------------------------------------------------------------
1 | import "@testing-library/jest-dom";
2 | import type { ReactNode } from "react";
3 | import { vi } from "vitest";
4 | import "vitest-canvas-mock";
5 |
6 | /**
7 | * started agora-rtc-sdk-ng@17.0.0, need mock RTCPeerConnection.
8 | * RTCPeerConnection does not implement global
9 | */
10 | // @ts-expect-error type
11 | global.RTCPeerConnection = vi.fn();
12 |
13 | /**
14 | * JSDOM does not implement global "HTMLMediaElement.prototype.play" function
15 | */
16 | HTMLMediaElement.prototype.play = vi.fn().mockReturnValue(Promise.resolve());
17 | HTMLMediaElement.prototype.pause = vi.fn().mockReturnValue(Promise.resolve());
18 |
19 | /**
20 | * navigator does not implement global "mediaDevices.prototype.getUserMedia" function
21 | * navigator does not implement global "mediaDevices.prototype.enumerateDevices" function
22 | *
23 | */
24 | const mockPromise = vi.fn(async () => {
25 | return new Promise(resolve => {
26 | resolve();
27 | });
28 | });
29 | Object.defineProperty(global.navigator, "mediaDevices", {
30 | value: {
31 | getUserMedia: mockPromise,
32 | enumerateDevices: mockPromise,
33 | },
34 | });
35 |
36 | export interface Props {
37 | children: ReactNode;
38 | }
39 |
40 | window.MediaStreamTrack = jest.fn();
41 | window.RTCIceCandidate = jest.fn();
42 |
--------------------------------------------------------------------------------
/packages/shared/test/setup/agora.tsx:
--------------------------------------------------------------------------------
1 | import AgoraRTC from "agora-rtc-sdk-ng";
2 |
3 | AgoraRTC.setLogLevel(4);
4 |
5 | export const errorMessage = "Agora-RTC-REACT [ERROR_TEST_MSG]";
6 |
--------------------------------------------------------------------------------
/packages/shared/test/setup/wrapper.tsx:
--------------------------------------------------------------------------------
1 | import type { IAgoraRTCClient } from "agora-rtc-sdk-ng";
2 | import type { ReactNode } from "react";
3 | import React from "react";
4 |
5 | import { AgoraRTCProvider } from "../../../agora-rtc-react/src/hooks";
6 | import type { Props } from "../setup";
7 |
8 | export const createWrapper =
9 | (client: IAgoraRTCClient): React.FC =>
10 | ({ children }: { children: ReactNode }) =>
11 | {children} ;
12 |
--------------------------------------------------------------------------------
/patches/seedrandom@3.0.5.patch:
--------------------------------------------------------------------------------
1 | diff --git a/seedrandom.js b/seedrandom.js
2 | index 12d7ee1ddd93e8708629953a7ef416f64cc4be75..63439c59165c5526961eeb9a29b414c2182d118c 100644
3 | --- a/seedrandom.js
4 | +++ b/seedrandom.js
5 | @@ -232,9 +232,10 @@ mixkey(math.random(), pool);
6 | if ((typeof module) == 'object' && module.exports) {
7 | module.exports = seedrandom;
8 | // When in node.js, try using crypto package for autoseeding.
9 | - try {
10 | - nodecrypto = require('crypto');
11 | - } catch (ex) {}
12 | + // PATCH: remove nodecrypto
13 | + // try {
14 | + // nodecrypto = require('crypto');
15 | + // } catch (ex) {}
16 | } else if ((typeof define) == 'function' && define.amd) {
17 | define(function() { return seedrandom; });
18 | } else {
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - packages/*
3 | - examples/*
4 |
--------------------------------------------------------------------------------
/scripts/const.ts:
--------------------------------------------------------------------------------
1 | import path from "path";
2 |
3 | export const docType = ["components", "hooks", "data-types"];
4 | export const languages = ["", "-en"];
5 | export const languagesFormat = [".zh-CN", ".en-US"];
6 |
7 | export const packagePath = path.join(__dirname, "..", "packages", "agora-rtc-react");
8 | export const docsPath = path.join(packagePath, "docs");
9 | export const urlPrefix = [
10 | "https://doc.shengwang.cn/api-ref/rtc/react/react-sdk/",
11 | "https://api-ref.agora.io/en/video-sdk/reactjs/2.x/",
12 | ];
13 |
14 | export const storiesPath = path.join(packagePath, "src", "stories");
15 | export const hooksPath = path.join(packagePath, "src", "hooks");
16 | export const componentsPath = path.join(packagePath, "src", "components");
17 | export const dataTypesPathList = [
18 | path.join(packagePath, "src", "types.ts"),
19 | path.join(packagePath, "src", "rtc.ts"),
20 | path.join(packagePath, "src", "error.ts"),
21 | ];
22 |
--------------------------------------------------------------------------------
/scripts/copy-docs.ts:
--------------------------------------------------------------------------------
1 | import fs from "node:fs";
2 | import path from "node:path";
3 |
4 | const docsPath = path.join(__dirname, "..", "docs");
5 |
6 | if (fs.existsSync(docsPath)) {
7 | fs.rmSync(docsPath, { recursive: true, force: true });
8 | } else {
9 | fs.mkdirSync(docsPath);
10 | }
11 |
12 | const storybookPath = path.join(__dirname, "..", "packages", "agora-rtc-react", "storybook-static");
13 | if (fs.existsSync(storybookPath)) {
14 | fs.cpSync(storybookPath, docsPath, { recursive: true });
15 | }
16 |
17 | for (const exampleName of fs.readdirSync("examples")) {
18 | if (exampleName.startsWith(".")) continue;
19 |
20 | const examplePath = path.join("examples", exampleName, "dist");
21 | if (fs.existsSync(examplePath) && fs.lstatSync(examplePath).isDirectory()) {
22 | fs.cpSync(examplePath, path.join(docsPath, exampleName), { recursive: true });
23 | }
24 | }
25 |
26 | // copy typedoc
27 | const typedocPath = path.join(__dirname, "..", "typedoc");
28 | fs.cpSync(typedocPath, path.join(docsPath, "api-ref"), { recursive: true });
29 |
--------------------------------------------------------------------------------
/scripts/docs/generate-storybook-mdx.ts:
--------------------------------------------------------------------------------
1 | import fs from "node:fs";
2 | import path from "node:path";
3 |
4 | import { docType, docsPath, languagesFormat, storiesPath } from "../const";
5 | import { emptyDirectory } from "../utils";
6 |
7 | for (let j = 0; j < docType.length; j++) {
8 | emptyDirectory(`${storiesPath}/${docType[j]}`);
9 | }
10 |
11 | const copyDir = (sourceDir, targetDir) => {
12 | fs.readdir(sourceDir, (err, files) => {
13 | if (err) throw err;
14 | files.forEach(file => {
15 | const sourcePath = path.join(sourceDir, file);
16 | const targetPath = path.join(targetDir, file);
17 | fs.stat(sourcePath, (err, stats) => {
18 | if (err) throw err;
19 | if (stats.isDirectory()) {
20 | // only copy hooks
21 | if (path.basename(sourcePath) !== docType[1]) {
22 | return;
23 | }
24 | fs.stat(targetPath, err => {
25 | if (err) throw err;
26 | copyDir(sourcePath, targetPath);
27 | });
28 | } else {
29 | //only copy .en-US.md
30 | if (file.indexOf(languagesFormat[1]) === -1) {
31 | return;
32 | }
33 | const docType = path.basename(path.dirname(targetPath));
34 | const prependContent = `import Readme from "../../../docs/${docType}/${file}?raw";\r\rimport { Meta, Markdown } from "@storybook/blocks";\r\r \r\r{Readme} \r`;
38 | fs.writeFile(targetPath, prependContent, err => {
39 | if (err) throw err;
40 | console.log(`${sourcePath} copied to ${targetPath}`);
41 | });
42 | }
43 | });
44 | });
45 | });
46 | };
47 |
48 | copyDir(docsPath, storiesPath);
49 |
--------------------------------------------------------------------------------
/scripts/docs/utils.ts:
--------------------------------------------------------------------------------
1 | import fs from "node:fs";
2 | import path from "node:path";
3 |
4 | export function tableToJson(table) {
5 | if (!table) {
6 | return [];
7 | }
8 | const data: HTMLTableCellElement[][] = [];
9 | for (let i = 1; i < table.rows.length; i++) {
10 | const tableRow: HTMLTableRowElement = table.rows[i];
11 | const rowData: HTMLTableCellElement[] = [];
12 | for (let j = 0; j < tableRow.cells.length; j++) {
13 | rowData.push(tableRow.cells[j]);
14 | }
15 | data.push(rowData);
16 | }
17 | return data;
18 | }
19 |
20 | export async function readDirRecursively(dir, handler) {
21 | const files = fs.readdirSync(dir);
22 | files.forEach(async file => {
23 | const filePath = path.join(dir, file);
24 | const stat = fs.statSync(filePath);
25 |
26 | if (stat.isDirectory()) {
27 | readDirRecursively(filePath, handler);
28 | } else {
29 | handler && handler(filePath);
30 | }
31 | });
32 | }
33 |
--------------------------------------------------------------------------------
/scripts/publishCN/common.sh:
--------------------------------------------------------------------------------
1 | # common.sh
2 | #!/bin/bash
3 |
4 | old_package_name=agora-rtc-react
5 | new_package_name=shengwang-rtc-react
6 |
7 | old_web_sdk_rtc=agora-rtc-sdk-ng
8 | new_web_sdk_rtc=shengwang-rtc-sdk-ng
9 |
10 |
11 |
--------------------------------------------------------------------------------
/scripts/publishCN/rewrite-dep.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 | MY_PATH=$(realpath $(dirname "$0"))
4 | PROJECT_ROOT=$(realpath ${MY_PATH}/../..)
5 | . ${PROJECT_ROOT}/scripts/publishCN/common.sh
6 |
7 | change_file=${PROJECT_ROOT}/package.json
8 | sed "s/${old_package_name}/${new_package_name}/g" ${change_file} > tmp && mv tmp ${change_file}
9 | echo "${change_file} rewritten successfully"
10 |
11 | change_file=${PROJECT_ROOT}/packages/agora-rtc-react/package.json
12 | sed "s/${old_package_name}/${new_package_name}/g" ${change_file} > tmp && mv tmp ${change_file}
13 | echo "${change_file} rewritten successfully"
14 |
15 | change_file=${PROJECT_ROOT}/packages/agora-rtc-react-ui/package.json
16 | sed "s/${old_package_name}/${new_package_name}/g" ${change_file} > tmp && mv tmp ${change_file}
17 | echo "${change_file} rewritten successfully"
18 |
--------------------------------------------------------------------------------
/scripts/publishCN/rewrite-example.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 | MY_PATH=$(realpath $(dirname "$0"))
4 | PROJECT_ROOT=$(realpath ${MY_PATH}/../..)
5 | . ${PROJECT_ROOT}/scripts/publishCN/common.sh
6 |
7 | change_dir="${PROJECT_ROOT}/examples/basic"
8 |
9 | find "$change_dir" -type f | while read -r file; do
10 | if [[ "$file" == *".ts" || "$file" == *".tsx" ]]; then
11 | sed -i.bak "s/${old_package_name}/${new_package_name}/g" "$file"
12 | echo "Replaced in $file"
13 | fi
14 | done
15 |
16 | find "$change_dir" -name "*.bak" -type f -delete
17 |
18 | echo "All replacements completed successfully, and backup files have been deleted."
19 |
20 | change_file=${PROJECT_ROOT}/examples/basic/package.json
21 | sed "s/${old_package_name}/${new_package_name}/g" ${change_file} > tmp && mv tmp ${change_file}
22 | echo "${change_file} rewritten successfully"
23 |
--------------------------------------------------------------------------------
/scripts/release/clean.ts:
--------------------------------------------------------------------------------
1 | import fs from "node:fs";
2 |
3 | const args = process.argv.slice(2);
4 |
5 | for (let i = 0; i < args.length; i++) {
6 | if (args[i].startsWith("-")) {
7 | args.splice(args.indexOf(args[i]), 1);
8 | i--;
9 | }
10 | }
11 |
12 | if (args.length < 2 || !args[0].startsWith("source:") || !args[1].includes("@")) {
13 | console.log("Usage: node clean.ts source:@");
14 | process.exit(1);
15 | }
16 |
17 | const source = args[0].substring(7);
18 | const output = args[1].substring(args[1].lastIndexOf("@") + 1);
19 |
20 | fs.readFile(source, "utf8", (err, data) => {
21 | if (err) throw err;
22 |
23 | const pkg = JSON.parse(data);
24 |
25 | delete pkg.scripts;
26 | delete pkg.devDependencies;
27 | delete pkg["release-it"];
28 | delete pkg.source;
29 | delete pkg["publish-config"];
30 | pkg["main"] = `dist/${pkg.name}.js`;
31 | pkg["types"] = `dist/${pkg.name}.d.ts`;
32 | pkg["module"] = `dist/${pkg.name}.mjs`;
33 |
34 | fs.writeFile(output, JSON.stringify(pkg), "utf8", err => {
35 | if (err) throw err;
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/scripts/release/update-version.ts:
--------------------------------------------------------------------------------
1 | import fs from "node:fs";
2 | import path from "node:path";
3 |
4 | const args = process.argv.slice(2);
5 |
6 | for (let i = 0; i < args.length; i++) {
7 | if (args[i].startsWith("-")) {
8 | args.splice(args.indexOf(args[i]), 1);
9 | i--;
10 | }
11 | }
12 |
13 | if (args.length < 2 || !args[0].startsWith("target:") || !args[1].includes("@")) {
14 | console.log("Usage: node update-version.ts target:@");
15 | process.exit(1);
16 | }
17 |
18 | const targetName = args[0].substring(7);
19 | const newVersion = args[1].substring(args[1].lastIndexOf("@") + 1);
20 |
21 | const targetPath = path.join(__dirname, "..", "..", "packages", targetName, "src", "rtc.ts");
22 |
23 | fs.readFile(targetPath, "utf8", (err, data) => {
24 | if (err) {
25 | console.error(err);
26 | return;
27 | }
28 |
29 | const updatedData = data.replace(
30 | /export const VERSION = ".+"/,
31 | `export const VERSION = "${newVersion}"`,
32 | );
33 |
34 | fs.writeFile(targetPath, updatedData, err => {
35 | if (err) {
36 | console.error(err);
37 | return;
38 | }
39 | console.log(`${targetPath} updated with version ${newVersion}`);
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/scripts/tsup/set-globals.ts:
--------------------------------------------------------------------------------
1 | const setGlobals = (
2 | globals: {
3 | [key: string]: string;
4 | } = {},
5 | ) => {
6 | return {
7 | name: "globals",
8 | setup({ onResolve, onLoad }) {
9 | onResolve({ filter: /^[^.]/ }, args => {
10 | if (args.path in globals) {
11 | return { path: args.path, namespace: "globals" };
12 | }
13 | });
14 |
15 | onLoad({ filter: /(?:)/, namespace: "globals" }, args => {
16 | return { contents: `module.exports = ${globals[args.path]}` };
17 | });
18 | },
19 | };
20 | };
21 |
22 | export default setGlobals;
23 |
--------------------------------------------------------------------------------
/scripts/upgrade-deps.ts:
--------------------------------------------------------------------------------
1 | import fs from "node:fs";
2 | import path from "node:path";
3 |
4 | const args = process.argv.slice(2);
5 |
6 | for (let i = 0; i < args.length; i++) {
7 | if (args[i].startsWith("-")) {
8 | args.splice(args.indexOf(args[i]), 1);
9 | i--;
10 | }
11 | }
12 |
13 | if (args.length < 2 || !args[0].startsWith("dep:") || !args[1].includes("@")) {
14 | console.log("Usage: node update-dep-version.js dep:@");
15 | process.exit(1);
16 | }
17 |
18 | const depName = args[0].substring(4);
19 | const newVersion = args[1].substring(args[1].lastIndexOf("@") + 1);
20 |
21 | const packageJsonPath = path.join(process.cwd(), "package.json");
22 | const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
23 |
24 | if (packageJson.peerDependencies && packageJson.peerDependencies[depName]) {
25 | packageJson.peerDependencies[depName] = `>=${newVersion}`;
26 | console.log(`PeerDependency ${depName} version updated to >=${newVersion} in ${packageJsonPath}`);
27 | }
28 |
29 | if (packageJson.dependencies && packageJson.dependencies[depName]) {
30 | packageJson.dependencies[depName] = `${newVersion}`;
31 | console.log(`Dependency ${depName} version updated to ${newVersion} in ${packageJsonPath}`);
32 | }
33 |
34 | if (packageJson.devDependencies && packageJson.devDependencies[depName]) {
35 | packageJson.devDependencies[depName] = `${newVersion}`;
36 | console.log(`DevDependency ${depName} version updated to ${newVersion} in ${packageJsonPath}`);
37 | }
38 |
39 | fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n");
40 |
--------------------------------------------------------------------------------
/scripts/utils.ts:
--------------------------------------------------------------------------------
1 | import fs from "node:fs";
2 |
3 | export function emptyDirectory(path) {
4 | if (fs.existsSync(path)) {
5 | fs.readdirSync(path).forEach(function (file) {
6 | const curPath = path + "/" + file;
7 | if (fs.lstatSync(curPath).isDirectory()) {
8 | emptyDirectory(curPath);
9 | fs.rmdirSync(curPath);
10 | } else {
11 | fs.unlinkSync(curPath);
12 | }
13 | });
14 | } else {
15 | fs.mkdirSync(path);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------