Alaznah

Built-in Alaznah Calling screens, overlay, controls, and video views.

UI components

Ready-made React Native UI for 1:1 voice and video. Pick one integration style, then wire props from Props.

All UI must sit under CallingProvider. Calling client.startCall() alone does not open a screen — you must mount CallingScreen or CallingUI (or your own screens).

Import paths

Main package — provider, hooks, screens overview, video views, and CallingUI:

ts
import {
  CallingProvider,
  CallingScreen,
  CallingUI,
  useCallingClient,
  useCall,
  useIncomingCall,
  useCallingReady,
  LocalVideoView,
  RemoteVideoView,
  VideoView,
} from '@alaznah/calling';

UI barrel — lower-level screens, controls, and theme helpers:

ts
import {
  IncomingCallScreen,
  ActiveCallScreen,
  CallControls,
  AudioWave,
  mergeTheme,
  defaultCallingTheme,
} from '@alaznah/calling/ui';
ExportFrom
CallingProvider, hooks@alaznah/calling
CallingScreen, CallingUI, LocalVideoView, RemoteVideoView, VideoView@alaznah/calling
IncomingCallScreen, ActiveCallScreen, CallControls, AudioWave, theme helpers@alaznah/calling/ui

Which component should I use?

GoalUse
Fastest demo / first callCallingScreen inside CallingProvider
Your chat/contacts UI + Alaznah call overlayCallingUI next to your navigator
Fully custom screensHooks + IncomingCallScreen / ActiveCallScreen / video views

Narrative walkthrough: Call UI. Theming & slots: Customization.


1. CallingProvider (required)

Wrap the part of the app that places or receives calls (usually above your navigator):

tsx
import {
  CallingProvider,
  DEFAULT_HOSTED_SIGNALING_URL,
} from '@alaznah/calling';

export function App() {
  return (
    <CallingProvider
      config={{
        userId: 'alice',
        getAuthToken: () => fetchCallingToken(),
        // omit signalingUrl to use Hosted Signaling, or set explicitly:
        signalingUrl: DEFAULT_HOSTED_SIGNALING_URL,
      }}
    >
      {/* navigator / screens / CallingUI */}
    </CallingProvider>
  );
}
PropRequiredNotes
configYesuserId, getAuthToken, optional signalingUrl, …
autoConnectNoDefault true
childrenYesYour app tree

Full config table: Props · tokens: Authentication.


2. CallingScreen — all-in-one

Dialer + automatic routing between idle / outgoing / incoming / active.

tsx
import { CallingProvider, CallingScreen } from '@alaznah/calling';

<CallingProvider config={config}>
  <CallingScreen
    userId="alice"
    defaultPeerId="bob"
    // optional:
    // pushToken={fcmOrApnsToken}
    // pushPlatform="android"
    // onError={(e) => console.warn(e)}
  />
</CallingProvider>
PropRequiredDescription
userIdYesSame as config.userId
defaultPeerIdNoPrefills who to call
pushToken / pushPlatformNoRegisters for background wake
onLogout / onErrorNoApp callbacks

Use this for Quick Start and demos. For a real chat app, prefer CallingUI so dialer stays your UI.


3. CallingUI — overlay for your app

Shows incoming and active call UI as a modal/overlay. Idle = renders nothing (your screens stay visible).

tsx
import {
  CallingProvider,
  CallingUI,
  useCallingClient,
  useCallingReady,
} from '@alaznah/calling';
function CallingOverlay() {
  const client = useCallingClient();
  return <CallingUI client={client} onError={(e) => console.warn(e)} />;
}

function Root() {
  return (
    <CallingProvider config={config}>
      <AppNavigator />
      <CallingOverlay />
    </CallingProvider>
  );
}

