# Custom HTML API reference

This document describes the JavaScript API available to custom HTML content rendered inside the Circle Plus mobile app. Use these functions and objects to interact with the native app from your custom HTML screens.

### Overview

When your custom HTML content is loaded inside the Circle mobile app, several JavaScript functions and objects are automatically injected into the `window` object. These allow you to:

* Access information about the currently authenticated user
* Navigate to other screens within the app
* Open URLs through the app's navigation system
* Send custom messages to the native app

All injected code is available immediately when your HTML loads - no additional setup required.

***

### Global Objects

#### window\.circleUser

An object containing information about the currently authenticated user.

**Type:** `Object`

**Properties:**

| Property      | Type      | Description                                 |
| ------------- | --------- | ------------------------------------------- |
| `name`        | `string`  | User's full display name                    |
| `email`       | `string`  | User's email address                        |
| `publicUid`   | `string`  | User's unique public identifier             |
| `isAdmin`     | `boolean` | `true` if the user has admin privileges     |
| `isModerator` | `boolean` | `true` if the user has moderator privileges |

**Example:**

```javascript
// Access current user information
console.log(window.circleUser);
// Output:
// {
//   "name": "John Doe",
//   "email": "john@example.com",
//   "publicUid": "abc123xyz",
//   "isAdmin": false,
//   "isModerator": true
// }

// Personalize content based on user
document.getElementById('greeting').textContent = `Welcome, ${window.circleUser.name}!`;

// Show admin-only content
if (window.circleUser.isAdmin) {
  document.getElementById('admin-panel').style.display = 'block';
}
```

***

### Navigation Functions

#### navigateToOrphanedScreen()

Navigates to another custom (orphaned/not in nav) screen within the app by its screen ID. This will "stop" the native navigation inside the webview.

**Signature:**

```javascript
window.navigateToOrphanedScreen(screenId)
```

**Parameters:**

| Parameter  | Type     | Required | Description                                                 |
| ---------- | -------- | -------- | ----------------------------------------------------------- |
| `screenId` | `string` | Yes      | The unique identifier of the orphaned screen to navigate to |

**Returns:** `void`

**Example:**

```javascript
// Navigate to a specific custom screen
window.navigateToOrphanedScreen('9a581ef4-8a0e-432b-8a83-2885c9b4e83a');

// Use in a button click handler
document.getElementById('next-btn').addEventListener('click', function() {
  window.navigateToOrphanedScreen('9a581ef4-8a0e-432b-8a83-2885c9b4e83a');
});
```

**Notes:**

* The screen ID must correspond to an existing orphaned screen in your Circle community
* Navigation is handled natively, providing smooth transitions

***

#### navigateToUrl()

Opens a URL through the app's navigation system. The app will handle the URL appropriately based on its type (deep links, external URLs, etc.).

**Signature:**

```javascript
window.navigateToUrl(url)
```

**Parameters:**

| Parameter | Type     | Required | Description            |
| --------- | -------- | -------- | ---------------------- |
| `url`     | `string` | Yes      | The URL to navigate to |

**Returns:** `void`

**Example:**

```javascript
// Navigate to an external website
window.navigateToUrl('https://example.com/resource');

// Navigate using a Circle deep link
window.navigateToUrl('https://community.circle.so/c/announcements');

// Use in a link click handler
document.querySelectorAll('a.native-nav').forEach(function(link) {
  link.addEventListener('click', function(e) {
    e.preventDefault();
    window.navigateToUrl(this.href);
  });
});
```

**Notes:**

* Deep links to Circle content will navigate within the app
* External URLs may open in an in-app browser or external browser depending on app configuration

### Feature Detection

You can check if your HTML is running inside the Circle mobile app:

```javascript
// Check if isInsideCircleMobileWebview is available
if (window.isInsideCircleMobileWebview) {
  // User data is available
  console.log('Logged in as:', window.circleUser.name);
}
```

***

### Callback Functions

The Circle mobile app provides callback functions that your custom HTML can call to notify the native app about specific events. These are particularly useful for signup flows, content creation, and payment processes.

#### window\.webview\.accountCreatedCallback()

Called when a user account is successfully created. This function notifies the native app and may clear cookies.

**Signature:**

```javascript
window.webview.accountCreatedCallback(data)
```

**Parameters:**

