# @hiyve/rtc-client-rn

React Native WebRTC client library for building mobile video conferencing applications on iOS and Android. Provides the same core functionality as `@hiyve/rtc-client` (web) with native mobile support including camera switching, platform-aware permissions, and `RTCView`-compatible media streams.

## Features

- Real-time audio/video communication via WebRTC
- iOS and Android support with `react-native-webrtc`
- Front/back camera switching
- Audio and video mute controls
- Room creation and joining
- Remote participant track events
- Recording and streaming status events
- Real-time transcription events
- Audio-only mode
- Bandwidth monitoring
- Branded aliases (`HiyveClient`, `HiyveClientEvents`)

## Installation

```bash
npm install @hiyve/rtc-client-rn
```

### Peer Dependencies

```bash
npm install react-native-webrtc @react-native-async-storage/async-storage
```

**iOS:** Install CocoaPods after adding `react-native-webrtc`:

```bash
cd ios && bundle exec pod install
```

**iOS `Info.plist`** — add camera and microphone usage descriptions:

```xml
<key>NSCameraUsageDescription</key>
<string>This app needs camera access for video calls</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs microphone access for audio calls</string>
```

**Android `AndroidManifest.xml`** — add permissions:

```xml
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
```

## Quick Start

```javascript
import { Client, ClientEvents } from '@hiyve/rtc-client-rn';
import { RTCView } from 'react-native-webrtc';

// 1. Create client with a room token from your server
const client = new Client({
  roomToken: 'your-room-token',
  serverRegionUrl: 'https://us-east-1.mediaserver.example.com',
  regions: ['us-east-1'],
});

// 2. Listen for events
client.on(ClientEvents.LOCAL_STREAM_READY, ({ stream }) => {
  // Render with <RTCView streamURL={stream.toURL()} mirror={true} />
});

client.on(ClientEvents.CONNECTED, () => {
  console.log('Connected to room');
});

client.on(ClientEvents.MEDIA_TRACK_ADDED, ({ userId, kind, stream }) => {
  // Store stream per userId+kind, render with <RTCView streamURL={stream.toURL()} />
});

client.on(ClientEvents.USER_DISCONNECTED, ({ userId }) => {
  // Remove participant from state
});

// 3. Create or join a room
await client.createRoom({ roomName: 'my-room', userId: 'user-123' });

// 4. Connect media transports (starts producing audio/video)
await client.connectTransports({});

// 5. Controls
await client.muteLocalAudio(true);   // Mute mic
await client.muteLocalVideo(true);   // Disable camera
await client.switchCamera();          // Flip front/back camera

// 6. Cleanup
client.removeAllListeners();
await client.closeConnection();
```

## API Reference

### Constructor

```javascript
new Client({ roomToken, serverRegionUrl, regions?, options? })
```

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `roomToken` | `string` | Yes | Authentication token obtained from your server |
| `serverRegionUrl` | `string` | Yes | Media server URL (e.g. `https://us-east-1.mediaserver.example.com`) |
| `regions` | `string[]` | No | Available server regions |
| `options.enableBandwidthMonitoring` | `boolean` | No | Enable network bandwidth monitoring |
| `options.audioMode` | `string` | No | Audio processing mode |

### Methods

| Method | Signature | Description |
|--------|-----------|-------------|
| `createRoom` | `({ roomName, userId, externalUserId? }) => Promise` | Create a new room and become the owner |
| `joinRoom` | `({ roomName, userId, externalUserId? }) => Promise` | Join an existing room as a participant |
| `connectTransports` | `({ options?, waitingRoomToken?, audioOnly? }) => Promise<void>` | Connect media transports and start producing audio/video |
| `muteLocalAudio` | `(mute: boolean, override?: boolean) => Promise` | Mute or unmute the local microphone |
| `muteLocalVideo` | `(mute: boolean, override?: boolean) => Promise` | Mute or unmute the local camera |
| `switchCamera` | `() => Promise<void>` | Switch between front and back camera |
| `closeConnection` | `() => Promise<void>` | Disconnect from the room and release all resources |
| `isLocalAudioPaused` | `() => boolean` | Check if local audio is currently muted |
| `isLocalVideoPaused` | `() => boolean` | Check if local video is currently muted |
| `on` | `(event: string, callback: Function) => void` | Register an event listener |
| `removeAllListeners` | `() => void` | Remove all registered event listeners |

### Events

Register listeners with `client.on(ClientEvents.EVENT_NAME, callback)`.

#### Connection Events

| Event | Payload | Description |
|-------|---------|-------------|
| `READY` | — | Client is initialized and ready |
| `CONNECTED` | — | Successfully connected to the room |
| `DISCONNECTED` | — | Disconnected from the room |
| `ROOM_JOINED` | `{ roomName }` | Room has been joined |
| `ROOM_CLOSED` | — | Room was closed by the owner |
| `ERROR` | `{ error }` | An error occurred |

