npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@emd-cloud/react-components

v1.14.5

Published

Use the EMD Cloud components with one package

Readme

EMD Cloud / React Components

Overview

The EMD Cloud React components provide a set of React components for interacting with the EMD Cloud platform. These components are designed to simplify working with EMD Cloud services in your React applications.

Getting Started

  1. Register on the EMD Cloud platform and create an application at https://console.cloud.emd.one.

  2. Obtain your application's API token.

  3. Install the required packages:

    NPM

    # Install the React components
    npm install @emd-cloud/react-components
        
    # Install the required peer dependencies (v1.11.0+ required for chat features)
    npm install @emd-cloud/sdk

    For TypeScript projects, types are included automatically. No additional @types packages needed.

  4. Wrap your application with the ApplicationProvider:

    import { ApplicationProvider } from '@emd-cloud/react-components';
        
    function App() {
      return (
        <ApplicationProvider 
          app="your-app-id" 
          apiUrl="https://api.emd.one" // optional, defaults to this value
          authToken="your-auth-token" // optional, can be set later
        >
          {/* Your app components */}
        </ApplicationProvider>
      );
    }

That's it! The EMD Cloud React components are now ready to use.

TypeScript Support

This library provides full TypeScript support with exported types from the EMD Cloud SDK. You can import and use these types in your TypeScript applications:

import { 
  UserData,
  AccountStatus,
  PingStatus, 
  SocialProvider,
  AppEnvironment,
  ForgotPassData,
  ForgotPassCheckCodeData,
  OAuthUrlResponse
} from '@emd-cloud/react-components';

// Use the types in your components
interface UserProfileProps {
  user: UserData;
  onStatusChange: (status: AccountStatus) => void;
}

const UserProfile: React.FC<UserProfileProps> = ({ user, onStatusChange }) => {
  return (
    <div>
      <h3>{user.firstName} {user.lastName}</h3>
      <p>Status: {user.accountStatus}</p>
      <p>Online: {user.pingStatus === PingStatus.Online ? 'Yes' : 'No'}</p>
    </div>
  );
};

Available Types:

Authentication Types:

  • UserData - Complete user information interface
  • AccountStatus - User account status enum (Pending, Approved, Rejected)
  • PingStatus - User online status enum (Online, Offline)
  • SocialProvider - OAuth provider enum (VK, YANDEX)
  • AppEnvironment - Application environment enum (Client, Server)
  • ForgotPassData - Password recovery request data
  • ForgotPassCheckCodeData - Password recovery code verification data
  • OAuthUrlResponse - OAuth URL response interface

Database Types:

  • Database - Database instance interface
  • DatabaseRowData - Generic database row data structure
  • DatabaseQuery - MongoDB-style query object for filtering
  • DatabaseSort - Sorting configuration for database queries
  • DatabaseListOptions - Options for listing/querying database rows
  • DatabaseGetRowOptions - Options for retrieving a single row
  • DatabaseCountOptions - Options for counting rows
  • DatabaseCreateOptions - Options for creating new rows
  • DatabaseUpdateOptions - Options for updating existing rows
  • DatabaseBulkUpdatePayload - Bulk update operation payload
  • DatabaseRowResponse - Single row response format
  • DatabaseRowsResponse - Multiple rows response format
  • DatabaseCountResponse - Row count response format
  • DatabaseBulkResponse - Bulk operation response format
  • DatabaseDeleteResponse - Delete operation response format
  • DatabaseTriggerResponse - Button trigger response format
  • DatabaseSaveMode - Save mode enum for database operations

Chat Types:

  • ChatChannelType - Chat channel type enum (Public, StaffToUser, PeerToPeer, Staff)
  • ChatReadPermission - Read permission levels for channels
  • ChatWritePermission - Write permission levels for channels
  • ChatChannel - Chat channel data structure
  • ChatMessage - Chat message structure
  • ChatAttachment - Message attachment structure (images, files)
  • ChatListOptions - Options for listing channels
  • ChatMessageListOptions - Options for listing messages
  • SendMessageOptions - Options for sending messages
  • GetUnreadCountOptions - Options for getting unread counts
  • UnreadCountResponse - Unread message count response
  • ConnectionState - WebSocket connection state enum (Connecting, Connected, Disconnected, Error)
  • WebSocketEvent - WebSocket event types enum
  • ChatWebSocketOptions - WebSocket configuration options
  • ChatWebSocketCallbacks - Event handler callbacks for real-time events

Common Types:

  • CallOptions - API call configuration options
  • AuthType - Authentication type enum (auth-token, api-token)

Components

ApplicationProvider

Description:

The ApplicationProvider is the foundational component that wraps your React application to enable EMD Cloud functionality. It automatically initializes and manages the EMD Cloud SDK instance, providing context to all child components and hooks. This provider handles SDK initialization, token management, and maintains the connection to EMD Cloud services.

Props:

  • app (string, required): Your EMD Cloud application ID obtained from the console
  • apiUrl (string, optional): EMD Cloud API endpoint URL. Defaults to "https://api.emd.one"
  • authToken (string, optional): Initial authentication token. Can be set later via hooks
  • tokenType (string, optional): Authentication token type for API requests. Defaults to "token"
  • children (ReactNode, required): Your application components

Key Features:

  • Automatic SDK Management: Initializes EMD Cloud SDK on mount and manages its lifecycle
  • Dynamic Loading: Gracefully handles cases where SDK is not installed
  • Token Management: Maintains authentication state across the application
  • Context Provision: Provides SDK instance and user data to all child components
  • Error Handling: Fails gracefully with helpful warnings when SDK is unavailable

Example:

import { ApplicationProvider } from '@emd-cloud/react-components';

function App() {
  return (
    <ApplicationProvider 
      app="your-app-id" 
      apiUrl="https://api.emd.one" // optional, defaults to this value
      authToken="your-auth-token" // optional, can be set later via useAuth
      tokenType="token" // optional, defaults to "token"
    >
      <YourAppComponents />
    </ApplicationProvider>
  );
}

// With environment variables
function App() {
  return (
    <ApplicationProvider 
      app={process.env.REACT_APP_EMD_CLOUD_APP_ID}
      authToken={localStorage.getItem('emd_auth_token')}
    >
      <YourAppComponents />
    </ApplicationProvider>
  );
}

Important Notes:

  • The ApplicationProvider must wrap any components that use EMD Cloud hooks
  • The SDK instance is available after the component mounts (handles async initialization)
  • All authentication and user management flows depend on this provider being properly configured
  • If the SDK peer dependency is not installed, the provider will log a warning and continue without SDK functionality

Hooks

Authorization Hooks:

Hook: useAuth

Description:

This hook manages user authentication processes through the EMD Cloud platform using the EMD Cloud SDK. It provides methods for login, registration, logout, social authentication, and password recovery. The hook automatically manages user state and integrates with the SDK for secure authentication.

