@scalemule/conference
v0.0.6
Published
ScaleMule Conference SDK — audio/video calls, screen sharing, device management
Readme
@scalemule/conference
ScaleMule Conference SDK — audio/video calls, screen sharing, device management.
Install
npm install @scalemule/conferenceUsage
Core client (no React required)
import { ConferenceClient } from '@scalemule/conference';
const conf = new ConferenceClient({ baseUrl: '/api/conference' });
const call = await conf.createCall({ conversationId, callType: 'video' });
const session = await conf.joinCall(call.id);React components
import { CallOverlay, CallButton, CallControls } from '@scalemule/conference/react';
<CallOverlay
session={session}
onTokenRefresh={() => conf.joinCall(call.id)}
onClose={() => conf.leaveCall(call.id)}
/>End-to-end call flow (with @scalemule/chat)
The conference SDK owns the call lifecycle + media UI. The chat SDK owns the call triggers + banners + sidebar indicators. Here's how they compose:
1. Trigger a call from the chat header
import { CallTriggerGroup } from '@scalemule/chat/react'
import { ConferenceClient, PreCallLobby } from '@scalemule/conference/react'
const conf = new ConferenceClient({ baseUrl: '/api/conference' })
<CallTriggerGroup
conversationId={conversationId}
onCallRequested={(id, type) => {
setLobbyOpen(true)
setCallType(type)
}}
/>2. Pre-call lobby (permissions + device preview)
<PreCallLobby
callType={callType}
onJoin={async (opts) => {
const call = await conf.createCall({ conversationId, callType })
const session = await conf.joinCall(call.id)
setSession(session)
setLobbyOpen(false)
}}
onCancel={() => setLobbyOpen(false)}
errorMessages={{
denied: 'Browser blocked access. Check your settings.',
notFound: 'No microphone or camera found.',
}}
/>3. Active call: huddle bar ↔ full-screen toggle
import { CallWindow } from '@scalemule/conference/react'
{session && (
<CallWindow
session={session}
callType={callType}
onTokenRefresh={() => conf.joinCall(callId)}
onClose={() => {
conf.leaveCall(callId)
setSession(null)
}}
/>
)}CallWindow renders either the full-screen <CallOverlay> or the compact <HuddleBar> and handles the Escape-to-minimize toggle. Pass initialMode="huddle" for audio-only calls.
4. Call duration timer
import { CallTimer, useCallDuration } from '@scalemule/conference/react'
// Component shorthand:
<CallTimer startedAt={call.createdAt} />
// Hook for custom rendering:
const { elapsed, formatted } = useCallDuration(call.createdAt)5. Incoming call notifications (polling + ring + browser Notification)
import { useCallNotifications, CallNotificationToast } from '@scalemule/conference/react'
const { activeCalls } = useCallNotifications({
client: conf,
currentUserId,
enableRingSound: true,
enableBrowserNotification: true,
onNotificationClick: (call) => router.push(`/messages/${call.conversationId}`),
})
{activeCalls
.filter((c) => c.conversationId !== currentConvId)
.map((call) => (
<CallNotificationToast
key={call.id}
call={call}
onJoin={() => openLobby(call.id)}
onDismiss={() => {}}
resolveUserName={(id) => profiles.get(id)?.display_name ?? 'Someone'}
/>
))}6. Active call banner (in the current conversation)
import { ActiveCallBanner } from '@scalemule/chat/react'
<ActiveCallBanner
call={activeCalls.find((c) => c.conversationId === currentConvId) ?? null}
onJoin={(callId) => openLobby(callId)}
getUserName={(id) => profiles.get(id)?.display_name}
/>7. Sidebar indicators
import { ConversationList, ActiveCallDot } from '@scalemule/chat/react'
const activeConvIds = new Set(activeCalls.map((c) => c.conversationId).filter(Boolean))
<ConversationList
renderActiveIndicator={(c) => <ActiveCallDot active={activeConvIds.has(c.id)} />}
/>8. Connection quality
import { ConnectionQualityIndicator } from '@scalemule/conference/react'
<ConnectionQualityIndicator quality={connectionQuality} />Host resolves connectionQuality from WebRTC stats (e.g. LiveKit's useConnectionQualityIndicator hook) — the SDK stays vendor-neutral.
Optional: LiveKit default styling
If you want LiveKit's default cosmetic styling for CallOverlay/VideoConference:
npm install @livekit/components-stylesThen import it in your app entry:
import '@livekit/components-styles';Without this, the components still work (they have inline layout styles) but lack LiveKit's theme styling.
License
MIT