| Parameter | Type     | Required | Description                                                   |
| --------- | -------- | -------- | ------------------------------------------------------------- |
| `data`    | `object` | Yes      | Account creation data including email and profile information |

**Example:**

```javascript
// After successful account creation
window.webview.accountCreatedCallback({
  email: 'newuser@example.com'
});
```

**Notes:**

* This function is typically available only on signup/registration screens
* May automatically clear cookies after being called

***

#### window\.webview\.handleEmailAlreadyExistsError()

Handles the case when a user tries to sign up with an email that already exists. This modifies the error link behavior to communicate with the native app instead of navigating.

**Signature:**

```javascript
window.webview.handleEmailAlreadyExistsError()
```

**Parameters:** None

**Example:**

```javascript
// Call this when the signup form shows an "email already exists" error
if (window.webview.handleEmailAlreadyExistsError) {
  window.webview.handleEmailAlreadyExistsError();
}
```

**Notes:**

* This function modifies the DOM to intercept clicks on the existing account error link
* Sends an error message to the native app instead of navigating

#### window\.webview\.onProfileInfoSaved()

Called when user profile information has been successfully saved. Triggers navigation back and shows a success notification.

**Signature:**

```javascript
window.webview.onProfileInfoSaved()
```

**Parameters:** None

**Example:**

```javascript
// After successfully saving profile changes
if (window.webview.onProfileInfoSaved) {
  window.webview.onProfileInfoSaved();
}
```

***

#### window\.webview\.onEventPurchaseEnded()

Called when an event purchase flow is completed. Used in paywall screens.

**Signature:**

```javascript
window.webview.onEventPurchaseEnded()
```

**Parameters:** None

**Example:**

```javascript
// After event purchase completion
if (window.webview.onEventPurchaseEnded) {
  window.webview.onEventPurchaseEnded();
}
```

***

#### window\.webview\.paymentSuccessful()

Called when a payment is successfully processed. Used in paywall and billing screens.

**Signature:**

```javascript
window.webview.paymentSuccessful()
```

**Parameters:** None

**Example:**

```javascript
// After successful payment processing
if (window.webview.paymentSuccessful) {
  window.webview.paymentSuccessful();
}
```

***

### Theme and Appearance

#### window\.webview\.setAppearance()

Allows your custom HTML to switch between light and dark themes to match the native app's appearance.

**Signature:**

```javascript
window.webview.setAppearance(appearance)
```

**Parameters:**

| Parameter    | Type     | Required | Description              |
| ------------ | -------- | -------- | ------------------------ |
| `appearance` | `string` | Yes      | Either "light" or "dark" |

**Example:**

```javascript
// Set dark mode
if (window.webview.setAppearance) {
  window.webview.setAppearance("dark");
}

// Match system preference
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (window.webview.setAppearance) {
  window.webview.setAppearance(prefersDark ? "dark" : "light");
}
```

### Examples

#### Personalized Welcome Screen

```html
<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      padding: 20px;
      margin: 0;
    }
    .welcome {
      text-align: center;
      padding: 40px 20px;
    }
    .user-name {
      font-size: 24px;
      font-weight: bold;
      margin-bottom: 10px;
    }
    .badge {
      display: inline-block;
      padding: 4px 12px;
      border-radius: 12px;
      font-size: 12px;
      margin: 4px;
    }
    .badge-admin { background: #e3f2fd; color: #1976d2; }
    .badge-moderator { background: #f3e5f5; color: #7b1fa2; }
    .nav-button {
      display: block;
      width: 100%;
      padding: 16px;
      margin: 10px 0;
      border: none;
      border-radius: 8px;
      background: #6366f1;
      color: white;
      font-size: 16px;
      cursor: pointer;
    }
  </style>
</head>
<body>
  <div class="welcome">
    <div class="user-name" id="userName"></div>
    <div id="badges"></div>
  </div>

  <button class="nav-button" onclick="goToResources()">
    View Resources
  </button>

  <button class="nav-button" onclick="goToHelp()">
    Get Help
  </button>

  <script>
    // Personalize the welcome message
    document.getElementById('userName').textContent =
      'Welcome, ' + window.circleUser.name + '!';

    // Show role badges
    var badgesHtml = '';
    if (window.circleUser.isAdmin) {
      badgesHtml += '<span class="badge badge-admin">Admin</span>';
    }
    if (window.circleUser.isModerator) {
      badgesHtml += '<span class="badge badge-moderator">Moderator</span>';
    }
    document.getElementById('badges').innerHTML = badgesHtml;

    // Navigation functions
    function goToResources() {
      window.navigateToOrphanedScreen('resources-screen');
    }

    function goToHelp() {
      window.navigateToUrl('https://help.example.com');
    }
  </script>
</body>
</html>
```