Available Methods:

  • logInUser (function): Log in a user with email/password.
  • signUpUser (function): Register a new user.
  • authorization (function): Check authentication status using a token.
  • socialLogin (function): Initiate OAuth login with VK or Yandex.
  • exchangeOAuthToken (function): Complete OAuth flow by exchanging secret for token.
  • forgotPassword (function): Initiate password recovery process.
  • forgotPasswordCheckCode (function): Verify password reset code.
  • forgotPasswordChange (function): Complete password reset.
  • updateUser (function): Update current or specified user’s profile information.
  • logOutUser (function): Log out the current user.

Return Value: Returns an object with:

  • userInfo (object): Current user information.
  • authorization (function): For authentication verification.
  • logInUser (function): For email/password login.
  • signUpUser (function): For user registration.
  • socialLogin (function): For OAuth authentication.
  • exchangeOAuthToken (function): For completing OAuth flow.
  • forgotPassword (function): For initiating password recovery.
  • forgotPasswordCheckCode (function): For verifying reset codes.
  • forgotPasswordChange (function): For completing password reset.
  • updateUser (function): Update current or specified user’s profile information.
  • logOutUser (function): For logout.

Basic Authentication Example:

import { useAuth } from '@emd-cloud/react-components';

const MyAuthComponent = () => {
  const { logInUser, signUpUser, logOutUser, userInfo } = useAuth();

  const handleLogin = async (login, password) => {
    try {
      const user = await logInUser({ login, password });
      console.log('Logged in user:', user);
    } catch (error) {
      console.error('Login failed:', error);
    }
  };

  const handleSignUp = async (login, password) => {
    try {
      const newUser = await signUpUser({ 
        login, 
        password,
        firstName: 'John',
        lastName: 'Doe',
        captchaToken: 'your-captcha-token' // optional
      });
      console.log('Registered user:', newUser);
    } catch (error) {
      console.error('Registration failed:', error);
    }
  };

  const handleLogout = () => {
    logOutUser();
    console.log('User logged out.');
  };

  return (
    <div>
      <h3>Current User: {userInfo ? userInfo.login : 'Not logged in'}</h3>
      {/* Implement your login and signup forms here */}
      <button onClick={() => handleLogin('[email protected]', 'password123')}>Login</button>
      <button onClick={() => handleSignUp('[email protected]', 'password123')}>Sign Up</button>
      <button onClick={handleLogout}>Logout</button>
    </div>
  );
};

Social Login Example:

import { useAuth } from '@emd-cloud/react-components';

const SocialLoginComponent = () => {
  const { socialLogin, exchangeOAuthToken } = useAuth();

  const handleVKLogin = async () => {
    try {
      const response = await socialLogin({
        provider: 'vk',
        redirectUrl: 'https://myapp.com/auth/callback'
      });
      
      // Redirect user to VK for authentication
      window.location.href = response.url;
    } catch (error) {
      console.error('Social login failed:', error);
    }
  };

  const handleYandexLogin = async () => {
    try {
      const response = await socialLogin({
        provider: 'yandex',
        redirectUrl: 'https://myapp.com/auth/callback'
      });
      
      // Redirect user to Yandex for authentication
      window.location.href = response.url;
    } catch (error) {
      console.error('Social login failed:', error);
    }
  };

  // Handle OAuth callback (call this on your callback page)
  const handleOAuthCallback = async () => {
    const urlParams = new URLSearchParams(window.location.search);
    const secret = urlParams.get('secret');
    
    if (secret) {
      try {
        const userData = await exchangeOAuthToken(secret);
        console.log('User authenticated:', userData);
        // Redirect to dashboard or main page
      } catch (error) {
        console.error('OAuth token exchange failed:', error);
      }
    }
  };

  return (
    <div>
      <button onClick={handleVKLogin}>Login with VK</button>
      <button onClick={handleYandexLogin}>Login with Yandex</button>
    </div>
  );
};

Password Recovery Example:

import { useAuth } from '@emd-cloud/react-components';

const PasswordRecoveryComponent = () => {
  const { forgotPassword, forgotPasswordCheckCode, forgotPasswordChange } = useAuth();

  const handleForgotPassword = async (email) => {
    try {
      const response = await forgotPassword({ email });
      console.log('Password reset requested:', response.requestId);
      // Save requestId for next steps
    } catch (error) {
      console.error('Password reset request failed:', error);
    }
  };

  const handleCheckCode = async (requestId, code) => {
    try {
      const response = await forgotPasswordCheckCode({ requestId, code });
      console.log('Code verified:', response);
    } catch (error) {
      console.error('Code verification failed:', error);
    }
  };

  const handleChangePassword = async (requestId, newPassword, newPasswordRepeat) => {
    try {
      const userData = await forgotPasswordChange({ 
        requestId, 
        newPassword, 
        newPasswordRepeat 
      });
      console.log('Password changed successfully:', userData);
    } catch (error) {
      console.error('Password change failed:', error);
    }
  };

  // Implementation details for your forms...
};

Database Hooks:

Hook: useDatabase

Description:

The useDatabase hook provides comprehensive database operations for EMD Cloud collections using the EMD Cloud SDK. It offers type-safe CRUD operations, advanced querying with MongoDB-style syntax, sorting, pagination, and bulk operations. This hook automatically manages authentication and provides error handling for all database interactions.

Available Methods:

  • getRows<T> (function): Retrieve multiple rows with filtering, sorting, and pagination
  • countRows (function): Count rows matching specific criteria
  • getRow<T> (function): Retrieve a single row by ID
  • createRow<T> (function): Create a new row in the collection
  • updateRow<T> (function): Update an existing row by ID
  • bulkUpdate (function): Perform bulk updates on multiple rows
  • deleteRow (function): Delete a single row by ID
  • deleteRows (function): Delete multiple rows matching criteria
  • triggerButton (function): Execute button actions on database rows

Key Features:

  • Type Safety: Full TypeScript support with generics for data types
  • Advanced Querying: MongoDB-style query syntax with $and, $or, $eq, $ne, etc.
  • Sorting & Pagination: Flexible sorting and pagination options
  • Bulk Operations: Efficient bulk updates and deletions
  • Authentication: Support for both user (auth-token) and server (api-token) authentication modes
  • Error Handling: Comprehensive error handling with meaningful error messages

Basic CRUD Example:

import { useDatabase } from '@emd-cloud/react-components';

// Define your data type for type safety
interface User {
  _id?: string;
  name: string;
  email: string;
  status: 'active' | 'inactive';
  createdAt?: string;
}