// Somewhere in chat / contacts:
async function onAudioCall(peerId: string) {
  const client = /* useCallingClient() in that screen */;
  await client.startCall({ calleeId: peerId, mediaType: 'audio' });
  // CallingUI picks up call:updated / incoming automatically
}
PropRequiredDescription
clientYesFrom useCallingClient()
themeNoPartial CallingTheme
backgroundColor / backgroundImageNoScreen backdrop
slotsNoCustom avatar / header / controls / overlay
renderIncomingScreen / renderActiveCallScreenNoReplace whole screens
onErrorNoError callback

What it shows

  1. Inbound ringingIncomingCallScreen (swipe-up accept)
  2. Outbound / connecting / connected → ActiveCallScreen
  3. No call → null

4. IncomingCallScreen

Default inbound layout (used by CallingUI / CallingScreen).

  • Shows peer id/name treatment + avatar
  • Decline = tap red
  • Accept = swipe up on green (tap does not accept)
  • Video: optional local preview + join-with-video

Prefer CallingUI instead of mounting this alone, so native suppress / drain stay correct — Incoming Calls.


5. ActiveCallScreen

In-call UI for connecting / connected / reconnecting states.

  • Voice: avatar + status + controls
  • Video: local / remote video layout
  • Optional minimize → floating bubble (MinimizedCallBubble)
  • On supported Android devices, an active connected video call automatically enters Picture-in-Picture when the user leaves the app (Home / another app). The call continues in the PiP window; tapping it returns to the full calling UI. Voice-only and ringing calls do not enter PiP. Host apps must declare android:supportsPictureInPicture="true" on the main Activity and forward onUserLeaveHint / onPictureInPictureModeChanged to the SDK (see the basic-call example).

Usually rendered via CallingUI. Manual use needs call, client, theme, and onEnd.


6. CallControls

Bottom dock: mute, speaker, camera (video), end.

Built into ActiveCallScreen. For a custom layout:

tsx
import { CallControls, defaultCallingTheme } from '@alaznah/calling/ui';
<CallControls
  call={call}
  client={client}
  theme={defaultCallingTheme}
  onEnd={() => client.end()}
/>

7. Video views

tsx
import { LocalVideoView, RemoteVideoView } from '@alaznah/calling';
import { useCall } from '@alaznah/calling';

function CustomVideo() {
  const call = useCall();
  if (!call) return null;
  return (
    <>
      <RemoteVideoView stream={call.remoteStream} objectFit="cover" />
      <LocalVideoView stream={call.localStream} mirror objectFit="cover" />
    </>
  );
}

CallingUI / ActiveCallScreen already wire these for the default experience.


8. Hooks used with UI

ts
const client = useCallingClient(); // startCall, accept, end, …
const ready = useCallingReady();   // wait before dialing
const call = useCall();            // active / focused call
const incoming = useIncomingCall(); // ringing inbound (if any)

Use useCall() — there is no useActiveCall.

More: Hooks · Client Methods.


Minimal patterns (copy-paste)

A — Demo with dialer

tsx
import { CallingProvider, CallingScreen } from '@alaznah/calling';

export default function App() {
  return (
    <CallingProvider
      config={{
        userId: 'alice',
        getAuthToken: fetchCallingToken,
      }}
    >
      <CallingScreen userId="alice" defaultPeerId="bob" />
    </CallingProvider>
  );
}

B — Chat app + overlay

tsx
import { CallingProvider, CallingUI, useCallingClient } from '@alaznah/calling';
function Overlay() {
  return <CallingUI client={useCallingClient()} />;
}

export default function App() {
  return (
    <CallingProvider config={config}>
      <ChatNavigator />
      <Overlay />
    </CallingProvider>
  );
}

C — Permissions before dial

ts
import { requestCallPermissions } from '@alaznah/calling';

// for audio call
await requestCallPermissions('audio');
await client.startCall({ calleeId: peerId, mediaType: 'audio' });

// for video call
await requestCallPermissions('video');
await client.startCall({ calleeId: peerId, mediaType: 'video' });