# Blue Billywig React Native Channel SDK

Embed Blue Billywig video channels in your React Native app with full navigation, authentication, and player control support.

## Installation

```bash
npm install @bluebillywig/react-native-channel react-native-webview
```

### iOS Setup

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

### Android Setup

No additional setup required.

## Quick Start

```tsx
import { BBChannel } from '@bluebillywig/react-native-channel';

function App() {
  return (
    <BBChannel
      channelUrl="https://demo.bbvms.com/ch/123.json"
      onReady={() => console.log('Channel loaded')}
      onMediaPlay={(media) => console.log('Playing:', media.title)}
    />
  );
}
```

## BBChannel Component

The main component for embedding a video channel.

### Props

#### Required

| Prop | Type | Description |
|------|------|-------------|
| `channelUrl` | `string` | URL to the channel JSON configuration |

#### Configuration Options

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `options.autoPlay` | `boolean` | `true` | Enable/disable autoplay |
| `options.searchBar` | `boolean` | `true` | Show/hide search bar |
| `options.noStats` | `boolean` | `false` | Disable analytics |
| `options.jwt` | `string` | - | JWT token for authenticated content |
| `options.rpcToken` | `string` | - | RPC token for authenticated content |
| `options.playout` | `string` | - | Custom playout configuration name |
| `options.contentId` | `string` | - | Initial content ID to display |
| `options.contentType` | `'mediaclip' \| 'mediacliplist'` | - | Type of initial content |
| `options.bundleUrl` | `string` | CDN URL | Custom bundle URL (must be *.bluebillywig.com) |
| `options.fullscreenMode` | `'fullscreen' \| 'landscape'` | `'landscape'` | Fullscreen behavior |

#### Lifecycle Events

| Prop | Type | Description |
|------|------|-------------|
| `onReady` | `() => void` | Called when channel is fully loaded |
| `onError` | `(error: BBErrorEvent) => void` | Called on errors |

#### Navigation Events

| Prop | Type | Description |
|------|------|-------------|
| `onNavigate` | `(event: BBNavigationEvent) => void` | Called on navigation changes |
| `onNavigationStateChange` | `(state: BBNavigationState) => void` | Called when navigation state changes |
| `onCanGoBackChange` | `(canGoBack: boolean) => void` | Called when back navigation availability changes |
| `initialNavigationState` | `BBNavigationState` | Initial state for deep linking |

#### Search Events

| Prop | Type | Description |
|------|------|-------------|
| `onSearch` | `(event: BBSearchEvent) => void` | Called when a search is performed |

#### Media Events

| Prop | Type | Description |
|------|------|-------------|
| `onMediaPlay` | `(media: BBMediaInfo) => void` | Called when media starts playing |
| `onMediaPause` | `(media: BBMediaInfo) => void` | Called when media is paused |
| `onMediaEnd` | `(media: BBMediaInfo) => void` | Called when media ends |
| `onFirstPlayerMedia` | `(media: BBMediaInfo) => void` | Called when first player media is identified |

#### Player Events

| Prop | Type | Description |
|------|------|-------------|
| `onPlayerStateChange` | `(state: BBPlayerState) => void` | Called on player state changes |
| `onPlayerEvent` | `(event: BBPlayerEvent) => void` | Called on any player event |
| `onTimeUpdate` | `(data: BBTimeUpdateEvent) => void` | Called frequently during playback |
| `onClipLoaded` | `(data: BBClipLoadedEvent) => void` | Called when a clip is loaded |
| `onAdStart` | `(data: BBAdEvent) => void` | Called when an ad starts |
| `onAdEnd` | `(data: BBAdEvent) => void` | Called when an ad ends |

### Ref Methods

Access methods via ref:

```tsx
const channelRef = useRef<BBChannelRef>(null);

// Later...
channelRef.current?.play();
channelRef.current?.setJwt('new-token');
```

#### Authentication Methods

| Method | Description |
|--------|-------------|
| `setJwt(jwt: string \| null)` | Update JWT token for authenticated content |
| `setRpcToken(token: string \| null)` | Update RPC token |

#### Navigation Methods

