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
await client.accept(callId);
await client.reject(callId, 'declined'); // or 'busy' | 'unavailable'Hooks:
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:
// 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:
client.setNativeIncomingSuppressed(true); // while in-app CallingUI is visible
client.setNativeIncomingSuppressed(false); // when leaving calling UIBackground / killed
To ring when the JS UI is not open:
- Configure FCM / APNs (and iOS PushKit / VoIP) credentials in the Developer Console
- Follow Push Notifications
- Register the device token:
await client.registerPushToken(token, Platform.OS === 'ios' ? 'ios' : 'android');- On resume / cold start after accept, sync:
await client.syncPendingCalls();Wake / auto-accept
After a native Accept (CallKit / full-screen intent), the SDK may cold-start already accepting. Observe:
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
| Scenario | What to use |
|---|---|
| App open | CallingScreen / CallingUI mounted |
| App returns to foreground | drainNativeIncomingAction + syncPendingCalls |
| App backgrounded / killed | Push + native incoming UI + token registration |
| Custom UI | useIncomingCall + accept / reject (implement your own swipe if desired) |