Alaznah

Foreground swipe-to-accept UX, decline, native wake, and syncing pending invites.

Incoming Calls

When another user calls your userId, the client emits an inbound call. How it appears depends on whether the app is in the foreground, background, or killed.

Foreground (in-app UI)

Keep CallingUI or CallingScreen mounted so the SDK can show IncomingCallScreen.

Accept — swipe up (not tap)

The green accept control is a vertical swipe-up gesture:

  • Tap does nothing (prevents accidental answer)
  • Drag the green button up past the threshold (or flick upward) to accept
  • While ringing, the button plays a pulse / ripple animation; stacked chevrons above hint the swipe direction
  • Audio calls show a flipped handset; video (join with video) shows a camera icon

Decline — tap

The red decline control is a normal press → reject.

Programmatic API

ts
await client.accept(callId);
await client.reject(callId, 'declined'); // or 'busy' | 'unavailable'

Hooks:

ts
const incoming = useIncomingCall();
// incoming?.callId, mediaType, peerId, ...

Video answer options

On video invites, the callee can toggle joining with camera on/off before swipe-accept. Audio invites stay audio.

Avoiding a double incoming UI

On Android (and similar native accept paths), the user may Accept/Decline on a system / native full-screen activity first. To avoid a brief flash of the JS incoming screen when returning to the app:

ts
// On AppState → 'active' (and before syncing UI)
await client.drainNativeIncomingAction();
await client.syncPendingCalls();

CallingProvider / built-in screens handle much of this when used as recommended. For custom hosts, call drainNativeIncomingAction early on resume.

Also:

ts
client.setNativeIncomingSuppressed(true);  // while in-app CallingUI is visible
client.setNativeIncomingSuppressed(false); // when leaving calling UI

Background / killed

To ring when the JS UI is not open:

  1. Configure FCM / APNs (and iOS PushKit / VoIP) credentials in the Developer Console
  2. Follow Push Notifications
  3. Register the device token:
ts
await client.registerPushToken(token, Platform.OS === 'ios' ? 'ios' : 'android');
  1. On resume / cold start after accept, sync:
ts
await client.syncPendingCalls();

Wake / auto-accept

After a native Accept (CallKit / full-screen intent), the SDK may cold-start already accepting. Observe:

ts
client.on('waking:for-call', (active) => { /* hide dialer chrome while true */ });
// or
const waking = useWakingForCall();

client.isAutoAcceptingCall(callId) is true when that invite was accepted natively — skip showing JS IncomingCallScreen for that id.

Checklist

ScenarioWhat to use
App openCallingScreen / CallingUI mounted
App returns to foregrounddrainNativeIncomingAction + syncPendingCalls
App backgrounded / killedPush + native incoming UI + token registration
Custom UIuseIncomingCall + accept / reject (implement your own swipe if desired)