const UserManagement = () => {
  const { getRows, createRow, updateRow, deleteRow } = useDatabase();

  // Get all active users
  const loadActiveUsers = async () => {
    try {
      const response = await getRows<User>('users', {
        query: {
          "$and": [
            { "data.status": { "$eq": "active" } }
          ]
        },
        limit: 50,
        sort: [{ column: "createdAt", sort: "desc" }]
      });
      
      console.log('Active users:', response.data);
      console.log('Total count:', response.count);
    } catch (error) {
      console.error('Failed to load users:', error);
    }
  };

  // Create a new user
  const createUser = async (userData: Omit<User, '_id' | 'createdAt'>) => {
    try {
      const response = await createRow<User>('users', userData);
      console.log('Created user:', response.data);
      return response.data;
    } catch (error) {
      console.error('Failed to create user:', error);
      throw error;
    }
  };

  // Update user status
  const updateUserStatus = async (userId: string, status: User['status']) => {
    try {
      const response = await updateRow<User>('users', userId, { status });
      console.log('Updated user:', response.data);
      return response.data;
    } catch (error) {
      console.error('Failed to update user:', error);
      throw error;
    }
  };

  // Delete a user
  const removeUser = async (userId: string) => {
    try {
      await deleteRow('users', userId);
      console.log('User deleted successfully');
    } catch (error) {
      console.error('Failed to delete user:', error);
      throw error;
    }
  };

  return (
    <div>
      <button onClick={loadActiveUsers}>Load Active Users</button>
      <button onClick={() => createUser({ 
        name: 'John Doe', 
        email: '[email protected]', 
        status: 'active' 
      })}>
        Create User
      </button>
    </div>
  );
};

Advanced Querying Example:

import { useDatabase, DatabaseQuery } from '@emd-cloud/react-components';

const AdvancedUserSearch = () => {
  const { getRows, countRows } = useDatabase();

  // Complex query with multiple conditions
  const searchUsers = async () => {
    const query: DatabaseQuery = {
      "$and": [
        {
          "$or": [
            { "data.name": { "$regex": "John", "$options": "i" } },
            { "data.email": { "$regex": "@gmail.com" } }
          ]
        },
        { "data.status": { "$eq": "active" } },
        { "data.createdAt": { "$gte": "2024-01-01" } }
      ]
    };

    try {
      // Get matching users with pagination
      const usersResponse = await getRows('users', {
        query,
        limit: 20,
        offset: 0,
        sort: [
          { column: "name", sort: "asc" },
          { column: "createdAt", sort: "desc" }
        ]
      });

      // Count total matching users
      const countResponse = await countRows('users', { query });

      console.log('Found users:', usersResponse.data);
      console.log('Total matching users:', countResponse.count);
    } catch (error) {
      console.error('Search failed:', error);
    }
  };

  // Search by date range
  const getUsersByDateRange = async (startDate: string, endDate: string) => {
    try {
      const response = await getRows('users', {
        query: {
          "data.createdAt": {
            "$gte": startDate,
            "$lte": endDate
          }
        },
        sort: [{ column: "createdAt", sort: "desc" }]
      });

      return response.data;
    } catch (error) {
      console.error('Date range search failed:', error);
      return [];
    }
  };

  return (
    <div>
      <button onClick={searchUsers}>Advanced Search</button>
      <button onClick={() => getUsersByDateRange('2024-01-01', '2024-12-31')}>
        Users This Year
      </button>
    </div>
  );
};

Bulk Operations Example:

import { useDatabase } from '@emd-cloud/react-components';

const BulkUserOperations = () => {
  const { bulkUpdate, deleteRows, getRows } = useDatabase();

  // Bulk update user statuses
  const activateAllPendingUsers = async () => {
    try {
      const response = await bulkUpdate('users', {
        query: {
          "data.status": { "$eq": "pending" }
        },
        update: {
          "$set": { "data.status": "active" }
        }
      });

      console.log('Bulk update result:', response);
      console.log(`Updated ${response.modifiedCount} users`);
    } catch (error) {
      console.error('Bulk update failed:', error);
    }
  };

  // Delete inactive users older than 6 months
  const cleanupInactiveUsers = async () => {
    const sixMonthsAgo = new Date();
    sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);

    try {
      const response = await deleteRows('users', {
        query: {
          "$and": [
            { "data.status": { "$eq": "inactive" } },
            { "data.lastLoginAt": { "$lt": sixMonthsAgo.toISOString() } }
          ]
        }
      });

      console.log(`Deleted ${response.deletedCount} inactive users`);
    } catch (error) {
      console.error('Cleanup failed:', error);
    }
  };

  // Get users with specific IDs
  const getUsersByIds = async (userIds: string[]) => {
    try {
      const response = await getRows('users', {
        query: {
          "_id": { "$in": userIds }
        }
      });

      return response.data;
    } catch (error) {
      console.error('Failed to get users by IDs:', error);
      return [];
    }
  };

  return (
    <div>
      <button onClick={activateAllPendingUsers}>Activate Pending Users</button>
      <button onClick={cleanupInactiveUsers}>Cleanup Inactive Users</button>
    </div>
  );
};

Authentication Modes Example:

import { useDatabase } from '@emd-cloud/react-components';

const ServerOperations = () => {
  const { getRows, createRow } = useDatabase();

  // Use server authentication for admin operations
  const getAdminData = async () => {
    try {
      const response = await getRows('admin_logs', {
        query: { "data.level": { "$eq": "error" } },
        limit: 100
      }, {
        authType: 'api-token' // Use server authentication
      });

      console.log('Admin logs:', response.data);
    } catch (error) {
      console.error('Admin operation failed:', error);
    }
  };

  // User-level operation (default auth-token)
  const getUserProfile = async (userId: string) => {
    try {
      const response = await getRows('user_profiles', {
        query: { "_id": { "$eq": userId } }
      }); // Uses default auth-token authentication

      return response.data[0];
    } catch (error) {
      console.error('Profile fetch failed:', error);
      return null;
    }
  };

  return (
    <div>
      <button onClick={getAdminData}>Get Admin Data</button>
      <button onClick={() => getUserProfile('user-123')}>Get User Profile</button>
    </div>
  );
};

Button Trigger Example:

import { useDatabase } from '@emd-cloud/react-components';

const WorkflowActions = () => {
  const { triggerButton } = useDatabase();

  // Trigger a workflow button on a specific row
  const approveApplication = async (applicationId: string) => {
    try {
      const response = await triggerButton(
        'applications',     // Collection name
        applicationId,      // Row ID
        'approve_button'    // Button identifier
      );

      console.log('Application approved:', response);
    } catch (error) {
      console.error('Approval failed:', error);
    }
  };

  // Trigger with custom authentication
  const triggerAdminAction = async (recordId: string) => {
    try {
      const response = await triggerButton(
        'admin_records',
        recordId,
        'admin_action_button',
        { authType: 'api-token' }
      );

      console.log('Admin action completed:', response);
    } catch (error) {
      console.error('Admin action failed:', error);
    }
  };

  return (
    <div>
      <button onClick={() => approveApplication('app-123')}>
        Approve Application
      </button>
      <button onClick={() => triggerAdminAction('record-456')}>
        Trigger Admin Action
      </button>
    </div>
  );
};

Webhook Hooks:

Hook: useWebhook

Description:

The useWebhook hook provides easy integration with EMD Cloud webhook endpoints using the EMD Cloud SDK. It offers convenient methods for calling webhooks with different HTTP methods and payload formats, supporting both user and server authentication modes. This hook simplifies webhook integration for notifications, external process triggers, data fetching, and third-party service integrations.

