npm package: @getcobrowse/react-native
Description: Cobrowse screen sharing SDK for React Native
Latest version: 0.1.9

# @getcobrowse/react-native

React Native SDK for Cobrowse screen sharing. Enables agents to view a customer's mobile screen in real-time with annotations, cursors, and highlights.

Uses native screen capture (ReplayKit on iOS, MediaProjection on Android) via Daily.js.

## Installation

```bash
# Install the SDK
npm install @getcobrowse/react-native

# Install peer dependencies
npm install @daily-co/react-native-daily-js @daily-co/react-native-webrtc react-native-svg
```

### iOS Setup (Expo)

Add the config plugin to your `app.json` or `app.config.js`:

```json
{
  "expo": {
    "plugins": [
      ["@getcobrowse/react-native/plugin/withCobrowseBroadcast", {
        "appGroup": "group.com.yourcompany.yourapp.cobrowse"
      }]
    ]
  }
}
```

Then rebuild:

```bash
npx expo prebuild --clean
npx expo run:ios --device
```

> **Important:** Screen sharing requires a **real device** — it does not work in the iOS Simulator. You also need a development build, not Expo Go.

### iOS Setup (Bare React Native)

iOS screen sharing requires a **Broadcast Upload Extension**. Daily's SDK handles all the IPC and frame encoding — you just need to create the target and wire it up.

**1. Create the extension target:**

In Xcode: File → New → Target → Broadcast Upload Extension. Name it `CobrowseBroadcast`. Uncheck "Include UI Extension".

**2. Replace `SampleHandler.swift`:**

Replace the generated `SampleHandler.swift` with:

```swift
import ReactNativeDailyJSScreenShareExtension

public class SampleHandler: DailyRPHandler {
  override init() {
    super.init(appGroupIdentifier: "group.com.yourcompany.yourapp.cobrowse")
  }
}
```

**3. Configure App Groups:**

In Xcode, enable the "App Groups" capability for **both** your main app target and the `CobrowseBroadcast` extension target. Add the same group identifier to both.

**4. Add to Info.plist:**

In your main app's `Info.plist`:

```xml
<key>RTCAppGroupIdentifier</key>
<string>group.com.yourcompany.yourapp.cobrowse</string>
<key>DailyScreenCaptureExtensionBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).CobrowseBroadcast</string>
```

**5. Install pods and rebuild:**

```bash
cd ios && pod install && cd ..
```

> **Important:** Screen sharing requires a **real device** — it does not work in the iOS Simulator.

### Android Setup

Add to `AndroidManifest.xml`:

```xml
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
```

Register the foreground service in your manifest:

```xml
<service
  android:name="com.daily.reactlibrary.DailyOngoingMeetingService"
  android:foregroundServiceType="mediaProjection" />
```

## Usage

### Basic Setup (2 lines)

Wrap your app with `CobrowseProvider`:

```tsx
import { CobrowseProvider } from '@getcobrowse/react-native'

export default function App() {
  return (
    <CobrowseProvider
      serverUrl="https://cobrowse.owner.sh"
      metadata={{ email: user.email, name: user.name }}
    >
      <YourApp />
    </CobrowseProvider>
  )
}
```

That's it. The SDK will:
- Register presence on the cobrowse dashboard
- Show a modal when an agent requests screen sharing
- Handle native screen capture via ReplayKit/MediaProjection
- Display agent annotations (cursor, highlights, pointers) as an overlay

### Track Current Screen

Pass `screenName` to show what screen the user is on in the dashboard:

```tsx
<CobrowseProvider
  serverUrl="https://cobrowse.owner.sh"
  metadata={{ email: user.email }}
  screenName={currentRouteName}
>
```

### Using the Hook

Access cobrowse state and controls from any component:

```tsx
import { useCobrowse } from '@getcobrowse/react-native'

function SupportButton() {
  const { state, agentName, stop } = useCobrowse()

  if (state === 'sharing') {
    return (
      <Button onPress={stop}>
        Stop sharing with {agentName}
      </Button>
    )
  }

  return null
}
```

### Auto-Accept (Kiosk Mode)

Skip the permission modal and auto-accept share requests:

```tsx
<CobrowseProvider
  serverUrl="https://cobrowse.owner.sh"
  metadata={{ deviceId: 'kiosk-001' }}
  autoAccept
>
```

### Floating Sharing Indicator

Use the built-in `SharingIndicator` component:

```tsx
import { SharingIndicator } from '@getcobrowse/react-native'

function MyScreen() {
  return (
    <View>
      {/* Your content */}
      <View style={{ position: 'absolute', bottom: 40, alignSelf: 'center' }}>
        <SharingIndicator />
      </View>
    </View>
  )
}
```

### Custom Share Request UI

Disable the built-in modal and handle requests yourself:

```tsx
import { CobrowseProvider, useCobrowse } from '@getcobrowse/react-native'

function App() {
  return (
    <CobrowseProvider
      serverUrl="https://cobrowse.owner.sh"
      metadata={{ email: user.email }}
      onShareRequest={(agentName) => {
        // Custom notification, bottom sheet, etc.
      }}
      autoAccept={false}
    >
      <CustomShareUI />
    </CobrowseProvider>
  )
}

function CustomShareUI() {
  const { state, agentName, acceptShare, declineShare } = useCobrowse()

  if (state !== 'share_requested') return null

  return (
    <BottomSheet>
      <Text>{agentName} wants to view your screen</Text>
      <Button onPress={acceptShare}>Allow</Button>
      <Button onPress={declineShare}>Decline</Button>
    </BottomSheet>
  )
}
```

### Callbacks

```tsx
<CobrowseProvider
  serverUrl="https://cobrowse.owner.sh"
  metadata={{ email: user.email }}
  onStateChange={(state) => analytics.track('cobrowse_state', { state })}
  onShareRequest={(agentName) => console.log(`${agentName} wants to share`)}
  onAnnotation={(event) => {
    // event.type: 'cursor' | 'highlight' | 'pointer' | 'clear' | 'cursor-hide'
  }}
>
```

## API Reference

### `<CobrowseProvider>`

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `serverUrl` | `string` | Yes | Cobrowse server URL |
| `metadata` | `Record<string, unknown>` | No | User identification metadata |
| `screenName` | `string` | No | Current screen name for dashboard |
| `autoAccept` | `boolean` | No | Auto-accept share requests |
| `onShareRequest` | `(agentName: string) => void` | No | Called when agent requests share |
| `onStateChange` | `(state: CobrowseState) => void` | No | Called on state transitions |
| `onAnnotation` | `(event: AnnotationEvent) => void` | No | Called when annotation received |

### `useCobrowse()`

Returns a `CobrowseHandle`:

| Property | Type | Description |
|----------|------|-------------|
| `state` | `CobrowseState` | Current SDK state |
| `agentName` | `string \| null` | Name of requesting/viewing agent |
| `acceptShare` | `() => void` | Accept pending share request |
| `declineShare` | `() => void` | Decline pending share request |
| `stop` | `() => Promise<void>` | Stop active screen share |
| `setMetadata` | `(metadata) => void` | Update user metadata |
| `setScreenName` | `(name) => void` | Update current screen name |
| `annotations` | `Annotation[]` | Current highlight annotations |
| `pointerAnimations` | `PointerAnimation[]` | Current pointer ripples |
| `cursor` | `CursorPosition` | Agent cursor position |

### `CobrowseState`

`'idle' | 'tracking' | 'share_requested' | 'connecting' | 'sharing' | 'stopping'`