#### Multi-Step Form Navigation

```html
<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
  <h1>Step 1: Basic Information</h1>

  <form id="step1Form">
    <input type="text" id="company" placeholder="Company Name" required>
    <button type="submit">Continue to Step 2</button>
  </form>

  <script>
    document.getElementById('step1Form').addEventListener('submit', function(e) {
      e.preventDefault();

      // Save data to localStorage for next step
      localStorage.setItem('company', document.getElementById('company').value);

      // Navigate to step 2
      window.navigateToOrphanedScreen('onboarding-step-2');
    });
  </script>
</body>
</html>
```

#### Conditional Content Based on User Role

```html
<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
  <div id="member-content">
    <h1>Member Dashboard</h1>
    <p>Welcome to your dashboard!</p>
  </div>

  <div id="admin-content" style="display: none;">
    <h2>Admin Tools</h2>
    <button onclick="window.navigateToOrphanedScreen('admin-settings')">
      Manage Settings
    </button>
    <button onclick="window.navigateToOrphanedScreen('user-management')">
      Manage Users
    </button>
  </div>

  <script>
    // Show admin tools only for admins
    if (window.circleUser.isAdmin) {
      document.getElementById('admin-content').style.display = 'block';
    }
  </script>
</body>
</html>
```

***

### Best Practices

#### 1. Always Check for API Availability

```javascript
// Wrap calls in availability checks for graceful degradation
if (typeof window.navigateToOrphanedScreen === 'function') {
  window.navigateToOrphanedScreen('9a581ef4-8a0e-432b-8a83-2885c9b4e83a');
} else {
  // Fallback for browser testing
  console.log('Would navigate to: my-screen');
}
```

#### 2. Use Viewport Meta Tag

Include the viewport meta tag for proper mobile rendering:

```html
<meta name="viewport" content="width=device-width, initial-scale=1.0">
```

#### 3. Handle Missing User Data Gracefully

```javascript
var userName = (window.circleUser && window.circleUser.name) || 'Guest';
```

#### 4. Test in Browser During Development

Create mock objects for browser testing but make sure to also test on your app, webviews can behave differently than a regular browser.

```javascript
// Add this at the top of your script for browser testing
if (typeof window.circleUser === 'undefined') {
  window.circleUser = {
    name: 'Test User',
    email: 'test@example.com',
    publicUid: 'test-123',
    isAdmin: true,
    isModerator: false
  };
}

if (typeof window.navigateToOrphanedScreen === 'undefined') {
  window.navigateToOrphanedScreen = function(screenId) {
    console.log('Navigate to screen:', screenId);
  };
}

if (typeof window.navigateToUrl === 'undefined') {
  window.navigateToUrl = function(url) {
    console.log('Navigate to URL:', url);
    window.location.href = url;
  };
}
```

#### 5. Use Mobile-Friendly Styles

```css
/* Ensure touch targets are large enough */
button, a {
  min-height: 44px;
  min-width: 44px;
}

/* Use system fonts for native feel */
body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}

/* Prevent text selection issues */
* {
  -webkit-user-select: none;
  user-select: none;
}

/* Allow text selection in input fields */
input, textarea {
  -webkit-user-select: auto;
  user-select: auto;
}
```

***

### API Summary

| API                                           | Type     | Description                |
| --------------------------------------------- | -------- | -------------------------- |
| `window.circleUser`                           | Object   | Current user information   |
| `window.circleUser.name`                      | string   | User's display name        |
| `window.circleUser.email`                     | string   | User's email               |
| `window.circleUser.publicUid`                 | string   | User's public ID           |
| `window.circleUser.isAdmin`                   | boolean  | Admin status               |
| `window.circleUser.isModerator`               | boolean  | Moderator status           |
| `window.navigateToOrphanedScreen(screenId)`   | Function | Navigate to custom screen  |
| `window.navigateToUrl(url)`                   | Function | Navigate to URL            |
| `window.ReactNativeWebView.postMessage(json)` | Function | Send message to native app |

***

### Support

If you encounter issues with the Custom HTML API, please contact the Circle support team or your community administrator.