Available Methods:

  • callWebhook (function): Make webhook calls with full control over HTTP method, headers, and body

Key Features:

  • Custom Requests: Full control over HTTP method, headers, and request body
  • Simple JSON Sending: Easy POST requests with JSON payloads
  • GET Requests: Fetch data from webhook endpoints
  • Authentication: Support for both auth-token (user) and api-token (server) authentication modes
  • Error Handling: Comprehensive error handling and response validation
  • Type Safety: Full TypeScript support for webhook responses

Basic Usage Example:

import { useWebhook } from '@emd-cloud/react-components';

const NotificationComponent = () => {
  const { callWebhook } = useWebhook();

  // Send a simple notification
  const sendUserLoginNotification = async (userId: string) => {
    try {
      const result = await callWebhook('user-login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          userId,
          timestamp: new Date().toISOString(),
          source: 'web-app',
          userAgent: navigator.userAgent
        })
      });

      console.log('Notification sent:', result);
    } catch (error) {
      console.error('Failed to send notification:', error);
    }
  };

  // Get webhook status or configuration
  const checkWebhookStatus = async () => {
    try {
      const status = await callWebhook('health-check', { method: 'GET' });
      console.log('Webhook status:', status);
      return status;
    } catch (error) {
      console.error('Failed to check webhook status:', error);
      return null;
    }
  };

  // Custom webhook call with specific requirements
  const triggerProcessing = async (data: any) => {
    try {
      const result = await callWebhook('data-processor', {
        method: 'PUT',
        headers: {
          'Content-Type': 'application/json',
          'X-Processing-Priority': 'high',
          'X-Request-ID': crypto.randomUUID()
        },
        body: JSON.stringify({
          action: 'process',
          data,
          timestamp: Date.now()
        })
      });

      console.log('Processing triggered:', result);
      return result;
    } catch (error) {
      console.error('Failed to trigger processing:', error);
      throw error;
    }
  };

  return (
    <div>
      <button onClick={() => sendUserLoginNotification('user-123')}>
        Send Login Notification
      </button>
      <button onClick={checkWebhookStatus}>
        Check Webhook Status
      </button>
      <button onClick={() => triggerProcessing({ type: 'user-data', id: 'user-123' })}>
        Trigger Data Processing
      </button>
    </div>
  );
};

Event Notifications Example:

import { useWebhook } from '@emd-cloud/react-components';

const EventSystem = () => {
  const { callWebhook } = useWebhook();

  // User activity tracking
  const trackUserActivity = async (activity: {
    userId: string;
    action: string;
    page: string;
    data?: any;
  }) => {
    try {
      await callWebhook('user-activity', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          ...activity,
          timestamp: new Date().toISOString(),
          sessionId: sessionStorage.getItem('sessionId'),
          userAgent: navigator.userAgent
        })
      });
    } catch (error) {
      console.error('Failed to track activity:', error);
    }
  };

  // Order processing notifications
  const notifyOrderEvent = async (
    event: 'created' | 'updated' | 'completed' | 'cancelled',
    orderData: any
  ) => {
    try {
      await callWebhook(`order-${event}`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          event,
          order: orderData,
          timestamp: new Date().toISOString(),
          source: 'ecommerce-app'
        })
      });

      console.log(`Order ${event} notification sent`);
    } catch (error) {
      console.error(`Failed to send order ${event} notification:`, error);
    }
  };

  // Error reporting
  const reportError = async (error: Error, context: any) => {
    try {
      await callWebhook('error-report', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          error: {
            message: error.message,
            stack: error.stack,
            name: error.name
          },
          context,
          timestamp: new Date().toISOString(),
          url: window.location.href,
          userAgent: navigator.userAgent
        })
      });
    } catch (webhookError) {
      console.error('Failed to report error via webhook:', webhookError);
    }
  };

  return (
    <div>
      <button onClick={() => trackUserActivity({
        userId: 'user-123',
        action: 'page_view',
        page: '/dashboard'
      })}>
        Track Page View
      </button>
      <button onClick={() => notifyOrderEvent('created', { 
        id: 'order-456', 
        total: 99.99,
        items: ['item1', 'item2']
      })}>
        Notify Order Created
      </button>
    </div>
  );
};

Third-Party Integration Example:

import { useWebhook } from '@emd-cloud/react-components';

const ThirdPartyIntegrations = () => {
  const { callWebhook } = useWebhook();

  // Slack notification integration
  const sendSlackNotification = async (message: string, channel: string) => {
    try {
      const result = await callWebhook('slack-notification', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          channel,
          text: message,
          username: 'EMD Cloud Bot',
          timestamp: new Date().toISOString()
        })
      });

      console.log('Slack notification sent:', result);
    } catch (error) {
      console.error('Failed to send Slack notification:', error);
    }
  };

  // Email service integration
  const sendEmail = async (to: string, subject: string, body: string) => {
    try {
      const result = await callWebhook('email-service', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-Email-Priority': 'normal'
        },
        body: JSON.stringify({
          to,
          subject,
          body,
          from: '[email protected]',
          timestamp: new Date().toISOString()
        })
      });

      console.log('Email sent:', result);
      return result;
    } catch (error) {
      console.error('Failed to send email:', error);
      throw error;
    }
  };

  // External API data sync
  const syncWithCRM = async (customerData: any) => {
    try {
      const result = await callWebhook('crm-sync', {
        method: 'PATCH',
        headers: {
          'Content-Type': 'application/json',
          'X-Sync-Source': 'web-app'
        },
        body: JSON.stringify({
          action: 'update_customer',
          data: customerData,
          syncTimestamp: new Date().toISOString()
        })
      });

      console.log('CRM sync completed:', result);
      return result;
    } catch (error) {
      console.error('CRM sync failed:', error);
      throw error;
    }
  };

  // Get integration status
  const checkIntegrationHealth = async () => {
    try {
      const [slackStatus, emailStatus, crmStatus] = await Promise.all([
        call('slack-health', { method: 'GET' }),
        call('email-health', { method: 'GET' }),
        call('crm-health', { method: 'GET' })
      ]);

      return {
        slack: slackStatus,
        email: emailStatus,
        crm: crmStatus
      };
    } catch (error) {
      console.error('Failed to check integration health:', error);
      return null;
    }
  };

  return (
    <div>
      <button onClick={() => sendSlackNotification('Hello from EMD Cloud!', '#general')}>
        Send Slack Message
      </button>
      <button onClick={() => sendEmail(
        '[email protected]',
        'Welcome to Our Service',
        'Thank you for signing up!'
      )}>
        Send Welcome Email
      </button>
      <button onClick={() => syncWithCRM({
        id: 'customer-123',
        name: 'John Doe',
        email: '[email protected]'
      })}>
        Sync with CRM
      </button>
      <button onClick={checkIntegrationHealth}>
        Check Integration Health
      </button>
    </div>
  );
};

Server Authentication Example:

import { useWebhook } from '@emd-cloud/react-components';