| Method | Description |
|--------|-------------|
| `navigateTo(page: 'main' \| 'search')` | Navigate to a page |
| `goBack()` | Navigate back |
| `search(query: string)` | Perform a search |
| `reload()` | Reload the channel |
| `playVideo(videoId: string, type?)` | Play a specific video |
| `navigateToEntity(entityId, entityType, options?)` | Navigate to entity detail page |
| `navigateToSubChannel(subChannelId: string)` | Navigate to sub-channel |
| `resetNavigation()` | Clear history and go to main |
| `setNavigationState(state: BBNavigationState)` | Restore navigation state |
| `getNavigationState(): Promise<BBNavigationState>` | Get current navigation state |
| `canGoBack(): Promise<boolean>` | Check if back navigation is possible |

#### Player Control Methods

| Method | Description |
|--------|-------------|
| `play()` | Start or resume playback |
| `pause()` | Pause playback |
| `seek(seconds: number)` | Seek to position |
| `setVolume(volume: number)` | Set volume (0.0 - 1.0) |
| `setMuted(muted: boolean)` | Mute or unmute |
| `enterFullscreen()` | Enter fullscreen mode |
| `exitFullscreen()` | Exit fullscreen mode |
| `getPlayerState(): Promise<BBPlayerState \| null>` | Get current player state |

## Types

### BBMediaInfo

Information about a media clip.

```typescript
interface BBMediaInfo {
  jsonUrl: string;        // URL for loading in native player
  playoutId: string;      // Playout configuration ID
  autoplay: boolean;      // Whether to autoplay
  thumbnailUrl?: string;  // Thumbnail URL
  title?: string;         // Media title
  mediaType?: 'video' | 'audio';  // Content type
}
```

### BBNavigationState

Full navigation state for deep linking and state persistence.

```typescript
interface BBNavigationState {
  pageType: 'main' | 'detailPage' | 'overviewPage' | 'searchPage';
  contentId?: string;           // Entity ID
  contentType?: 'mediaclip' | 'mediacliplist';
  searchQuery?: string;
  clickedSearchResult?: boolean;
  blockId?: string;
  contextId?: string;
  playout?: string;
  collectionId?: string;
  subChannelId?: string;
}
```

### BBPlayerState

Current player state.

```typescript
interface BBPlayerState {
  isPlaying: boolean;
  currentTime?: number;   // In seconds
  duration?: number;      // In seconds
  volume?: number;        // 0.0 - 1.0
  isMuted?: boolean;
  isFullscreen?: boolean;
}
```

### BBPlayerEvent

Union type for all player events. Use with `onPlayerEvent` for fine-grained control.

```typescript
// Event types:
// 'play', 'pause', 'ended', 'canplay', 'waiting',
// 'seeking', 'seeked', 'timeupdate', 'durationchange', 'progress',
// 'volumechange', 'fullscreenchange', 'loadstart', 'loadedmetadata', 'loadeddata',
// 'cliploaded', 'clipfailed', 'cliplistloaded',
// 'adstart', 'adend', 'adskip', 'aderror', 'error'
```

### BBTimeUpdateEvent

Time update data sent frequently during playback.

```typescript
interface BBTimeUpdateEvent {
  timestamp: number;
  clipId?: string;
  title?: string;
  currentTime: number;  // In seconds
  duration: number;     // In seconds
  progress: number;     // 0-100 percentage
}
```

### BBClipLoadedEvent

Clip loaded event data.

```typescript
interface BBClipLoadedEvent {
  timestamp: number;
  clipId: string;
  title: string;
  duration: number;     // In seconds
  thumbnailUrl?: string;
  isLive?: boolean;
}
```

### BBAdEvent

Advertisement event data.

```typescript
interface BBAdEvent {
  timestamp: number;
  position?: 'pre' | 'mid' | 'post';
  duration?: number;      // In seconds
  remainingTime?: number; // In seconds
  skippable?: boolean;
  skipOffset?: number;    // Seconds until skip available
}
```

### BBErrorEvent

Error event data.

```typescript
interface BBErrorEvent {
  code: string;
  message: string;
  details?: unknown;
}
```

### BBSearchEvent

Search event data.

```typescript
interface BBSearchEvent {
  query: string;
  resultCount: number;
}
```

## Examples

### Authentication

```tsx
function AuthenticatedChannel() {
  const channelRef = useRef<BBChannelRef>(null);
  const [token, setToken] = useState<string | null>(null);

  // Update token when it changes
  useEffect(() => {
    channelRef.current?.setJwt(token);
  }, [token]);

  return (
    <BBChannel
      ref={channelRef}
      channelUrl="https://demo.bbvms.com/ch/123.json"
      options={{ jwt: token ?? undefined }}
    />
  );
}
```

