# Example App Walkthrough

The [Streampixel SDK Example](https://github.com/infinity-void-metaverse/Streampixel-SDK-Example) is a React application that demonstrates a complete SDK integration. This page walks through its structure and key patterns.

## Project Structure

```
Streampixel-SDK-Example/
├── public/
│   ├── index.html            # HTML entry point
│   ├── favicon.png
│   └── manifest.json         # PWA manifest
├── src/
│   ├── index.js              # React entry point
│   ├── index.css             # All styling (548 lines)
│   └── components/
│       └── App.js            # Main application component (706 lines)
├── config-overrides.js       # Webpack polyfills for the SDK
├── package.json
└── package-lock.json
```

## Setup and Running

```bash
git clone https://github.com/infinity-void-metaverse/Streampixel-SDK-Example.git
cd Streampixel-SDK-Example
npm install
npm start
```

Open `http://localhost:3000/YOUR_PROJECT_ID` in your browser.

### URL Parameters

The example reads configuration from the URL:

| Parameter    | Source                   | Description                         |
| ------------ | ------------------------ | ----------------------------------- |
| Project ID   | URL path (`/PROJECT_ID`) | Your Streampixel project identifier |
| `streamerId` | Query param              | Target a specific UE instance       |
| `sfuHost`    | Query param              | Set to `true` for SFU host mode     |
| `sfuPlayer`  | Query param              | Set to `true` for SFU viewer mode   |

Example URL: `http://localhost:3000/abc123?sfuPlayer=true&streamerId=host1`

## Feature Toggles

At the top of `App.js`:

```javascript
const SHOW_DEV_TOOLS = true;  // Set to false to hide the Developer Tools panel
```

{% hint style="info" %}
Set `SHOW_DEV_TOOLS` to `false` before deploying to production. The Developer Tools panel exposes console commands, JSON messaging, and mic/camera controls that are intended for development only.
{% endhint %}

## LOADING\_CONFIG

{% hint style="success" %}
The `LOADING_CONFIG` object is the easiest way to customize the loading experience. Change text, colors, and status messages in one place without modifying any event handler logic.
{% endhint %}

The example centralizes all loading screen text and colors in a single configuration object:

```javascript
const LOADING_CONFIG = {
  backgroundColor: '#18181A',
  accentColor: '#4e9cff',
  logoUrl: null,                    // Path to a logo image
  title: 'Connecting to Stream',
  subtitle: 'Please wait while we set up your experience...',
  disconnectedSubtitle: 'The stream session has ended.',
  showSpinner: true,
  queueMessage: (position) => `You are in queue at position ${position}`,

  statusMessages: {
    initializing:     'Initializing...',
    connecting:       'Connecting to server...',
    webRtcConnecting: 'Establishing WebRTC connection...',
    sdpNegotiation:   'Negotiating stream parameters...',
    webRtcConnected:  'WebRTC connected, loading stream...',
    streamLoading:    'Stream is loading...',
    playingStream:    'Starting video playback...',
    inQueue:          'Waiting in queue...',
    failed:           'Connection failed. Please try again.',
    disconnected:     'Disconnected from stream.',
    reconnecting:     'Reconnecting to stream...',
    retrying:         'Retrying connection...',
    reconnected:      'Reconnected! Loading stream...',
    reconnectFailed:  'Unable to reconnect. Please refresh the page.',
  },

  reconnectingTitle:       'Reconnecting',
  reconnectingSubtitle:    'Please wait while we restore your session...',
  reconnectedTitle:        'Reconnected',
  reconnectFailedTitle:    'Reconnection Failed',
  reconnectFailedSubtitle: 'We were unable to restore your session.',
};
```

Modify this object to customize the loading experience without touching event handlers.

## How the Example Wires SDK Events

### SDK Initialization

The app initializes inside a `useEffect` hook that runs when `projectId` is available:

```javascript
useEffect(() => {
  if (!projectId) return;

  const startPlay = async () => {
    const { appStream, pixelStreaming, queueHandler, UIControl, reconnectStream } =
      await StreamPixelApplication({
        appId: projectId,
        AutoConnect: true,
        streamerId,
        sfuHost,
        sfuPlayer,
        forceTurn: true,
      });

    // Store refs for use in event handlers
    pixelStreamingRef.current = pixelStreaming;
    appStreamRef.current = appStream;
    uiControlRef.current = UIControl;

    // ... register events ...
  };

  startPlay();
}, [projectId, streamerId, sfuHost, sfuPlayer]);
```

### Lifecycle Events → Loading Progress

Each WebRTC event updates React state for the loading screen:

```javascript
pixelStreaming.addEventListener('webRtcConnecting', () => {
  setLoadingStatus(LOADING_CONFIG.statusMessages.webRtcConnecting);
  setLoadingProgress(30);
});

// ... similar for each event ...

appStream.onVideoInitialized = () => {
  videoRef.current.append(appStream.rootElement);
  setLoadingProgress(100);
  setTimeout(() => setIsLoading(false), 300);
};
```

### Reconnection → Loading Screen

The reconnection state drives the loading screen back into view:

```javascript
reconnectStream.on('state', (data) => {
  switch (data.status) {
    case 'reconnecting':
      setIsLoading(true);
      setLoadingTitle(LOADING_CONFIG.reconnectingTitle);
      break;
    case 'connected':
      setLoadingTitle(LOADING_CONFIG.reconnectedTitle);
      break;
    case 'failed':
      setLoadingTitle(LOADING_CONFIG.reconnectFailedTitle);
      break;
  }
});
```

### AFK Warning → Custom Overlay

The example replaces the default AFK overlay with a styled card:

```javascript
pixelStreaming.addEventListener('afkWarningActivate', (e) => {
  setAfkWarning(true);
  setAfkCountdown(e.data.countDown);
  dismissAfkRef.current = e.data.dismissAfk;
});

pixelStreaming.addEventListener('afkWarningUpdate', (e) => {
  setAfkCountdown(e.data.countDown);
});

pixelStreaming.addEventListener('afkWarningDeactivate', () => {
  setAfkWarning(false);
});
```

The dismiss button calls `dismissAfkRef.current()`.

## Developer Tools Panel

When `SHOW_DEV_TOOLS` is `true`, the example shows a panel with these sections:

| Section                   | What It Does                                                            |
| ------------------------- | ----------------------------------------------------------------------- |
| **Console Command**       | Sends UE console commands (e.g., `stat fps`) via `emitConsoleCommand()` |
| **UI Interaction (JSON)** | Sends custom JSON to UE via `emitUIInteraction()`                       |
| **Connection**            | Disconnect button via `pixelStreaming.disconnect()`                     |
| **Microphone & Camera**   | Enable mic/camera via `unmuteMicrophone()` / `unmuteCamera()`           |
| **Hovering Mouse**        | Toggle hover mouse via `UIControl.toggleHoveringMouse()`                |

## Stream Controls Toolbar

The bottom-right toolbar shows after loading completes:

* **Mute/Unmute** -- Toggles both video and audio elements
* **Fullscreen** -- Toggles browser fullscreen mode
* **Stream Info** -- Shows stats from `UIControl.getStreamStats()`
* **Settings** (if resolution enabled) -- Resolution selection dropdown
* **Dev Tools** (if enabled) -- Opens the Developer Tools panel

## Key React Patterns Used

* **`useRef`** for SDK instances (`pixelStreamingRef`, `appStreamRef`, `uiControlRef`) -- avoids re-renders
* **`useState`** for UI state (loading, controls, AFK, stats)
* **`useCallback`** for event handlers (mute, fullscreen, dev tools actions)
* **`mounted` flag** in useEffect to prevent state updates after unmount
* **Event handler `stopPropagation`** on dev tools inputs to prevent keyboard events from reaching the stream