const AdminWebhookOperations = () => {
  const { callWebhook } = useWebhook();

  // Server-level operations requiring API token
  const triggerSystemMaintenance = async () => {
    try {
      const result = await callWebhook('system-maintenance', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-Maintenance-Type': 'scheduled'
        },
        body: JSON.stringify({
          action: 'start_maintenance',
          duration: '30m',
          timestamp: new Date().toISOString()
        })
      }, {
        authType: 'api-token' // Use server authentication
      });

      console.log('Maintenance triggered:', result);
    } catch (error) {
      console.error('Failed to trigger maintenance:', error);
    }
  };

  // Send admin notifications
  const sendAdminAlert = async (alertType: string, message: string) => {
    try {
      await callWebhook('admin-alerts', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          type: alertType,
          message,
          severity: 'high',
          timestamp: new Date().toISOString(),
          source: 'admin-panel'
        })
      }, {
        authType: 'api-token'
      });

      console.log('Admin alert sent');
    } catch (error) {
      console.error('Failed to send admin alert:', error);
    }
  };

  // User-level webhook (default auth-token)
  const sendUserFeedback = async (feedback: string) => {
    try {
      await callWebhook('user-feedback', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          feedback,
          timestamp: new Date().toISOString(),
          page: window.location.pathname
        })
      }); // Uses default auth-token

      console.log('Feedback submitted');
    } catch (error) {
      console.error('Failed to submit feedback:', error);
    }
  };

  return (
    <div>
      <button onClick={triggerSystemMaintenance}>
        Trigger System Maintenance
      </button>
      <button onClick={() => sendAdminAlert('system_overload', 'CPU usage is above 90%')}>
        Send Admin Alert
      </button>
      <button onClick={() => sendUserFeedback('Great app!')}>
        Send User Feedback
      </button>
    </div>
  );
};

Error Handling and Response Validation:

import { useWebhook } from '@emd-cloud/react-components';

const RobustWebhookComponent = () => {
  const { callWebhook } = useWebhook();

  // Webhook with retry logic
  const sendWithRetry = async (webhookId: string, data: any, maxRetries = 3) => {
    let lastError: Error;

    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        const result = await callWebhook(webhookId, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            ...data,
            attempt,
            timestamp: new Date().toISOString()
          })
        });

        console.log(`Webhook succeeded on attempt ${attempt}:`, result);
        return result;
      } catch (error) {
        lastError = error as Error;
        console.warn(`Webhook attempt ${attempt} failed:`, error);

        if (attempt < maxRetries) {
          // Wait before retry (exponential backoff)
          await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
        }
      }
    }

    throw new Error(`Webhook failed after ${maxRetries} attempts: ${lastError.message}`);
  };

  // Validate webhook response
  const callWebhookWithValidation = async (webhookId: string, options: RequestInit) => {
    try {
      const result = await callWebhook(webhookId, options);

      // Custom validation logic
      if (result && typeof result === 'object' && 'status' in result) {
        if (result.status === 'error') {
          throw new Error(`Webhook returned error: ${result.message || 'Unknown error'}`);
        }
      }

      return result;
    } catch (error) {
      console.error('Webhook validation failed:', error);
      
      // Log error details for debugging
      console.error('Webhook details:', {
        webhookId,
        method: options.method || 'GET',
        headers: options.headers,
        timestamp: new Date().toISOString()
      });

      throw error;
    }
  };

  // Bulk webhook operations with error collection
  const sendBulkNotifications = async (notifications: Array<{id: string, data: any}>) => {
    const results = [];
    const errors = [];

    for (const notification of notifications) {
      try {
        const result = await callWebhook(notification.id, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(notification.data)
        });
        results.push({ id: notification.id, success: true, result });
      } catch (error) {
        errors.push({ id: notification.id, error: error.message });
      }
    }

    console.log(`Bulk notifications: ${results.length} succeeded, ${errors.length} failed`);
    
    if (errors.length > 0) {
      console.warn('Failed notifications:', errors);
    }

    return { results, errors };
  };

  return (
    <div>
      <button onClick={() => sendWithRetry('unreliable-webhook', { test: 'data' })}>
        Send with Retry Logic
      </button>
      <button onClick={() => callWebhookWithValidation('validation-webhook', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ validate: true })
      })}>
        Call with Validation
      </button>
      <button onClick={() => sendBulkNotifications([
        { id: 'notification-1', data: { message: 'Hello 1' } },
        { id: 'notification-2', data: { message: 'Hello 2' } },
        { id: 'notification-3', data: { message: 'Hello 3' } }
      ])}>
        Send Bulk Notifications
      </button>
    </div>
  );
};

Chat Hooks:

Hook: useChat

Description:

The useChat hook provides comprehensive REST API operations for managing chat channels and messages in EMD Cloud using the EMD Cloud SDK. It offers complete chat functionality including channel creation, message sending, unread tracking, and support for multiple channel types (public, staff-to-user, peer-to-peer, and staff channels). This hook automatically manages authentication and provides error handling for all chat operations.

Available Methods:

  • listChannels (function): List chat channels with filtering and pagination
  • createChannelByType (function): Create or get existing channel by type
  • upsertChannel (function): Create or update a chat channel
  • getChannel (function): Get channel details
  • deleteChannel (function): Delete a chat channel
  • sendMessage (function): Send a message with optional attachments
  • listMessages (function): List messages in a channel with pagination
  • deleteMessage (function): Delete a message
  • getUnreadCount (function): Get unread message counts for a channel

Key Features:

  • Channel Management: Create, list, update, and delete chat channels
  • Multiple Channel Types: Support for public, staff-to-user, peer-to-peer, and staff channels
  • Message Operations: Send, list, and delete messages with attachment support
  • Unread Tracking: Track and clear unread message counts
  • Search & Pagination: Search channels/messages with flexible pagination
  • Attachment Support: Send images, files, and other attachments with messages
  • Type Safety: Full TypeScript support with SDK types
  • Error Handling: Comprehensive error handling with meaningful messages

Basic Channel Management Example:

import { useChat, ChatChannelType } from '@emd-cloud/react-components';
import { useState, useEffect } from 'react';