#### Participant Events

| Event | Payload | Description |
|-------|---------|-------------|
| `USER_JOINED_ROOM` | `{ userId }` | A remote participant joined the room |
| `USER_DISCONNECTED` | `{ userId }` | A remote participant left the room |
| `NEW_PRODUCER` | `{ userId, kind }` | A remote participant started producing media |

#### Media Events

| Event | Payload | Description |
|-------|---------|-------------|
| `LOCAL_STREAM_READY` | `{ stream }` | Local camera/mic stream is available for rendering |
| `MEDIA_TRACK_ADDED` | `{ userId, kind, stream }` | A remote participant's audio or video track is available |
| `MEDIA_TRACK_REMOVED` | `{ userId, kind }` | A remote participant's track was removed |
| `AUDIO_MUTED` | `{ muted }` | Local audio mute state changed (`'muted'` or `'unmuted'`) |
| `VIDEO_MUTED` | `{ muted }` | Local video mute state changed |
| `REMOTE_AUDIO_MUTED` | `{ userId, muted }` | A remote participant's audio mute state changed |
| `REMOTE_VIDEO_MUTED` | `{ userId, muted }` | A remote participant's video mute state changed |
| `OUTPUT_MUTED` | `{ muted }` | Local output (speaker) mute state changed |
| `REMOTE_OUTPUT_MUTED` | `{ userId, muted }` | A remote participant's output mute state changed |

#### Feature Events

| Event | Payload | Description |
|-------|---------|-------------|
| `RECORDING_STARTED` | — | Room recording has started |
| `RECORDING_STOPPED` | — | Room recording has stopped |
| `STREAMING_STARTED` | — | Room live streaming has started |
| `STREAMING_STOPPED` | — | Room live streaming has stopped |
| `RECEIVE_CHAT_MESSAGE` | `{ message }` | A chat message was received |
| `TRANSCRIPTION_RECEIVED` | `{ transcription }` | Real-time transcription data received |
| `DATA_MESSAGE` | `{ data }` | A custom data message was received |

### Additional Exports

| Export | Description |
|--------|-------------|
| `ClientManager` | Manages multiple client instances |
| `DeviceCheck` | Device permission checking utility |
| `MediaDeviceTester` | Audio/video device testing |
| `BandwidthMonitor` | Network bandwidth monitoring |
| `FileSystemCache` | Media file caching |
| `EnhancedFileSystemCache` | Extended file caching with additional features |
| `cleanRoomName(name)` | Sanitize a room name string |
| `cleanUserId(id)` | Sanitize a user ID string |
| `getJoinToken(params)` | Obtain a join token for a room |
| `getRoomNameFromJoinToken(token)` | Extract room name from a join token |
| `waitForHostToStartRoom(params)` | Wait until the room host starts the session |

### Branded Aliases

```javascript
import { HiyveClient, HiyveClientEvents } from '@hiyve/rtc-client-rn';
// Equivalent to Client and ClientEvents
```

## Rendering Video

Use `RTCView` from `react-native-webrtc` to render streams:

```jsx
import { RTCView } from 'react-native-webrtc';

// Local video (front camera, mirrored)
<RTCView
  streamURL={localStream.toURL()}
  objectFit="cover"
  mirror={true}
  style={{ width: '100%', height: 200 }}
/>

// Remote video
<RTCView
  streamURL={remoteStream.toURL()}
  objectFit="cover"
  style={{ width: '100%', height: 200 }}
/>
```

Key differences from web `<video>` elements:
- Use `streamURL` (from `stream.toURL()`) instead of `srcObject`
- `objectFit` and `mirror` are component props, not CSS
- Each consumer provides its own `MediaStream` via `MEDIA_TRACK_ADDED`

## Platform Notes

### iOS

- Camera and microphone usage descriptions must be in `Info.plist`
- CocoaPods required for `react-native-webrtc` native modules
- iOS Simulator has limited camera support; use a physical device for full testing
- iOS Simulator can access `localhost` directly

### Android

- Camera and microphone permissions must be requested at runtime via `PermissionsAndroid`
- Android Emulator maps `10.0.2.2` to the host machine's `localhost`
- Requires Android SDK 24+
- Use an x86_64 emulator image with hardware acceleration

## Requirements

- React Native 0.73+
- `react-native-webrtc` >= 118.0.0
- `@react-native-async-storage/async-storage` >= 1.0.0
- A Hiyve room token (obtained from your backend server)

## Related Packages

| Package | Description |
|---------|-------------|
| `@hiyve/rtc-client` | Web browser WebRTC client (same API surface) |
| `@hiyve/core` | Framework-agnostic state store for Hiyve applications |
| `@hiyve/react` | React bindings with hooks for web applications |

## License

Commercial - IWantToPractice, LLC
