# @hiyve/rn-core

Framework-agnostic state store for Hiyve React Native WebRTC applications.

Mirrors the `@hiyve/core` API for React Native, with platform-appropriate media handling, async lifecycle methods, and camera switching.

## Installation

```bash
npm install @hiyve/rn-core @hiyve/rtc-client-rn react-native-webrtc
```

## Quick Start

```typescript
import { HiyveStoreRN } from '@hiyve/rn-core';

const store = new HiyveStoreRN({
  generateRoomToken: async () => {
    const res = await fetch('https://your-api.com/token', { method: 'POST' });
    return res.json(); // { roomToken, serverRegionUrl, serverRegion }
  },
  onError: (err) => console.error('Hiyve error:', err),
});

// Create a room
await store.createRoom('my-room', 'user123');

// Subscribe to state changes
const unsub = store.subscribeToSlice('localMedia', () => {
  const media = store.getSlice('localMedia');
  console.log('Local stream:', media.localStream);
  console.log('Audio muted:', media.isAudioMuted);
});

// Toggle controls
await store.toggleAudio();
await store.toggleVideo();
await store.switchCamera(); // RN-only: flip front/back camera

// Leave and clean up
await store.leaveRoom();
unsub();
```

## API

### HiyveStoreRN

The main class. Created with `HiyveStoreRNOptions`:

| Option | Type | Description |
|--------|------|-------------|
| `generateRoomToken` | `() => Promise<TokenData>` | **Required.** Returns `{ roomToken, serverRegionUrl, serverRegion?, regions? }` |
| `storageAdapter` | `StorageAdapter` | Optional adapter for persisting device preferences |
| `onError` | `(error: Error) => void` | Optional error callback |

### Subscription API

| Method | Returns | Description |
|--------|---------|-------------|
| `subscribe(listener)` | `() => void` | Subscribe to all state changes. Returns unsubscribe function. |
| `subscribeToSlice(slice, listener)` | `() => void` | Subscribe to a specific slice. Returns unsubscribe function. |
| `getState()` | `HiyveStoreRNState` | Get the full current state snapshot. |
| `getSlice(slice)` | Slice type | Get a specific state slice. |
| `getClient()` | `Client \| null` | Get the raw RN Client instance. |

### Room Actions

| Method | Returns | Description |
|--------|---------|-------------|
| `createRoom(roomName, userId, options?)` | `Promise<void>` | Create a new room as host |
| `joinRoom(roomName, userId)` | `Promise<void>` | Join an existing room |
| `joinRoomWithToken(options)` | `Promise<void>` | Join a room using a join token from an invite link. Options: `{ joinToken, roomRegion, userId, password? }` |
| `getRoomInfoFromToken(options)` | `Promise<RoomInfoFromToken>` | Query room name and active status from a join token without joining. Options: `{ joinToken, roomRegion, password? }`. Returns `{ roomName, isActive }` |
| `leaveRoom()` | `Promise<void>` | Leave the room and disconnect |

### Media Controls

| Method | Returns | Description |
|--------|---------|-------------|
| `toggleAudio()` | `Promise<void>` | Toggle microphone mute/unmute |
| `toggleVideo()` | `Promise<void>` | Toggle camera on/off |
| `switchCamera()` | `Promise<void>` | Switch between front and back camera (RN-only) |

### Chat

| Method | Returns | Description |
|--------|---------|-------------|
| `sendMessage(content)` | `void` | Send a text chat message |
| `sendDataMessage(data)` | `void` | Send arbitrary JSON data |
| `clearUnread()` | `void` | Reset unread message counter |
| `loadChatHistory(cursor?)` | `Promise<{ hasMore }>` | Load older messages |

### Capture Controls

| Method | Returns | Description |
|--------|---------|-------------|
| `startRecording(options?)` | `Promise<boolean>` | Start cloud recording |
| `stopRecording()` | `Promise<void>` | Stop recording |
| `startStreaming(options?)` | `Promise<void>` | Start live streaming |
| `stopStreaming()` | `Promise<void>` | Stop streaming |
| `startTranscription()` | `Promise<boolean>` | Start real-time transcription |
| `stopTranscription()` | `Promise<void>` | Stop transcription |