const ChannelManager = () => {
  const [channels, setChannels] = useState([]);
  const { listChannels, createChannelByType, deleteChannel } = useChat();

  // Load public channels
  const loadPublicChannels = async () => {
    try {
      const response = await listChannels({
        type: ChatChannelType.Public,
        limit: 20,
        sort: 'DESC',
        orderBy: 'lastMessageAt'
      });

      setChannels(response.data);
      console.log(`Found ${response.count} channels`);
    } catch (error) {
      console.error('Failed to load channels:', error);
    }
  };

  // Create a support channel
  const createSupportChannel = async (userId: string) => {
    try {
      const channel = await createChannelByType(ChatChannelType.StaffToUser, {
        userId
      });

      console.log('Support channel created:', channel.id);
      return channel;
    } catch (error) {
      console.error('Failed to create support channel:', error);
    }
  };

  // Create a direct message channel
  const createDirectMessage = async (userIds: string[]) => {
    try {
      const channel = await createChannelByType(ChatChannelType.PeerToPeer, {
        accesses: userIds
      });

      console.log('DM channel created:', channel.id);
      return channel;
    } catch (error) {
      console.error('Failed to create DM channel:', error);
    }
  };

  // Delete a channel
  const removeChannel = async (channelId: string) => {
    try {
      await deleteChannel(channelId);
      console.log('Channel deleted');
      loadPublicChannels(); // Refresh list
    } catch (error) {
      console.error('Failed to delete channel:', error);
    }
  };

  useEffect(() => {
    loadPublicChannels();
  }, []);

  return (
    <div>
      <h3>Chat Channels</h3>
      <button onClick={loadPublicChannels}>Refresh Channels</button>
      <button onClick={() => createSupportChannel('user-123')}>
        Create Support Channel
      </button>
      <button onClick={() => createDirectMessage(['user-1', 'user-2'])}>
        Create DM
      </button>

      <ul>
        {channels.map((channel) => (
          <li key={channel._id}>
            <span>{channel.id}</span>
            {channel.unreadCountRecipient > 0 && (
              <span className="badge">{channel.unreadCountRecipient}</span>
            )}
            <button onClick={() => removeChannel(channel._id)}>Delete</button>
          </li>
        ))}
      </ul>
    </div>
  );
};

Message Operations Example:

import { useChat } from '@emd-cloud/react-components';
import { useState, useEffect } from 'react';

const ChatConversation = ({ channelId }: { channelId: string }) => {
  const [messages, setMessages] = useState([]);
  const [messageText, setMessageText] = useState('');
  const { sendMessage, listMessages, deleteMessage, getUnreadCount } = useChat();

  // Load messages
  const loadMessages = async () => {
    try {
      const response = await listMessages(channelId, {
        limit: 50,
        page: 0,
        orderBy: 'createdAt',
        sort: 'DESC'
      });

      setMessages(response.data.reverse()); // Show oldest first
    } catch (error) {
      console.error('Failed to load messages:', error);
    }
  };

  // Send a text message
  const handleSendMessage = async () => {
    if (!messageText.trim()) return;

    try {
      const message = await sendMessage(channelId, {
        message: messageText
      });

      setMessages(prev => [...prev, message]);
      setMessageText('');
    } catch (error) {
      console.error('Failed to send message:', error);
    }
  };

  // Send message with attachments
  const sendMessageWithFiles = async (fileIds: string[]) => {
    try {
      const message = await sendMessage(channelId, {
        message: 'Sent some files',
        attaches: fileIds.map(id => ({
          type: 'file',
          attach: id,
          name: `file-${id}`
        }))
      });

      setMessages(prev => [...prev, message]);
    } catch (error) {
      console.error('Failed to send message with files:', error);
    }
  };

  // Delete a message
  const handleDeleteMessage = async (messageId: string) => {
    try {
      await deleteMessage(channelId, messageId);
      setMessages(prev => prev.filter(m => m._id !== messageId));
    } catch (error) {
      console.error('Failed to delete message:', error);
    }
  };

  // Check unread count and mark as read
  const markAsRead = async () => {
    try {
      const counts = await getUnreadCount(channelId, {
        cleanupUnreaded: true // Mark as read
      });

      console.log('Unread cleared:', counts);
    } catch (error) {
      console.error('Failed to mark as read:', error);
    }
  };

  useEffect(() => {
    loadMessages();
    markAsRead();
  }, [channelId]);

  return (
    <div>
      <h3>Conversation</h3>

      <div className="messages">
        {messages.map((message) => (
          <div key={message._id} className="message">
            <p>{message.message}</p>
            {message.attaches?.map((attach) => (
              <div key={attach._id}>
                <a href={`/files/${attach.attach}`}>{attach.name}</a>
              </div>
            ))}
            <button onClick={() => handleDeleteMessage(message._id)}>
              Delete
            </button>
          </div>
        ))}
      </div>

      <div className="input">
        <input
          value={messageText}
          onChange={(e) => setMessageText(e.target.value)}
          onKeyPress={(e) => e.key === 'Enter' && handleSendMessage()}
          placeholder="Type a message..."
        />
        <button onClick={handleSendMessage}>Send</button>
      </div>
    </div>
  );
};

Search and Filtering Example:

import { useChat, ChatChannelType } from '@emd-cloud/react-components';
import { useState } from 'react';

const ChatSearch = () => {
  const [searchQuery, setSearchQuery] = useState('');
  const [results, setResults] = useState([]);
  const { listChannels, listMessages } = useChat();

  // Search channels
  const searchChannels = async () => {
    try {
      const response = await listChannels({
        type: ChatChannelType.Public,
        search: searchQuery,
        limit: 20
      });

      setResults(response.data);
      console.log(`Found ${response.count} matching channels`);
    } catch (error) {
      console.error('Search failed:', error);
    }
  };

  // Search messages in a channel
  const searchMessages = async (channelId: string) => {
    try {
      const response = await listMessages(channelId, {
        search: searchQuery,
        limit: 100
      });

      return response.data;
    } catch (error) {
      console.error('Message search failed:', error);
      return [];
    }
  };

  // Get unread chats only
  const getUnreadChats = async () => {
    try {
      const response = await listChannels({
        type: ChatChannelType.StaffToUser,
        unreadedChats: true,
        limit: 50
      });

      setResults(response.data);
      console.log(`${response.count} unread chats`);
    } catch (error) {
      console.error('Failed to get unread chats:', error);
    }
  };

  return (
    <div>
      <input
        value={searchQuery}
        onChange={(e) => setSearchQuery(e.target.value)}
        placeholder="Search channels..."
      />
      <button onClick={searchChannels}>Search Channels</button>
      <button onClick={getUnreadChats}>Show Unread</button>

      <ul>
        {results.map((channel) => (
          <li key={channel._id}>
            {channel.id}
            {channel.unreadCountRecipient > 0 && (
              <span> ({channel.unreadCountRecipient} unread)</span>
            )}
          </li>
        ))}
      </ul>
    </div>
  );
};

Hook: useChatWebSocket

Description:

The useChatWebSocket hook provides real-time chat messaging functionality via WebSocket connection using the EMD Cloud SDK. It implements the Pusher WebSocket protocol for live message updates, connection management, channel subscriptions, and event handling. The hook offers automatic reconnection, connection state tracking, and React-friendly lifecycle management with automatic cleanup.

Available Methods & Properties:

  • connect() (function): Connect to the WebSocket server
  • disconnect() (function): Disconnect from the WebSocket server
  • subscribeToChannel(channelId, chatId?) (function): Subscribe to a chat channel
  • unsubscribeFromChannel(channelId) (function): Unsubscribe from a channel
  • subscribeToSupport() (function): Subscribe to support channel (staff only)
  • setCallbacks(callbacks) (function): Set or update event callbacks
  • getConnectionState() (function): Get current connection state
  • getSubscribedChannels() (function): Get list of subscribed channels
  • connectionState (property): Reactive connection state
  • isConnected (property): Boolean indicating if connected