### Player Progress Tracking

```tsx
function ProgressTrackingChannel() {
  const [progress, setProgress] = useState(0);

  return (
    <>
      <ProgressBar value={progress} />
      <BBChannel
        channelUrl="https://demo.bbvms.com/ch/123.json"
        onTimeUpdate={(data) => setProgress(data.progress)}
        onPlayerEvent={(event) => {
          if (event.type === 'ended') {
            console.log('Video finished!');
          }
        }}
      />
    </>
  );
}
```

### Deep Linking

```tsx
function DeepLinkChannel() {
  const channelRef = useRef<BBChannelRef>(null);

  // Handle deep link
  useEffect(() => {
    const url = Linking.getInitialURL();
    if (url?.includes('video=')) {
      const videoId = extractVideoId(url);
      channelRef.current?.navigateToEntity(videoId, 'mediaclip', { autoPlay: true });
    }
  }, []);

  // Save state for restoration
  const handleNavigationChange = (state: BBNavigationState) => {
    AsyncStorage.setItem('channelState', JSON.stringify(state));
  };

  return (
    <BBChannel
      ref={channelRef}
      channelUrl="https://demo.bbvms.com/ch/123.json"
      onNavigationStateChange={handleNavigationChange}
    />
  );
}
```

### Android Back Button

```tsx
function BackButtonChannel() {
  const channelRef = useRef<BBChannelRef>(null);
  const [canGoBack, setCanGoBack] = useState(false);

  useEffect(() => {
    const backHandler = BackHandler.addEventListener('hardwareBackPress', () => {
      if (canGoBack) {
        channelRef.current?.goBack();
        return true;
      }
      return false;
    });
    return () => backHandler.remove();
  }, [canGoBack]);

  return (
    <BBChannel
      ref={channelRef}
      channelUrl="https://demo.bbvms.com/ch/123.json"
      onCanGoBackChange={setCanGoBack}
    />
  );
}
```

### Player Controls

```tsx
function PlayerControlsChannel() {
  const channelRef = useRef<BBChannelRef>(null);

  const handlePlayPause = async () => {
    const state = await channelRef.current?.getPlayerState();
    if (state?.isPlaying) {
      channelRef.current?.pause();
    } else {
      channelRef.current?.play();
    }
  };

  return (
    <>
      <TouchableOpacity onPress={handlePlayPause}>
        <Text>Play/Pause</Text>
      </TouchableOpacity>
      <BBChannel
        ref={channelRef}
        channelUrl="https://demo.bbvms.com/ch/123.json"
      />
    </>
  );
}
```

## BBChannelWithPlayer Component

A channel component with integrated native video player. Combines BBChannel (WebView) with BBPlayerView (native player) for optimal video playback. Requires `@bluebillywig/react-native-bb-player` as a peer dependency.

```tsx
import { BBChannelWithPlayer } from '@bluebillywig/react-native-channel';

function App() {
  return (
    <BBChannelWithPlayer
      channelUrl="https://demo.bbvms.com/ch/123.json"
      playerMode="inline"
      onNativePlayerStateChange={(state) => console.log('Player:', state)}
    />
  );
}
```

### Props (in addition to BBChannel props)

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `playerMode` | `'inline' \| 'fullscreen'` | `'inline'` | Player display mode |
| `playerHeight` | `number` | - | Fixed height for inline player (uses aspectRatio if not set) |
| `playerAspectRatio` | `number` | `16/9` | Aspect ratio for inline player |
| `showBackArrow` | `boolean` | `true` | Show close button over inline player |
| `jwt` | `string` | - | JWT token for both channel and player |
| `onNativePlayerStateChange` | `(state) => void` | - | Called on player state changes |
| `onNativePlayerError` | `(error) => void` | - | Called on player errors |

## Troubleshooting

### WebView not rendering

Make sure `react-native-webview` is properly installed and linked:

```bash
npm install react-native-webview
cd ios && pod install
```

### Video not playing on iOS

Ensure your app has the proper permissions:

```xml
<!-- ios/YourApp/Info.plist -->
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>
```

### Channel not loading

1. Verify the channel URL is accessible
2. Check network connectivity
3. Ensure CORS is properly configured on your publication

### Authentication errors

- Verify JWT/RPC tokens are valid and not expired
- Check that tokens have the correct permissions

## Support

- Documentation: https://support.bluebillywig.com
- Email: support@bluebillywig.com

## License

Proprietary - Blue Billywig
