@cometchat/skills 4.0.0 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -2
- package/bin/install.js +448 -57
- package/package.json +1 -1
- package/skills/cometchat/SKILL.md +337 -80
- package/skills/cometchat-a11y/SKILL.md +476 -0
- package/skills/cometchat-android-v5/SKILL.md +1 -1
- package/skills/cometchat-android-v5-calls/SKILL.md +413 -0
- package/skills/cometchat-android-v5-calls/references/README.md +53 -0
- package/skills/cometchat-android-v5-calls/references/add-calls-to-existing-chat.md +195 -0
- package/skills/cometchat-android-v5-calls/references/audio-controls.md +91 -0
- package/skills/cometchat-android-v5-calls/references/background-handling.md +168 -0
- package/skills/cometchat-android-v5-calls/references/call-layouts.md +123 -0
- package/skills/cometchat-android-v5-calls/references/call-logs.md +113 -0
- package/skills/cometchat-android-v5-calls/references/call-session.md +274 -0
- package/skills/cometchat-android-v5-calls/references/custom-ui.md +150 -0
- package/skills/cometchat-android-v5-calls/references/event-listeners.md +123 -0
- package/skills/cometchat-android-v5-calls/references/group-calls.md +205 -0
- package/skills/cometchat-android-v5-calls/references/idle-timeout.md +111 -0
- package/skills/cometchat-android-v5-calls/references/in-call-chat.md +134 -0
- package/skills/cometchat-android-v5-calls/references/join-session.md +120 -0
- package/skills/cometchat-android-v5-calls/references/migration-v4-to-v5.md +206 -0
- package/skills/cometchat-android-v5-calls/references/participant-management.md +110 -0
- package/skills/cometchat-android-v5-calls/references/picture-in-picture.md +106 -0
- package/skills/cometchat-android-v5-calls/references/raise-hand.md +242 -0
- package/skills/cometchat-android-v5-calls/references/recording.md +101 -0
- package/skills/cometchat-android-v5-calls/references/ringing-integration.md +152 -0
- package/skills/cometchat-android-v5-calls/references/screen-sharing.md +82 -0
- package/skills/cometchat-android-v5-calls/references/server-fcm-voip.md +215 -0
- package/skills/cometchat-android-v5-calls/references/session-settings.md +121 -0
- package/skills/cometchat-android-v5-calls/references/setup.md +137 -0
- package/skills/cometchat-android-v5-calls/references/share-invite.md +151 -0
- package/skills/cometchat-android-v5-calls/references/video-controls.md +87 -0
- package/skills/cometchat-android-v5-calls/references/voip-calling.md +526 -0
- package/skills/cometchat-android-v5-components/SKILL.md +1 -1
- package/skills/cometchat-android-v5-core/SKILL.md +1 -1
- package/skills/cometchat-android-v5-customization/SKILL.md +1 -1
- package/skills/cometchat-android-v5-extensions/SKILL.md +1 -1
- package/skills/cometchat-android-v5-features/SKILL.md +1 -1
- package/skills/cometchat-android-v5-placement/SKILL.md +1 -1
- package/skills/cometchat-android-v5-production/SKILL.md +1 -1
- package/skills/cometchat-android-v5-push/SKILL.md +1 -1
- package/skills/cometchat-android-v5-testing/SKILL.md +1 -1
- package/skills/cometchat-android-v5-theming/SKILL.md +1 -1
- package/skills/cometchat-android-v5-troubleshooting/SKILL.md +1 -1
- package/skills/cometchat-android-v6/SKILL.md +1 -1
- package/skills/cometchat-android-v6-builder-settings/SKILL.md +1 -1
- package/skills/cometchat-android-v6-calls/SKILL.md +375 -0
- package/skills/cometchat-android-v6-calls/references/add-calls-to-existing-chat.md +97 -0
- package/skills/cometchat-android-v6-calls/references/call-layouts.md +91 -0
- package/skills/cometchat-android-v6-calls/references/call-session.md +163 -0
- package/skills/cometchat-android-v6-calls/references/device-management.md +171 -0
- package/skills/cometchat-android-v6-calls/references/group-calls.md +191 -0
- package/skills/cometchat-android-v6-calls/references/idle-timeout.md +107 -0
- package/skills/cometchat-android-v6-calls/references/in-call-chat.md +136 -0
- package/skills/cometchat-android-v6-calls/references/raise-hand.md +186 -0
- package/skills/cometchat-android-v6-calls/references/ringing-integration.md +176 -0
- package/skills/cometchat-android-v6-calls/references/server-fcm-voip.md +127 -0
- package/skills/cometchat-android-v6-calls/references/share-invite.md +105 -0
- package/skills/cometchat-android-v6-compose-components/SKILL.md +2 -2
- package/skills/cometchat-android-v6-compose-customization/SKILL.md +1 -1
- package/skills/cometchat-android-v6-compose-placement/SKILL.md +1 -1
- package/skills/cometchat-android-v6-compose-theming/SKILL.md +1 -1
- package/skills/cometchat-android-v6-core/SKILL.md +1 -1
- package/skills/cometchat-android-v6-events/SKILL.md +1 -1
- package/skills/cometchat-android-v6-extensions/SKILL.md +1 -1
- package/skills/cometchat-android-v6-features/SKILL.md +1 -1
- package/skills/cometchat-android-v6-kotlin-components/SKILL.md +1 -1
- package/skills/cometchat-android-v6-kotlin-customization/SKILL.md +1 -1
- package/skills/cometchat-android-v6-kotlin-placement/SKILL.md +1 -1
- package/skills/cometchat-android-v6-kotlin-theming/SKILL.md +1 -1
- package/skills/cometchat-android-v6-migration/SKILL.md +398 -0
- package/skills/cometchat-android-v6-production/SKILL.md +1 -1
- package/skills/cometchat-android-v6-push/SKILL.md +1 -1
- package/skills/cometchat-android-v6-testing/SKILL.md +1 -1
- package/skills/cometchat-android-v6-troubleshooting/SKILL.md +1 -1
- package/skills/cometchat-angular-calls/SKILL.md +511 -0
- package/skills/cometchat-angular-calls/references/add-calls-to-existing-chat.md +95 -0
- package/skills/cometchat-angular-calls/references/call-layouts.md +106 -0
- package/skills/cometchat-angular-calls/references/call-session.md +167 -0
- package/skills/cometchat-angular-calls/references/custom-ui.md +231 -0
- package/skills/cometchat-angular-calls/references/device-management.md +135 -0
- package/skills/cometchat-angular-calls/references/group-calls.md +412 -0
- package/skills/cometchat-angular-calls/references/idle-timeout.md +145 -0
- package/skills/cometchat-angular-calls/references/in-call-chat.md +148 -0
- package/skills/cometchat-angular-calls/references/lazy-loading-pitfalls.md +205 -0
- package/skills/cometchat-angular-calls/references/migration-v4-to-v5.md +53 -0
- package/skills/cometchat-angular-calls/references/ngzone-and-async-callbacks.md +183 -0
- package/skills/cometchat-angular-calls/references/picture-in-picture.md +285 -0
- package/skills/cometchat-angular-calls/references/raise-hand.md +167 -0
- package/skills/cometchat-angular-calls/references/recording-screen-share.md +267 -0
- package/skills/cometchat-angular-calls/references/ringing-integration.md +131 -0
- package/skills/cometchat-angular-calls/references/server-web-push-vapid.md +100 -0
- package/skills/cometchat-angular-calls/references/share-invite.md +116 -0
- package/skills/cometchat-angular-components/SKILL.md +1 -1
- package/skills/cometchat-angular-core/SKILL.md +1 -1
- package/skills/cometchat-angular-customization/SKILL.md +1 -1
- package/skills/cometchat-angular-features/SKILL.md +1 -1
- package/skills/cometchat-angular-patterns/SKILL.md +1 -1
- package/skills/cometchat-angular-placement/SKILL.md +1 -1
- package/skills/cometchat-angular-production/SKILL.md +1 -1
- package/skills/cometchat-angular-push/SKILL.md +403 -0
- package/skills/cometchat-angular-testing/SKILL.md +333 -0
- package/skills/cometchat-angular-theming/SKILL.md +1 -1
- package/skills/cometchat-angular-troubleshooting/SKILL.md +1 -1
- package/skills/cometchat-astro-patterns/SKILL.md +1 -1
- package/skills/cometchat-calls/SKILL.md +358 -0
- package/skills/cometchat-calls/references/use-case-broadcast.md +194 -0
- package/skills/cometchat-calls/references/use-case-marketplace.md +158 -0
- package/skills/cometchat-calls/references/use-case-support.md +190 -0
- package/skills/cometchat-calls/references/use-case-team.md +169 -0
- package/skills/cometchat-calls/references/use-case-telehealth.md +154 -0
- package/skills/cometchat-components/SKILL.md +1 -1
- package/skills/cometchat-core/SKILL.md +1 -1
- package/skills/cometchat-customization/SKILL.md +2 -2
- package/skills/cometchat-features/SKILL.md +2 -2
- package/skills/cometchat-flutter-v5/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-calls/SKILL.md +325 -94
- package/skills/cometchat-flutter-v5-calls/references/add-calls-to-existing-chat.md +169 -0
- package/skills/cometchat-flutter-v5-calls/references/call-layouts.md +98 -0
- package/skills/cometchat-flutter-v5-calls/references/call-session.md +330 -0
- package/skills/cometchat-flutter-v5-calls/references/device-management.md +120 -0
- package/skills/cometchat-flutter-v5-calls/references/group-calls.md +187 -0
- package/skills/cometchat-flutter-v5-calls/references/idle-timeout.md +97 -0
- package/skills/cometchat-flutter-v5-calls/references/in-call-chat.md +132 -0
- package/skills/cometchat-flutter-v5-calls/references/migration-v4-to-v5.md +160 -0
- package/skills/cometchat-flutter-v5-calls/references/raise-hand.md +244 -0
- package/skills/cometchat-flutter-v5-calls/references/ringing-integration.md +193 -0
- package/skills/cometchat-flutter-v5-calls/references/server-push-bridge.md +101 -0
- package/skills/cometchat-flutter-v5-calls/references/share-invite.md +142 -0
- package/skills/cometchat-flutter-v5-conversations/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-core/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-customization/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-events/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-messages/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-production/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-push/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-testing/SKILL.md +390 -0
- package/skills/cometchat-flutter-v5-theming/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-troubleshooting/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-users-groups/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-calls/SKILL.md +308 -243
- package/skills/cometchat-flutter-v6-calls/references/add-calls-to-existing-chat.md +134 -0
- package/skills/cometchat-flutter-v6-calls/references/call-layouts.md +102 -0
- package/skills/cometchat-flutter-v6-calls/references/call-session.md +229 -0
- package/skills/cometchat-flutter-v6-calls/references/device-management.md +99 -0
- package/skills/cometchat-flutter-v6-calls/references/group-calls.md +190 -0
- package/skills/cometchat-flutter-v6-calls/references/idle-timeout.md +80 -0
- package/skills/cometchat-flutter-v6-calls/references/in-call-chat.md +122 -0
- package/skills/cometchat-flutter-v6-calls/references/raise-hand.md +268 -0
- package/skills/cometchat-flutter-v6-calls/references/ringing-integration.md +171 -0
- package/skills/cometchat-flutter-v6-calls/references/server-push-bridge.md +93 -0
- package/skills/cometchat-flutter-v6-calls/references/share-invite.md +102 -0
- package/skills/cometchat-flutter-v6-components/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-conversations/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-core/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-customization/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-events/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-features/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-messages/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-migration/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-placement/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-production/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-push/SKILL.md +452 -0
- package/skills/cometchat-flutter-v6-testing/SKILL.md +319 -0
- package/skills/cometchat-flutter-v6-theming/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-troubleshooting/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-users-groups/SKILL.md +1 -1
- package/skills/cometchat-i18n/SKILL.md +358 -0
- package/skills/cometchat-ios/SKILL.md +5 -5
- package/skills/cometchat-ios-calls/SKILL.md +409 -0
- package/skills/cometchat-ios-calls/references/add-calls-to-existing-chat.md +172 -0
- package/skills/cometchat-ios-calls/references/avaudiosession-routing.md +189 -0
- package/skills/cometchat-ios-calls/references/call-layouts.md +136 -0
- package/skills/cometchat-ios-calls/references/call-session.md +221 -0
- package/skills/cometchat-ios-calls/references/callkit-and-pushkit.md +353 -0
- package/skills/cometchat-ios-calls/references/custom-ui.md +322 -0
- package/skills/cometchat-ios-calls/references/device-management.md +171 -0
- package/skills/cometchat-ios-calls/references/group-calls.md +396 -0
- package/skills/cometchat-ios-calls/references/idle-timeout.md +185 -0
- package/skills/cometchat-ios-calls/references/in-call-chat.md +183 -0
- package/skills/cometchat-ios-calls/references/migration-v4-to-v5.md +138 -0
- package/skills/cometchat-ios-calls/references/picture-in-picture.md +246 -0
- package/skills/cometchat-ios-calls/references/raise-hand.md +239 -0
- package/skills/cometchat-ios-calls/references/recording-screen-share.md +339 -0
- package/skills/cometchat-ios-calls/references/ringing-integration.md +142 -0
- package/skills/cometchat-ios-calls/references/server-apns-pushkit.md +267 -0
- package/skills/cometchat-ios-calls/references/share-invite.md +152 -0
- package/skills/cometchat-ios-calls/references/swiftui-uikit-hosting.md +246 -0
- package/skills/cometchat-ios-calls/references/voip-cert-setup.md +166 -0
- package/skills/cometchat-ios-components/SKILL.md +1 -1
- package/skills/cometchat-ios-core/SKILL.md +1 -1
- package/skills/cometchat-ios-customization/SKILL.md +1 -1
- package/skills/cometchat-ios-features/SKILL.md +1 -1
- package/skills/cometchat-ios-placement/SKILL.md +1 -1
- package/skills/cometchat-ios-production/SKILL.md +1 -1
- package/skills/cometchat-ios-push/SKILL.md +1 -1
- package/skills/cometchat-ios-testing/SKILL.md +371 -0
- package/skills/cometchat-ios-theming/SKILL.md +1 -1
- package/skills/cometchat-ios-troubleshooting/SKILL.md +1 -1
- package/skills/cometchat-native-bare-patterns/SKILL.md +45 -3
- package/skills/cometchat-native-calls/SKILL.md +599 -0
- package/skills/cometchat-native-calls/references/add-calls-to-existing-chat.md +203 -0
- package/skills/cometchat-native-calls/references/call-layouts.md +124 -0
- package/skills/cometchat-native-calls/references/call-session.md +182 -0
- package/skills/cometchat-native-calls/references/custom-ui.md +250 -0
- package/skills/cometchat-native-calls/references/device-management.md +135 -0
- package/skills/cometchat-native-calls/references/expo-vs-bare.md +179 -0
- package/skills/cometchat-native-calls/references/group-calls.md +291 -0
- package/skills/cometchat-native-calls/references/idle-timeout.md +128 -0
- package/skills/cometchat-native-calls/references/in-call-chat.md +143 -0
- package/skills/cometchat-native-calls/references/migration-v4-to-v5.md +72 -0
- package/skills/cometchat-native-calls/references/picture-in-picture.md +276 -0
- package/skills/cometchat-native-calls/references/raise-hand.md +203 -0
- package/skills/cometchat-native-calls/references/recording-screen-share.md +290 -0
- package/skills/cometchat-native-calls/references/ringing-integration.md +118 -0
- package/skills/cometchat-native-calls/references/server-push-bridge.md +135 -0
- package/skills/cometchat-native-calls/references/server-push-payloads.md +193 -0
- package/skills/cometchat-native-calls/references/share-invite.md +144 -0
- package/skills/cometchat-native-calls/references/voip-push-end-to-end.md +317 -0
- package/skills/cometchat-native-components/SKILL.md +1 -1
- package/skills/cometchat-native-core/SKILL.md +1 -1
- package/skills/cometchat-native-customization/SKILL.md +1 -1
- package/skills/cometchat-native-expo-patterns/SKILL.md +18 -1
- package/skills/cometchat-native-features/SKILL.md +1 -1
- package/skills/cometchat-native-placement/SKILL.md +1 -1
- package/skills/cometchat-native-production/SKILL.md +1 -1
- package/skills/cometchat-native-push/SKILL.md +1 -1
- package/skills/cometchat-native-testing/SKILL.md +1 -1
- package/skills/cometchat-native-theming/SKILL.md +1 -1
- package/skills/cometchat-native-troubleshooting/SKILL.md +1 -1
- package/skills/cometchat-nextjs-patterns/SKILL.md +1 -1
- package/skills/cometchat-placement/SKILL.md +29 -1
- package/skills/cometchat-production/SKILL.md +1 -1
- package/skills/cometchat-react-calls/SKILL.md +539 -0
- package/skills/cometchat-react-calls/references/add-calls-to-existing-chat.md +145 -0
- package/skills/cometchat-react-calls/references/call-layouts.md +161 -0
- package/skills/cometchat-react-calls/references/call-session.md +235 -0
- package/skills/cometchat-react-calls/references/custom-ui.md +201 -0
- package/skills/cometchat-react-calls/references/device-management.md +206 -0
- package/skills/cometchat-react-calls/references/group-calls.md +262 -0
- package/skills/cometchat-react-calls/references/idle-timeout.md +175 -0
- package/skills/cometchat-react-calls/references/in-call-chat.md +212 -0
- package/skills/cometchat-react-calls/references/migration-v4-to-v5.md +172 -0
- package/skills/cometchat-react-calls/references/picture-in-picture.md +245 -0
- package/skills/cometchat-react-calls/references/raise-hand.md +238 -0
- package/skills/cometchat-react-calls/references/recording-screen-share.md +143 -0
- package/skills/cometchat-react-calls/references/ringing-integration.md +255 -0
- package/skills/cometchat-react-calls/references/server-web-push-vapid.md +241 -0
- package/skills/cometchat-react-calls/references/share-invite.md +176 -0
- package/skills/cometchat-react-calls/references/testing-calls-on-web.md +198 -0
- package/skills/cometchat-react-calls/references/voip-and-web-push.md +165 -0
- package/skills/cometchat-react-patterns/SKILL.md +56 -8
- package/skills/cometchat-react-push/SKILL.md +448 -0
- package/skills/cometchat-react-router-patterns/SKILL.md +1 -1
- package/skills/cometchat-react-testing/SKILL.md +404 -0
- package/skills/cometchat-theming/SKILL.md +3 -3
- package/skills/cometchat-troubleshooting/SKILL.md +2 -2
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Server-side push payloads for RN calls
|
|
2
|
+
|
|
3
|
+
The RN client only handles received pushes. The server side — building and sending the payloads — is yours to implement. This reference is a copy-paste-ready Node.js example the skill points users at.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## What the server has to do
|
|
8
|
+
|
|
9
|
+
1. Subscribe to CometChat's `call.received` webhook (Dashboard → Webhooks)
|
|
10
|
+
2. When fired, look up the receiver UID's stored push tokens (PushKit + FCM)
|
|
11
|
+
3. Send a VoIP-shaped payload to each platform with the right priority + cert
|
|
12
|
+
|
|
13
|
+
Two endpoints, two payload shapes, one webhook. Simple but easy to get wrong.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Express skeleton
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
// server/push-handler.ts
|
|
21
|
+
import express from "express";
|
|
22
|
+
import apn from "node-apn"; // npm i node-apn
|
|
23
|
+
import admin from "firebase-admin"; // npm i firebase-admin
|
|
24
|
+
|
|
25
|
+
const app = express();
|
|
26
|
+
app.use(express.json());
|
|
27
|
+
|
|
28
|
+
// Initialize APNs (PushKit) provider with your VoIP cert
|
|
29
|
+
const voipCert = apn.Provider({
|
|
30
|
+
token: undefined,
|
|
31
|
+
cert: process.env.APNS_VOIP_CERT_PATH, // path to your VoIP .p12 / .pem
|
|
32
|
+
key: process.env.APNS_VOIP_KEY_PATH,
|
|
33
|
+
passphrase: process.env.APNS_VOIP_PASSPHRASE,
|
|
34
|
+
production: process.env.NODE_ENV === "production",
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Initialize Firebase Admin
|
|
38
|
+
admin.initializeApp({
|
|
39
|
+
credential: admin.credential.cert({
|
|
40
|
+
projectId: process.env.FIREBASE_PROJECT_ID!,
|
|
41
|
+
clientEmail: process.env.FIREBASE_CLIENT_EMAIL!,
|
|
42
|
+
privateKey: process.env.FIREBASE_PRIVATE_KEY!.replace(/\\n/g, "\n"),
|
|
43
|
+
}),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// CometChat webhook — fired when a call is initiated
|
|
47
|
+
app.post("/webhook/cometchat/call-received", async (req, res) => {
|
|
48
|
+
const { receiver, sender, sessionId, type } = req.body.data;
|
|
49
|
+
const subs = await getPushSubscriptionsForUid(receiver); // your DB
|
|
50
|
+
|
|
51
|
+
await Promise.allSettled([
|
|
52
|
+
...subs.ios.map((token) => sendIOSVoip(token, { sessionId, callerName: sender.name, callerUid: sender.uid, callType: type })),
|
|
53
|
+
...subs.android.map((token) => sendAndroidVoip(token, { sessionId, callerName: sender.name, callerUid: sender.uid, callType: type })),
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
res.status(200).send({ ok: true });
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
async function sendIOSVoip(token: string, payload: { sessionId: string; callerName: string; callerUid: string; callType: string }) {
|
|
60
|
+
const note = new apn.Notification();
|
|
61
|
+
note.topic = `${process.env.IOS_BUNDLE_ID}.voip`; // CRITICAL: voip topic, not the bundle id
|
|
62
|
+
note.pushType = "voip"; // CRITICAL
|
|
63
|
+
note.priority = 10;
|
|
64
|
+
note.expiry = Math.floor(Date.now() / 1000) + 30; // expire in 30s — call rings or doesn't
|
|
65
|
+
note.payload = {
|
|
66
|
+
"content-available": 1,
|
|
67
|
+
data: payload,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const result = await voipCert.send(note, token);
|
|
71
|
+
if (result.failed.length > 0) {
|
|
72
|
+
console.error("PushKit send failed:", result.failed);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function sendAndroidVoip(token: string, payload: { sessionId: string; callerName: string; callerUid: string; callType: string }) {
|
|
77
|
+
await admin.messaging().send({
|
|
78
|
+
token,
|
|
79
|
+
android: {
|
|
80
|
+
priority: "high", // CRITICAL — without this, FCM may delay
|
|
81
|
+
ttl: 30 * 1000, // 30s TTL
|
|
82
|
+
},
|
|
83
|
+
data: { // CRITICAL — `data`, not `notification`
|
|
84
|
+
type: "incoming_call",
|
|
85
|
+
sessionId: payload.sessionId,
|
|
86
|
+
callerName: payload.callerName,
|
|
87
|
+
callerUid: payload.callerUid,
|
|
88
|
+
callType: payload.callType,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
app.listen(3000);
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The skill writes this file as `server/push.example.ts` next to a README pointing at the env vars + cert paths.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Token registration endpoint
|
|
101
|
+
|
|
102
|
+
The client sends its tokens to your server when the user logs in:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
// Client side
|
|
106
|
+
import VoipPushNotification from "react-native-voip-push-notification";
|
|
107
|
+
import messaging from "@react-native-firebase/messaging";
|
|
108
|
+
|
|
109
|
+
async function registerPushTokens(cometchatUid: string) {
|
|
110
|
+
if (Platform.OS === "ios") {
|
|
111
|
+
VoipPushNotification.addEventListener("register", async (token) => {
|
|
112
|
+
await fetch("/api/push/register", {
|
|
113
|
+
method: "POST",
|
|
114
|
+
headers: { "Content-Type": "application/json" },
|
|
115
|
+
body: JSON.stringify({ uid: cometchatUid, platform: "ios", token }),
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
VoipPushNotification.registerVoipToken();
|
|
119
|
+
} else {
|
|
120
|
+
const token = await messaging().getToken();
|
|
121
|
+
await fetch("/api/push/register", {
|
|
122
|
+
method: "POST",
|
|
123
|
+
headers: { "Content-Type": "application/json" },
|
|
124
|
+
body: JSON.stringify({ uid: cometchatUid, platform: "android", token }),
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Server stores tokens keyed by `(cometchatUid, platform)`. On logout, call `/api/push/unregister` to clean up.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Token rotation
|
|
135
|
+
|
|
136
|
+
Both PushKit and FCM tokens can rotate without warning (OS upgrades, app reinstalls, expirations).
|
|
137
|
+
|
|
138
|
+
- iOS PushKit: handle `didInvalidatePushToken` and re-register
|
|
139
|
+
- FCM: handle `messaging().onTokenRefresh` and re-register
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
// iOS
|
|
143
|
+
VoipPushNotification.addEventListener("didInvalidatePushToken", async () => {
|
|
144
|
+
// wait for next register event, will fire automatically
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Android
|
|
148
|
+
messaging().onTokenRefresh(async (newToken) => {
|
|
149
|
+
await fetch("/api/push/register", {
|
|
150
|
+
method: "POST",
|
|
151
|
+
body: JSON.stringify({ uid: getCurrentUid(), platform: "android", token: newToken }),
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Webhook verification
|
|
159
|
+
|
|
160
|
+
CometChat signs webhooks. Verify the signature before trusting payloads:
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
import crypto from "crypto";
|
|
164
|
+
|
|
165
|
+
app.post("/webhook/cometchat/call-received", (req, res, next) => {
|
|
166
|
+
const signature = req.header("x-cometchat-signature");
|
|
167
|
+
const expected = crypto
|
|
168
|
+
.createHmac("sha256", process.env.COMETCHAT_WEBHOOK_SECRET!)
|
|
169
|
+
.update(JSON.stringify(req.body))
|
|
170
|
+
.digest("hex");
|
|
171
|
+
if (signature !== expected) return res.status(401).send({ error: "invalid signature" });
|
|
172
|
+
next();
|
|
173
|
+
});
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Without this, anyone with your endpoint URL can ring your users.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Rate limits
|
|
181
|
+
|
|
182
|
+
- **APNs:** 4000 notifications/sec per cert. Far above what an app needs for VoIP.
|
|
183
|
+
- **FCM:** 1000 tokens per `send()` call (use `sendMulticast` for batches). Per-token rate limits not enforced strictly.
|
|
184
|
+
|
|
185
|
+
For one-call-at-a-time semantics, you won't hit either. For broadcast scenarios (multi-party "ring everyone"), batch sends instead of looping `send()`.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Testing the server locally
|
|
190
|
+
|
|
191
|
+
ngrok the webhook endpoint to a public URL, register it in CometChat dashboard. Two real devices (one iOS, one Android) on real networks. Trigger a call from device A to device B; watch the server logs for the webhook fire and the push send.
|
|
192
|
+
|
|
193
|
+
For unit tests, mock the apn + firebase-admin modules. Don't try to test real APNs/FCM in CI — certs and rate limits make it hostile.
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Share invite on React Native
|
|
2
|
+
|
|
3
|
+
Same SDK API as web. RN-specific: use the platform's native share sheet via `Share.share()` (built into RN) or `react-native-share` for richer options.
|
|
4
|
+
|
|
5
|
+
**Canonical docs:** https://www.cometchat.com/docs/calls/react-native/share-invite
|
|
6
|
+
**Read first:** `cometchat-react-calls/references/share-invite.md` — deep-link routing rule + dialog UX.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Hard rule: deep-link routing
|
|
11
|
+
|
|
12
|
+
Same rule as web. Configure your app's URL scheme + universal/app links so `https://yourapp.com/call/:sessionId` opens the app:
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
// App.tsx — react-navigation linking config
|
|
16
|
+
const linking = {
|
|
17
|
+
prefixes: ["https://yourapp.com", "yourapp://"],
|
|
18
|
+
config: {
|
|
19
|
+
screens: {
|
|
20
|
+
Call: "call/:sessionId",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
<NavigationContainer linking={linking}>
|
|
26
|
+
{/* ... */}
|
|
27
|
+
</NavigationContainer>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
iOS: configure Associated Domains entitlement.
|
|
31
|
+
Android: add `<intent-filter>` with `android:autoVerify="true"` to `AndroidManifest.xml`.
|
|
32
|
+
|
|
33
|
+
Without these, share-invite URLs land in browser, not the app.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## SDK API
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { CometChatCalls } from "@cometchat/calls-sdk-react-native";
|
|
41
|
+
|
|
42
|
+
const callSettings = new CallSettingsBuilder()
|
|
43
|
+
.setHideShareInviteButton(false)
|
|
44
|
+
.build();
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
const handler = () => shareCallInvite(sessionId);
|
|
48
|
+
CometChatCalls.addEventListener("onShareInviteButtonClicked", handler);
|
|
49
|
+
return () => CometChatCalls.removeEventListener("onShareInviteButtonClicked", handler);
|
|
50
|
+
}, [sessionId]);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Native share sheet
|
|
56
|
+
|
|
57
|
+
RN ships `Share` from `react-native`:
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import { Share, Platform } from "react-native";
|
|
61
|
+
|
|
62
|
+
async function shareCallInvite(sessionId: string) {
|
|
63
|
+
const url = `https://yourapp.com/call/${sessionId}`;
|
|
64
|
+
const message = "I'm on a call — tap to join.";
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const result = await Share.share({
|
|
68
|
+
url: Platform.OS === "ios" ? url : undefined, // iOS prefers `url`
|
|
69
|
+
message: Platform.OS === "android" ? `${message}\n${url}` : message,
|
|
70
|
+
title: "Join my call", // Android only
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (result.action === Share.dismissedAction) {
|
|
74
|
+
// User cancelled — silent
|
|
75
|
+
}
|
|
76
|
+
} catch (err) {
|
|
77
|
+
console.warn("Share failed", err);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`Share.share` opens iOS UIActivityViewController and Android's chooser intent. For both: pass `url` on iOS (separate field), append URL to `message` on Android (the `url` field is iOS-only).
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Clipboard fallback
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
import Clipboard from "@react-native-clipboard/clipboard";
|
|
90
|
+
import { Alert } from "react-native";
|
|
91
|
+
|
|
92
|
+
async function copyLink(url: string) {
|
|
93
|
+
Clipboard.setString(url);
|
|
94
|
+
Alert.alert("Copied", "Call link copied to clipboard");
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Most apps don't need this — `Share.share` always succeeds, even if no targets are available it shows the system "no apps installed" toast. Clipboard is a nice-to-have alongside the share sheet, not a fallback.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## react-native-share for richer UX
|
|
103
|
+
|
|
104
|
+
For per-channel buttons (just WhatsApp, just SMS, etc.):
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
import Share from "react-native-share";
|
|
108
|
+
|
|
109
|
+
await Share.shareSingle({
|
|
110
|
+
social: Share.Social.WHATSAPP,
|
|
111
|
+
message: "Join my call",
|
|
112
|
+
url: callLink,
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Use only if your UX requires it. Native share sheet is usually enough.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Anti-patterns
|
|
121
|
+
|
|
122
|
+
Web sister rules apply, plus RN-specific:
|
|
123
|
+
|
|
124
|
+
1. **Setting `url` on Android Share.share.** Android ignores it; URL doesn't appear in shared content. Concat to `message` instead.
|
|
125
|
+
2. **No deep-link config.** URL opens in browser → web fallback or 404.
|
|
126
|
+
3. **Sharing without `await`.** Caller doesn't know if the user cancelled, success messaging fires for cancelled shares.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Verification checklist
|
|
131
|
+
|
|
132
|
+
- [ ] `react-navigation` `linking` config maps `/call/:sessionId`
|
|
133
|
+
- [ ] Universal links (iOS) + auto-verified app links (Android) configured
|
|
134
|
+
- [ ] `Share.share` URL field passed on iOS, concat'd to message on Android
|
|
135
|
+
- [ ] Listener cleaned up on unmount
|
|
136
|
+
- [ ] Real-device smoke: share via WhatsApp/Messages → tap → opens your app at the call
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Pointers
|
|
141
|
+
|
|
142
|
+
- `cometchat-react-calls/references/share-invite.md` — sister
|
|
143
|
+
- `cometchat-native-calls` SKILL.md
|
|
144
|
+
- Canonical docs: https://www.cometchat.com/docs/calls/react-native/share-invite
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
# VoIP push end-to-end on React Native
|
|
2
|
+
|
|
3
|
+
The hardest piece of RN calls. This reference is the canonical implementation — the SKILL.md sketches it; here is the code.
|
|
4
|
+
|
|
5
|
+
Stack:
|
|
6
|
+
|
|
7
|
+
- **`react-native-callkeep`** — bridges CallKit (iOS) + ConnectionService (Android). One Dart-side API: "ring this device with this caller name."
|
|
8
|
+
- **`react-native-voip-push-notification`** — iOS PushKit token registration and payload delivery
|
|
9
|
+
- **`@react-native-firebase/messaging`** — Android FCM data messages
|
|
10
|
+
- **Server-side** — your push server splits sends: PushKit (VoIP cert) for iOS, FCM with `priority: "high"` and `data` payload for Android
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## iOS — PushKit + CallKit
|
|
15
|
+
|
|
16
|
+
### Install + native config
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install react-native-callkeep react-native-voip-push-notification
|
|
20
|
+
cd ios && pod install && cd ..
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
In Xcode (manual steps the skill documents but cannot automate):
|
|
24
|
+
|
|
25
|
+
1. Capabilities → Background Modes → enable: **Audio, AirPlay, and Picture in Picture** + **Voice over IP** + **Remote notifications**
|
|
26
|
+
2. Capabilities → Push Notifications → ensure enabled
|
|
27
|
+
3. Apple Developer portal → Certificates → create a **VoIP Services** certificate for your bundle ID (separate from the standard APNs cert)
|
|
28
|
+
4. Upload the `.p12` to your push server
|
|
29
|
+
|
|
30
|
+
### Info.plist
|
|
31
|
+
|
|
32
|
+
```xml
|
|
33
|
+
<key>UIBackgroundModes</key>
|
|
34
|
+
<array>
|
|
35
|
+
<string>audio</string>
|
|
36
|
+
<string>voip</string>
|
|
37
|
+
<string>remote-notification</string>
|
|
38
|
+
</array>
|
|
39
|
+
<key>NSMicrophoneUsageDescription</key>
|
|
40
|
+
<string>So you can talk during voice and video calls.</string>
|
|
41
|
+
<key>NSCameraUsageDescription</key>
|
|
42
|
+
<string>So you can be seen during video calls.</string>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Register PushKit + CallKit at app launch
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
// App.tsx
|
|
49
|
+
import { useEffect } from "react";
|
|
50
|
+
import VoipPushNotification from "react-native-voip-push-notification";
|
|
51
|
+
import RNCallKeep from "react-native-callkeep";
|
|
52
|
+
import { Platform } from "react-native";
|
|
53
|
+
|
|
54
|
+
const callKeepOptions = {
|
|
55
|
+
ios: {
|
|
56
|
+
appName: "YourApp",
|
|
57
|
+
supportsVideo: true,
|
|
58
|
+
maximumCallGroups: 1,
|
|
59
|
+
maximumCallsPerCallGroup: 1,
|
|
60
|
+
},
|
|
61
|
+
android: {
|
|
62
|
+
alertTitle: "Permissions required",
|
|
63
|
+
alertDescription: "This app needs to access your phone calling system to ring you on incoming calls",
|
|
64
|
+
cancelButton: "Cancel",
|
|
65
|
+
okButton: "OK",
|
|
66
|
+
foregroundService: {
|
|
67
|
+
channelId: "com.yourapp.calls",
|
|
68
|
+
channelName: "Calls",
|
|
69
|
+
notificationTitle: "Call in progress",
|
|
70
|
+
notificationIcon: "ic_launcher",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export function App() {
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
RNCallKeep.setup(callKeepOptions).then(() => {
|
|
78
|
+
RNCallKeep.setAvailable(true);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
if (Platform.OS === "ios") {
|
|
82
|
+
// Register PushKit listener — iOS will call onRegister with the VoIP token
|
|
83
|
+
VoipPushNotification.addEventListener("register", (token) => {
|
|
84
|
+
// Send this token to your server, keyed by the logged-in CometChat UID
|
|
85
|
+
sendVoipTokenToServer(token);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Incoming VoIP push payload arrives — IMMEDIATELY report to CallKit
|
|
89
|
+
VoipPushNotification.addEventListener("notification", (notification) => {
|
|
90
|
+
const { sessionId, callerName, callerUid } = notification.data;
|
|
91
|
+
// CRITICAL: this MUST happen within ~5s of payload delivery or iOS terminates the app
|
|
92
|
+
RNCallKeep.displayIncomingCall(
|
|
93
|
+
sessionId, // unique call UUID
|
|
94
|
+
callerUid, // handle
|
|
95
|
+
callerName, // localized caller name
|
|
96
|
+
"generic", // handle type
|
|
97
|
+
true, // hasVideo
|
|
98
|
+
);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
VoipPushNotification.registerVoipToken();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (Platform.OS === "android") {
|
|
105
|
+
setupAndroidVoipPush();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return () => {
|
|
109
|
+
VoipPushNotification.removeEventListener("register");
|
|
110
|
+
VoipPushNotification.removeEventListener("notification");
|
|
111
|
+
};
|
|
112
|
+
}, []);
|
|
113
|
+
|
|
114
|
+
// CallKit answer/end events — wire them to CometChat Calls SDK
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
RNCallKeep.addEventListener("answerCall", async ({ callUUID }) => {
|
|
117
|
+
// sessionId === callUUID we passed to displayIncomingCall
|
|
118
|
+
await CometChatCalls.acceptCall(callUUID);
|
|
119
|
+
navigate("OngoingCall", { sessionId: callUUID });
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
RNCallKeep.addEventListener("endCall", async ({ callUUID }) => {
|
|
123
|
+
await CometChatCalls.leaveSession();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
return () => {
|
|
127
|
+
RNCallKeep.removeEventListener("answerCall");
|
|
128
|
+
RNCallKeep.removeEventListener("endCall");
|
|
129
|
+
};
|
|
130
|
+
}, []);
|
|
131
|
+
|
|
132
|
+
return /* ...your nav... */;
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**5-second rule:** Apple terminates apps that delay `RNCallKeep.displayIncomingCall` more than ~5 seconds after PushKit delivery. Do not `await` async work before the call — display first, then do the rest from inside the answer handler.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Android — FCM data messages + ConnectionService
|
|
141
|
+
|
|
142
|
+
### Install + native config
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
npm install @react-native-firebase/app @react-native-firebase/messaging
|
|
146
|
+
cd ios && pod install && cd .. # iOS link for firebase even if you don't use iOS messaging
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Place `google-services.json` (downloaded from Firebase console) at `android/app/google-services.json`.
|
|
150
|
+
|
|
151
|
+
In `android/build.gradle`:
|
|
152
|
+
|
|
153
|
+
```gradle
|
|
154
|
+
buildscript {
|
|
155
|
+
dependencies {
|
|
156
|
+
classpath 'com.google.gms:google-services:4.4.0'
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
In `android/app/build.gradle`:
|
|
162
|
+
|
|
163
|
+
```gradle
|
|
164
|
+
apply plugin: 'com.google.gms.google-services'
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### AndroidManifest.xml
|
|
168
|
+
|
|
169
|
+
```xml
|
|
170
|
+
<uses-permission android:name="android.permission.INTERNET" />
|
|
171
|
+
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
|
172
|
+
<uses-permission android:name="android.permission.CAMERA" />
|
|
173
|
+
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
|
174
|
+
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
|
175
|
+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
176
|
+
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
|
|
177
|
+
<uses-permission android:name="android.permission.BIND_TELECOM_CONNECTION_SERVICE" tools:ignore="ProtectedPermissions" />
|
|
178
|
+
|
|
179
|
+
<!-- Android 14+ -->
|
|
180
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
|
181
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" />
|
|
182
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
|
|
183
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
|
|
184
|
+
|
|
185
|
+
<application>
|
|
186
|
+
<service
|
|
187
|
+
android:name="io.wazo.callkeep.RNCallKeepBackgroundMessagingService"
|
|
188
|
+
android:foregroundServiceType="phoneCall|microphone|camera"
|
|
189
|
+
android:exported="false" />
|
|
190
|
+
</application>
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The `tools` namespace must be declared on `<manifest>`: `xmlns:tools="http://schemas.android.com/tools"`.
|
|
194
|
+
|
|
195
|
+
### Register FCM + handle data messages
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
import messaging from "@react-native-firebase/messaging";
|
|
199
|
+
import RNCallKeep from "react-native-callkeep";
|
|
200
|
+
|
|
201
|
+
async function setupAndroidVoipPush() {
|
|
202
|
+
await messaging().requestPermission();
|
|
203
|
+
const token = await messaging().getToken();
|
|
204
|
+
await sendFcmTokenToServer(token);
|
|
205
|
+
|
|
206
|
+
// Foreground messages — when app is open
|
|
207
|
+
messaging().onMessage(async (remoteMessage) => {
|
|
208
|
+
if (remoteMessage.data?.type === "incoming_call") {
|
|
209
|
+
handleIncomingCallPayload(remoteMessage.data);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Background / killed app — registered in index.js (see below)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function handleIncomingCallPayload(data: Record<string, string>) {
|
|
217
|
+
RNCallKeep.displayIncomingCall(
|
|
218
|
+
data.sessionId,
|
|
219
|
+
data.callerUid,
|
|
220
|
+
data.callerName,
|
|
221
|
+
"generic",
|
|
222
|
+
true,
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Background handler — must be in index.js, NOT a component
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
// index.js (top of file, before AppRegistry.registerComponent)
|
|
231
|
+
import messaging from "@react-native-firebase/messaging";
|
|
232
|
+
import RNCallKeep from "react-native-callkeep";
|
|
233
|
+
|
|
234
|
+
messaging().setBackgroundMessageHandler(async (remoteMessage) => {
|
|
235
|
+
if (remoteMessage.data?.type === "incoming_call") {
|
|
236
|
+
RNCallKeep.displayIncomingCall(
|
|
237
|
+
remoteMessage.data.sessionId,
|
|
238
|
+
remoteMessage.data.callerUid,
|
|
239
|
+
remoteMessage.data.callerName,
|
|
240
|
+
"generic",
|
|
241
|
+
true,
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Background handler must register in `index.js` so it's invoked when the JS engine boots in background-headless mode.
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Server-side push payload shape
|
|
252
|
+
|
|
253
|
+
Your push server gets the call event from CometChat (via webhook) and dispatches:
|
|
254
|
+
|
|
255
|
+
### iOS payload (PushKit — sent via APNs with `apns-push-type: voip`)
|
|
256
|
+
|
|
257
|
+
```json
|
|
258
|
+
{
|
|
259
|
+
"aps": {
|
|
260
|
+
"content-available": 1
|
|
261
|
+
},
|
|
262
|
+
"data": {
|
|
263
|
+
"sessionId": "session-abc-123",
|
|
264
|
+
"callerName": "Alice",
|
|
265
|
+
"callerUid": "cometchat-uid-1",
|
|
266
|
+
"callType": "video"
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Send to the **VoIP-specific** APNs endpoint with the VoIP cert. Standard APNs cert won't work.
|
|
272
|
+
|
|
273
|
+
### Android payload (FCM data message)
|
|
274
|
+
|
|
275
|
+
```json
|
|
276
|
+
{
|
|
277
|
+
"to": "<receiver-fcm-token>",
|
|
278
|
+
"priority": "high",
|
|
279
|
+
"data": {
|
|
280
|
+
"type": "incoming_call",
|
|
281
|
+
"sessionId": "session-abc-123",
|
|
282
|
+
"callerName": "Alice",
|
|
283
|
+
"callerUid": "cometchat-uid-1",
|
|
284
|
+
"callType": "video"
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**`data` not `notification`.** ConnectionService cannot intercept `notification` payloads — they go to the system tray. Only `data` payloads invoke the background handler.
|
|
290
|
+
|
|
291
|
+
`priority: "high"` is required. Without it, FCM may delay delivery by minutes — too slow to ring.
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## Common failure modes
|
|
296
|
+
|
|
297
|
+
| Symptom | Cause | Fix |
|
|
298
|
+
|---|---|---|
|
|
299
|
+
| iOS: incoming call doesn't ring on lock screen | VoIP cert not uploaded to push server, or not matched to bundle ID | Verify cert in Apple Dev portal; rotate if expired |
|
|
300
|
+
| iOS: app crashes ~5s after PushKit delivery | Async work before `displayIncomingCall` | Display first, then do the rest in the answer handler |
|
|
301
|
+
| iOS: PushKit token never registers | Background Modes capability missing | Re-check Xcode capabilities; uninstall + reinstall app to retrigger registration |
|
|
302
|
+
| Android: incoming call shows as a regular notification (not heads-up) | Sent as `notification` instead of `data` | Server must use FCM `data` payload exclusively |
|
|
303
|
+
| Android: incoming call delayed by minutes | `priority` missing or `"normal"` | Server must set `priority: "high"` |
|
|
304
|
+
| Android 14+: app crashes on call start | Wrong `foregroundServiceType` | Must include `phoneCall` (silent crash with `microphone\|camera` only) |
|
|
305
|
+
| Android: ConnectionService not invoked | `MANAGE_OWN_CALLS` permission denied at runtime | Show rationale dialog and re-request |
|
|
306
|
+
| Both: lock-screen UI stuck after call ends | Missing `RNCallKeep.endCall` in hangup path | Add `RNCallKeep.endCall(callUUID)` to the cleanup |
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Testing VoIP push (without a server)
|
|
311
|
+
|
|
312
|
+
The hardest part of dev is iterating without a real push server. Two workarounds:
|
|
313
|
+
|
|
314
|
+
1. **Trigger CallKit directly without push** — call `RNCallKeep.displayIncomingCall(...)` from a debug button in your app. Bypasses the server entirely. Useful for testing the in-app flow.
|
|
315
|
+
2. **Use Firebase Console "Send test message"** — send a one-off FCM `data` payload to a specific token. Does NOT support PushKit — iOS still needs a real VoIP push server.
|
|
316
|
+
|
|
317
|
+
For real end-to-end tests, you need a push server. The skill scaffolds a tiny Express example (`server/push.example.ts`) that the user runs locally; it's documented but not tested in CI because real APNs/FCM requires real certs.
|
|
@@ -3,7 +3,7 @@ name: cometchat-native-components
|
|
|
3
3
|
description: "Component catalog for the CometChat React Native UI Kit v5 — names, props, slot views, request builders, hide flags, style shape. Always loaded before writing CometChat* JSX."
|
|
4
4
|
license: "MIT"
|
|
5
5
|
compatibility: "Node.js >=18; React Native >=0.70; @cometchat/chat-uikit-react-native ^5"
|
|
6
|
-
allowed-tools: "
|
|
6
|
+
allowed-tools: "shell, file-read, file-search, file-list, ask-user"
|
|
7
7
|
metadata:
|
|
8
8
|
author: "CometChat"
|
|
9
9
|
version: "3.0.0"
|
|
@@ -3,7 +3,7 @@ name: cometchat-native-core
|
|
|
3
3
|
description: "Shared rules for CometChat React Native UI Kit v5. Always loaded alongside framework (expo/bare) and placement skills. Read this first."
|
|
4
4
|
license: "MIT"
|
|
5
5
|
compatibility: "Node.js >=18; React Native >=0.70; @cometchat/chat-uikit-react-native ^5; @cometchat/chat-sdk-react-native ^4"
|
|
6
|
-
allowed-tools: "
|
|
6
|
+
allowed-tools: "shell, file-read, file-search, file-list"
|
|
7
7
|
metadata:
|
|
8
8
|
author: "CometChat"
|
|
9
9
|
version: "3.0.0"
|
|
@@ -3,7 +3,7 @@ name: cometchat-native-customization
|
|
|
3
3
|
description: "Customize the CometChat React Native UI Kit without forking — four-tier model: props → request builders → text formatters + message templates → DataSource decorators + event bus."
|
|
4
4
|
license: "MIT"
|
|
5
5
|
compatibility: "Node.js >=18; React Native >=0.70; @cometchat/chat-uikit-react-native ^5"
|
|
6
|
-
allowed-tools: "
|
|
6
|
+
allowed-tools: "shell, file-read, file-search, file-list, ask-user"
|
|
7
7
|
metadata:
|
|
8
8
|
author: "CometChat"
|
|
9
9
|
version: "3.0.0"
|