### Waiting Room

| Method | Returns | Description |
|--------|---------|-------------|
| `admitUser(userId)` | `Promise<void>` | Admit a waiting user (host only) |
| `rejectUser(userId)` | `Promise<void>` | Reject a waiting user (host only) |

### Interaction

| Method | Returns | Description |
|--------|---------|-------------|
| `toggleHandRaised()` | `Promise<void>` | Toggle local user's hand raised state |
| `lowerAllHands()` | `Promise<void>` | Lower all hands (host only) |
| `setDominant(userId)` | `void` | Set dominant speaker for layout (host only) |

### Lifecycle

| Method | Returns | Description |
|--------|---------|-------------|
| `destroy()` | `Promise<void>` | Destroy the store and release all resources |

## State Slices

The store manages 14 state slices, each subscribable independently:

| Slice | Key Fields |
|-------|-----------|
| `connection` | `isConnected`, `isConnecting`, `error` |
| `room` | `room`, `isOwner`, `isInRoom` |
| `participants` | `participants` (Map), `localUserId` |
| `localMedia` | `isAudioMuted`, `isVideoMuted`, `localStream` |
| `recording` | `isRecording`, `isRecordingStarting`, `recordingId` |
| `streaming` | `isStreaming`, `streamingId`, `streamingUrl` |
| `transcription` | `isTranscribing`, `transcriptions[]` |
| `chat` | `messages[]`, `unreadCount` |
| `waitingRoom` | `waitingUsers[]`, `isWaitingForAdmission` |
| `waitForHost` | `isWaiting`, `roomName`, `elapsedTime` |
| `audioProcessing` | `feedbackDetected` |
| `layout` | `dominantSpeaker` |
| `handRaise` | `raisedHands` (Map) |
| `aiChat` | `messages[]` |

## Storage Adapter

For persisting device preferences across app restarts:

```typescript
import AsyncStorage from '@react-native-async-storage/async-storage';
import { createAsyncStorageAdapter } from '@hiyve/rn-core';

const storage = createAsyncStorageAdapter(AsyncStorage);
await storage.init(['hiyve-selected-devices']);

const store = new HiyveStoreRN({
  generateRoomToken: fetchToken,
  storageAdapter: storage,
});
```

## Platform Differences from @hiyve/core

| Feature | Web (`@hiyve/core`) | React Native (`@hiyve/rn-core`) |
|---------|--------------------|---------------------------------|
| Camera selection | `setVideoDevice(deviceId)` | `switchCamera()` (front/back toggle) |
| Screen sharing | Supported | Not available |
| Local video | Renders to a DOM element via `localVideoElementId` | Available as `localStream` in `localMedia` state |
| `leaveRoom()` | Synchronous | Async (`Promise<void>`) |
| `destroy()` | Synchronous | Async (`Promise<void>`) |
| Audio processing | `gainNode`, `audioInputMonitor`, `feedbackDetected` | `feedbackDetected` only |

## Debug Logging

The package exports a `debug` utility for troubleshooting. Debug logging is disabled by default; `error()` calls always print regardless.

```typescript
import { debug } from '@hiyve/rn-core';

if (__DEV__) {
  debug.enable();
}

debug.log('Store initialized');       // Only when enabled
debug.warn('Slow connection');        // Only when enabled
debug.error('Connection failed');     // Always printed
debug.disable();
```

| Method | Description |
|--------|-------------|
| `debug.enable()` | Enable debug logging at runtime |
| `debug.disable()` | Disable debug logging at runtime |
| `debug.log(...args)` | Log info (only when enabled) |
| `debug.warn(...args)` | Log warning (only when enabled) |
| `debug.error(...args)` | Log error (always printed) |
| `debug.isEnabled()` | Check if debug mode is active |

## Requirements

- React Native 0.70+
- `react-native-webrtc` >= 118.0.0
- `@hiyve/rtc-client-rn`