Key Features:

  • Real-time Messaging: Receive messages instantly via WebSocket
  • Auto-reconnect: Automatic reconnection with exponential backoff
  • Connection Management: Connect, disconnect, and track connection state
  • Channel Subscriptions: Subscribe/unsubscribe to multiple channels
  • Event Callbacks: Handle message received, deleted, and connection state changes
  • Auto-cleanup: Automatic disconnect on component unmount
  • Type Safety: Full TypeScript support for all events
  • Support Integration: Subscribe to support channel for staff notifications

Basic WebSocket Connection Example:

import { useChatWebSocket, ConnectionState } from '@emd-cloud/react-components';
import { useEffect, useState } from 'react';

const ChatComponent = () => {
  const [messages, setMessages] = useState([]);

  const {
    connect,
    disconnect,
    subscribeToChannel,
    connectionState,
    isConnected
  } = useChatWebSocket({
    autoConnect: true, // Connect automatically on mount
    autoReconnect: true,
    maxReconnectAttempts: 10,
    callbacks: {
      onMessageReceived: (message) => {
        console.log('New message:', message);
        setMessages(prev => [...prev, message]);
      },
      onMessageDeleted: (data) => {
        console.log('Message deleted:', data._id);
        setMessages(prev => prev.filter(m => m._id !== data._id));
      },
      onConnectionStateChange: (state) => {
        console.log('Connection state:', state);
      },
      onError: (error) => {
        console.error('WebSocket error:', error);
      }
    }
  });

  // Subscribe to a channel when connected
  useEffect(() => {
    if (isConnected) {
      subscribeToChannel('public-general')
        .then(() => console.log('Subscribed to channel'))
        .catch((error) => console.error('Subscription failed:', error));
    }
  }, [isConnected]);

  return (
    <div>
      <div className="status">
        <p>Status: {connectionState}</p>
        {!isConnected && <button onClick={connect}>Connect</button>}
        {isConnected && <button onClick={disconnect}>Disconnect</button>}
      </div>

      <div className="messages">
        {messages.map((msg) => (
          <div key={msg._id}>
            <strong>{msg.user}:</strong> {msg.message}
          </div>
        ))}
      </div>
    </div>
  );
};

Advanced Channel Management Example:

import { useChatWebSocket, useChat } from '@emd-cloud/react-components';
import { useEffect, useState } from 'react';

const MultiChannelChat = () => {
  const [activeChannels, setActiveChannels] = useState<string[]>([]);
  const [messagesByChannel, setMessagesByChannel] = useState<Record<string, any[]>>({});
  const { listChannels, sendMessage } = useChat();

  const {
    connect,
    subscribeToChannel,
    unsubscribeFromChannel,
    setCallbacks,
    connectionState,
    isConnected,
    getSubscribedChannels
  } = useChatWebSocket({
    autoConnect: false, // Manual connection control
    autoReconnect: true,
    reconnectDelay: 1000,
    maxReconnectDelay: 30000
  });

  // Set up callbacks
  useEffect(() => {
    setCallbacks({
      onMessageReceived: (message) => {
        const channelId = typeof message.channel === 'string'
          ? message.channel
          : message.channel.id;

        setMessagesByChannel(prev => ({
          ...prev,
          [channelId]: [...(prev[channelId] || []), message]
        }));
      },
      onMessageDeleted: (data) => {
        setMessagesByChannel(prev => {
          const updated = { ...prev };
          if (updated[data.channel]) {
            updated[data.channel] = updated[data.channel].filter(
              m => m._id !== data._id
            );
          }
          return updated;
        });
      }
    });
  }, []);

  // Load and subscribe to channels
  const loadAndSubscribeToChannels = async () => {
    try {
      if (!isConnected) {
        await connect();
      }

      const response = await listChannels({
        type: 'public' as any,
        limit: 10
      });

      const channelIds = response.data.map(ch => ch.id);
      setActiveChannels(channelIds);

      // Subscribe to all channels
      for (const channelId of channelIds) {
        await subscribeToChannel(channelId);
      }

      console.log(`Subscribed to ${channelIds.length} channels`);
    } catch (error) {
      console.error('Failed to load channels:', error);
    }
  };

  // Join a specific channel
  const joinChannel = async (channelId: string) => {
    try {
      if (!isConnected) {
        await connect();
      }

      await subscribeToChannel(channelId);
      setActiveChannels(prev => [...prev, channelId]);
      console.log(`Joined channel: ${channelId}`);
    } catch (error) {
      console.error('Failed to join channel:', error);
    }
  };

  // Leave a channel
  const leaveChannel = (channelId: string) => {
    unsubscribeFromChannel(channelId);
    setActiveChannels(prev => prev.filter(id => id !== channelId));
    console.log(`Left channel: ${channelId}`);
  };

  // Send message to active channel
  const sendToChannel = async (channelId: string, text: string) => {
    try {
      await sendMessage(channelId, { message: text });
    } catch (error) {
      console.error('Failed to send message:', error);
    }
  };

  return (
    <div>
      <div className="header">
        <p>Connection: {connectionState}</p>
        <p>Channels: {activeChannels.length}</p>
        <button onClick={loadAndSubscribeToChannels}>
          Load Channels
        </button>
      </div>

      <div className="channels">
        {activeChannels.map((channelId) => (
          <div key={channelId} className="channel">
            <h4>{channelId}</h4>
            <button onClick={() => leaveChannel(channelId)}>Leave</button>

            <div className="messages">
              {(messagesByChannel[channelId] || []).map((msg) => (
                <div key={msg._id}>{msg.message}</div>
              ))}
            </div>

            <input
              onKeyPress={(e) => {
                if (e.key === 'Enter') {
                  sendToChannel(channelId, e.currentTarget.value);
                  e.currentTarget.value = '';
                }
              }}
              placeholder="Type a message..."
            />
          </div>
        ))}
      </div>
    </div>
  );
};

Support Channel Integration Example:

import { useChatWebSocket } from '@emd-cloud/react-components';
import { useState, useEffect } from 'react';

const StaffSupportDashboard = () => {
  const [supportCount, setSupportCount] = useState(0);
  const [supportChannels, setSupportChannels] = useState([]);

  const {
    connect,
    subscribeToSupport,
    subscribeToChannel,
    setCallbacks,
    isConnected
  } = useChatWebSocket({
    autoConnect: true
  });

  useEffect(() => {
    // Set up support-specific callbacks
    setCallbacks({
      onSupportCountUpdated: (data) => {
        console.log('Unread support messages:', data.count);
        setSupportCount(data.count);
      },
      onSupportChannelUpdated: (channel) => {
        console.log('Support channel updated:', channel);
        setSupportChannels(prev => {
          const index = prev.findIndex(ch => ch._id === channel._id);
          if (index >= 0) {
            const updated = [...prev];
            updated[index] = channel;
            return updated;
          }
          return [...prev, channel];
        });
      },
      onMessageReceived: (message) => {
        console.log('New support message:', message);
        // Handle new message in support channel
      }
    });
  }, []);

  // Subscribe to support channel when connected
  useEffect(() => {
    if (isConnected) {
      subscribeToSupport()
        .then(() => console.log('Subscribed to support channel'))
        .catch((error) => console.error('Support subscription failed:', error));
    }
  }, [isConnected]);

  // Subscribe to individual support channels
  const subscribeToSupportChannel = async (channelId: string) => {
    try {
      await subscribeToChannel(channelId, channelId);
      console.log(`Subscribed to support channel: ${channelId}`);
    } catch (error) {
      console.error('Failed to subscribe to support channel:', error);
    }
  };

  return (
    <div>
      <h3>Staff Support Dashboard</h3>
      <div className="stats">
        <p>Unread Support Messages: {supportCount}</p>
        <p>Active Support Channels: {supportChannels.length}</p>
      </div>

      <div className="support-channels">
        {supportChannels.map((channel) => (
          <div key={channel._id} className="support-channel">
            <h4>{channel.id}</h4>
            <p>Unread: {channel.unreadCountCreator || 0}</p>
            <p>Resolved: {channel.resolved ? 'Yes' : 'No'}</p>
            <button onClick={() => subscribeToSupportChannel(channel.id)}>
              Join Channel
            </button>
          </div>
        ))}
      </div>
    </div>
  );
};

Connection State Management Example:

import { useChatWebSocket, ConnectionState } from '@emd-cloud/react-components';
import { useEffect, useState } from 'react';

const ConnectionMonitor = () => {
  const [connectionHistory, setConnectionHistory] = useState<string[]>([]);
  const [reconnectAttempts, setReconnectAttempts] = useState(0);

  const {
    connect,
    disconnect,
    connectionState,
    isConnected,
    getConnectionState
  } = useChatWebSocket({
    autoConnect: false,
    autoReconnect: true,
    maxReconnectAttempts: 5,
    reconnectDelay: 2000,
    callbacks: {
      onConnectionStateChange: (state) => {
        const timestamp = new Date().toLocaleTimeString();
        setConnectionHistory(prev => [
          ...prev,
          `${timestamp}: ${state}`
        ]);

        if (state === ConnectionState.Connecting) {
          setReconnectAttempts(prev => prev + 1);
        } else if (state === ConnectionState.Connected) {
          setReconnectAttempts(0);
        }
      },
      onError: (error) => {
        console.error('Connection error:', error.message);
      }
    }
  });

  // Manual connection control
  const handleConnect = async () => {
    try {
      await connect();
      console.log('Connected successfully');
    } catch (error) {
      console.error('Connection failed:', error);
    }
  };

  const handleDisconnect = () => {
    disconnect();
    console.log('Disconnected');
  };

  // Monitor connection state
  useEffect(() => {
    const interval = setInterval(() => {
      const currentState = getConnectionState();
      console.log('Current state:', currentState);
    }, 5000);

    return () => clearInterval(interval);
  }, []);

  return (
    <div>
      <h3>WebSocket Connection Monitor</h3>

      <div className="status">
        <p>State: <strong>{connectionState}</strong></p>
        <p>Connected: {isConnected ? 'Yes' : 'No'}</p>
        <p>Reconnect Attempts: {reconnectAttempts}</p>
      </div>

      <div className="controls">
        <button onClick={handleConnect} disabled={isConnected}>
          Connect
        </button>
        <button onClick={handleDisconnect} disabled={!isConnected}>
          Disconnect
        </button>
      </div>

      <div className="history">
        <h4>Connection History</h4>
        <ul>
          {connectionHistory.map((entry, index) => (
            <li key={index}>{entry}</li>
          ))}
        </ul>
      </div>
    </div>
  );
};

Uploader Hooks:

Hook: useUploader

Description:

This hook provides file upload functionality to EMD Cloud storage using the SDK's TUS (resumable upload) protocol implementation. It manages multiple concurrent file uploads with progress tracking, automatic retries, and flexible permission settings. The hook integrates seamlessly with the ApplicationProvider and handles all aspects of the upload lifecycle including progress updates, error handling, and upload cancellation.

Key Features:

  • Resumable Uploads: Uses TUS protocol for reliable uploads that can survive connection interruptions
  • Multiple File Support: Upload multiple files simultaneously with independent progress tracking
  • Progress Tracking: Real-time progress updates with bytes uploaded, total bytes, and percentage
  • Flexible Permissions: Support for public, authenticated users, staff only, or specific user access
  • Chunked Upload: Large file support with configurable chunk size (default: 5MB)
  • Automatic Retry: Configurable retry delays for failed upload chunks
  • Upload Control: Ability to cancel/abort individual file uploads
  • Request Interception: onBeforeRequest callback for request customization
  • Type Safety: Full TypeScript support with SDK types

Parameters:

Note: The hook automatically uses apiUrl and app configuration from the ApplicationProvider context. Make sure your application is wrapped with ApplicationProvider before using this hook.

  • integration (string, optional): S3 integration identifier. Default: 'default'
  • chunkSize (number, optional): Size of each upload chunk in bytes. Default: 5242880 (5MB)
  • headers (object, optional): Additional HTTP headers to include in upload requests
  • readPermission (ReadPermission, optional): File access permission level. Options:
    • ReadPermission.Public - File accessible to everyone
    • ReadPermission.OnlyAuthUser - File accessible to all authenticated users
    • ReadPermission.OnlyAppStaff - File accessible to uploader and app staff (default)
    • ReadPermission.OnlyPermittedUsers - File accessible only to users in permittedUsers array
  • permittedUsers (string[], optional): Array of user IDs who can access the file (required when readPermission is OnlyPermittedUsers)
  • presignedUrlTTL (number, optional): Lifetime of generated presigned URLs in minutes. Default: 60
  • retryDelays (number[], optional): Array of delay intervals in milliseconds for retry attempts. Default: [0, 3000, 5000, 10000, 20000]
  • onBeforeUpload (function, optional): Callback invoked before upload starts. Return false to cancel upload. Default: () => true
  • onBeforeRequest (function, optional): Callback invoked before each HTTP request during upload (for request interception/modification)
  • onUpdate (function, required): Callback invoked when file upload statuses change. Receives array of file objects with current status

Return Value: Returns an object with two properties:

  • uploadFiles (function): Function to initiate file uploads. Accepts an array of File objects
  • isProccess (boolean): Boolean flag indicating if any uploads are currently in progress

File Object Structure:

Each file in the onUpdate callback has the following structure:

{
  id: string;              // Unique upload ID
  fileName: string;        // Name of the file
  status: 'started' | 'progress' | 'success' | 'failed';
  progress?: string;       // Upload progress percentage (e.g., "45.32")
  bytesUploaded?: number;  // Number of bytes uploaded
  bytesTotal?: number;     // Total file size in bytes
  fileUrl?: string;        // URL to access the file (available after success)
  error?: Error;           // Error object (if upload failed)
  methods?: {
    stop: () => void;      // Function to cancel/abort the upload
  };
}

Basic Example: