@cometchat/skills 4.1.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.
Files changed (257) hide show
  1. package/bin/install.js +298 -41
  2. package/package.json +1 -1
  3. package/skills/cometchat/SKILL.md +146 -27
  4. package/skills/cometchat-a11y/SKILL.md +476 -0
  5. package/skills/cometchat-android-v5/SKILL.md +1 -1
  6. package/skills/cometchat-android-v5-calls/SKILL.md +413 -0
  7. package/skills/cometchat-android-v5-calls/references/README.md +53 -0
  8. package/skills/cometchat-android-v5-calls/references/add-calls-to-existing-chat.md +195 -0
  9. package/skills/cometchat-android-v5-calls/references/audio-controls.md +91 -0
  10. package/skills/cometchat-android-v5-calls/references/background-handling.md +168 -0
  11. package/skills/cometchat-android-v5-calls/references/call-layouts.md +123 -0
  12. package/skills/cometchat-android-v5-calls/references/call-logs.md +113 -0
  13. package/skills/cometchat-android-v5-calls/references/call-session.md +274 -0
  14. package/skills/cometchat-android-v5-calls/references/custom-ui.md +150 -0
  15. package/skills/cometchat-android-v5-calls/references/event-listeners.md +123 -0
  16. package/skills/cometchat-android-v5-calls/references/group-calls.md +205 -0
  17. package/skills/cometchat-android-v5-calls/references/idle-timeout.md +111 -0
  18. package/skills/cometchat-android-v5-calls/references/in-call-chat.md +134 -0
  19. package/skills/cometchat-android-v5-calls/references/join-session.md +120 -0
  20. package/skills/cometchat-android-v5-calls/references/migration-v4-to-v5.md +206 -0
  21. package/skills/cometchat-android-v5-calls/references/participant-management.md +110 -0
  22. package/skills/cometchat-android-v5-calls/references/picture-in-picture.md +106 -0
  23. package/skills/cometchat-android-v5-calls/references/raise-hand.md +242 -0
  24. package/skills/cometchat-android-v5-calls/references/recording.md +101 -0
  25. package/skills/cometchat-android-v5-calls/references/ringing-integration.md +152 -0
  26. package/skills/cometchat-android-v5-calls/references/screen-sharing.md +82 -0
  27. package/skills/cometchat-android-v5-calls/references/server-fcm-voip.md +215 -0
  28. package/skills/cometchat-android-v5-calls/references/session-settings.md +121 -0
  29. package/skills/cometchat-android-v5-calls/references/setup.md +137 -0
  30. package/skills/cometchat-android-v5-calls/references/share-invite.md +151 -0
  31. package/skills/cometchat-android-v5-calls/references/video-controls.md +87 -0
  32. package/skills/cometchat-android-v5-calls/references/voip-calling.md +526 -0
  33. package/skills/cometchat-android-v5-components/SKILL.md +1 -1
  34. package/skills/cometchat-android-v5-core/SKILL.md +1 -1
  35. package/skills/cometchat-android-v5-customization/SKILL.md +1 -1
  36. package/skills/cometchat-android-v5-extensions/SKILL.md +1 -1
  37. package/skills/cometchat-android-v5-features/SKILL.md +1 -1
  38. package/skills/cometchat-android-v5-placement/SKILL.md +1 -1
  39. package/skills/cometchat-android-v5-production/SKILL.md +1 -1
  40. package/skills/cometchat-android-v5-push/SKILL.md +1 -1
  41. package/skills/cometchat-android-v5-testing/SKILL.md +1 -1
  42. package/skills/cometchat-android-v5-theming/SKILL.md +1 -1
  43. package/skills/cometchat-android-v5-troubleshooting/SKILL.md +1 -1
  44. package/skills/cometchat-android-v6/SKILL.md +1 -1
  45. package/skills/cometchat-android-v6-builder-settings/SKILL.md +1 -1
  46. package/skills/cometchat-android-v6-calls/SKILL.md +375 -0
  47. package/skills/cometchat-android-v6-calls/references/add-calls-to-existing-chat.md +97 -0
  48. package/skills/cometchat-android-v6-calls/references/call-layouts.md +91 -0
  49. package/skills/cometchat-android-v6-calls/references/call-session.md +163 -0
  50. package/skills/cometchat-android-v6-calls/references/device-management.md +171 -0
  51. package/skills/cometchat-android-v6-calls/references/group-calls.md +191 -0
  52. package/skills/cometchat-android-v6-calls/references/idle-timeout.md +107 -0
  53. package/skills/cometchat-android-v6-calls/references/in-call-chat.md +136 -0
  54. package/skills/cometchat-android-v6-calls/references/raise-hand.md +186 -0
  55. package/skills/cometchat-android-v6-calls/references/ringing-integration.md +176 -0
  56. package/skills/cometchat-android-v6-calls/references/server-fcm-voip.md +127 -0
  57. package/skills/cometchat-android-v6-calls/references/share-invite.md +105 -0
  58. package/skills/cometchat-android-v6-compose-components/SKILL.md +2 -2
  59. package/skills/cometchat-android-v6-compose-customization/SKILL.md +1 -1
  60. package/skills/cometchat-android-v6-compose-placement/SKILL.md +1 -1
  61. package/skills/cometchat-android-v6-compose-theming/SKILL.md +1 -1
  62. package/skills/cometchat-android-v6-core/SKILL.md +1 -1
  63. package/skills/cometchat-android-v6-events/SKILL.md +1 -1
  64. package/skills/cometchat-android-v6-extensions/SKILL.md +1 -1
  65. package/skills/cometchat-android-v6-features/SKILL.md +1 -1
  66. package/skills/cometchat-android-v6-kotlin-components/SKILL.md +1 -1
  67. package/skills/cometchat-android-v6-kotlin-customization/SKILL.md +1 -1
  68. package/skills/cometchat-android-v6-kotlin-placement/SKILL.md +1 -1
  69. package/skills/cometchat-android-v6-kotlin-theming/SKILL.md +1 -1
  70. package/skills/cometchat-android-v6-migration/SKILL.md +398 -0
  71. package/skills/cometchat-android-v6-production/SKILL.md +1 -1
  72. package/skills/cometchat-android-v6-push/SKILL.md +1 -1
  73. package/skills/cometchat-android-v6-testing/SKILL.md +1 -1
  74. package/skills/cometchat-android-v6-troubleshooting/SKILL.md +1 -1
  75. package/skills/cometchat-angular-calls/SKILL.md +511 -0
  76. package/skills/cometchat-angular-calls/references/add-calls-to-existing-chat.md +95 -0
  77. package/skills/cometchat-angular-calls/references/call-layouts.md +106 -0
  78. package/skills/cometchat-angular-calls/references/call-session.md +167 -0
  79. package/skills/cometchat-angular-calls/references/custom-ui.md +231 -0
  80. package/skills/cometchat-angular-calls/references/device-management.md +135 -0
  81. package/skills/cometchat-angular-calls/references/group-calls.md +412 -0
  82. package/skills/cometchat-angular-calls/references/idle-timeout.md +145 -0
  83. package/skills/cometchat-angular-calls/references/in-call-chat.md +148 -0
  84. package/skills/cometchat-angular-calls/references/lazy-loading-pitfalls.md +205 -0
  85. package/skills/cometchat-angular-calls/references/migration-v4-to-v5.md +53 -0
  86. package/skills/cometchat-angular-calls/references/ngzone-and-async-callbacks.md +183 -0
  87. package/skills/cometchat-angular-calls/references/picture-in-picture.md +285 -0
  88. package/skills/cometchat-angular-calls/references/raise-hand.md +167 -0
  89. package/skills/cometchat-angular-calls/references/recording-screen-share.md +267 -0
  90. package/skills/cometchat-angular-calls/references/ringing-integration.md +131 -0
  91. package/skills/cometchat-angular-calls/references/server-web-push-vapid.md +100 -0
  92. package/skills/cometchat-angular-calls/references/share-invite.md +116 -0
  93. package/skills/cometchat-angular-components/SKILL.md +1 -1
  94. package/skills/cometchat-angular-core/SKILL.md +1 -1
  95. package/skills/cometchat-angular-customization/SKILL.md +1 -1
  96. package/skills/cometchat-angular-features/SKILL.md +1 -1
  97. package/skills/cometchat-angular-patterns/SKILL.md +1 -1
  98. package/skills/cometchat-angular-placement/SKILL.md +1 -1
  99. package/skills/cometchat-angular-production/SKILL.md +1 -1
  100. package/skills/cometchat-angular-push/SKILL.md +403 -0
  101. package/skills/cometchat-angular-testing/SKILL.md +333 -0
  102. package/skills/cometchat-angular-theming/SKILL.md +1 -1
  103. package/skills/cometchat-angular-troubleshooting/SKILL.md +1 -1
  104. package/skills/cometchat-astro-patterns/SKILL.md +1 -1
  105. package/skills/cometchat-calls/SKILL.md +358 -0
  106. package/skills/cometchat-calls/references/use-case-broadcast.md +194 -0
  107. package/skills/cometchat-calls/references/use-case-marketplace.md +158 -0
  108. package/skills/cometchat-calls/references/use-case-support.md +190 -0
  109. package/skills/cometchat-calls/references/use-case-team.md +169 -0
  110. package/skills/cometchat-calls/references/use-case-telehealth.md +154 -0
  111. package/skills/cometchat-components/SKILL.md +1 -1
  112. package/skills/cometchat-core/SKILL.md +1 -1
  113. package/skills/cometchat-customization/SKILL.md +2 -2
  114. package/skills/cometchat-features/SKILL.md +2 -2
  115. package/skills/cometchat-flutter-v5/SKILL.md +1 -1
  116. package/skills/cometchat-flutter-v5-calls/SKILL.md +325 -94
  117. package/skills/cometchat-flutter-v5-calls/references/add-calls-to-existing-chat.md +169 -0
  118. package/skills/cometchat-flutter-v5-calls/references/call-layouts.md +98 -0
  119. package/skills/cometchat-flutter-v5-calls/references/call-session.md +330 -0
  120. package/skills/cometchat-flutter-v5-calls/references/device-management.md +120 -0
  121. package/skills/cometchat-flutter-v5-calls/references/group-calls.md +187 -0
  122. package/skills/cometchat-flutter-v5-calls/references/idle-timeout.md +97 -0
  123. package/skills/cometchat-flutter-v5-calls/references/in-call-chat.md +132 -0
  124. package/skills/cometchat-flutter-v5-calls/references/migration-v4-to-v5.md +160 -0
  125. package/skills/cometchat-flutter-v5-calls/references/raise-hand.md +244 -0
  126. package/skills/cometchat-flutter-v5-calls/references/ringing-integration.md +193 -0
  127. package/skills/cometchat-flutter-v5-calls/references/server-push-bridge.md +101 -0
  128. package/skills/cometchat-flutter-v5-calls/references/share-invite.md +142 -0
  129. package/skills/cometchat-flutter-v5-conversations/SKILL.md +1 -1
  130. package/skills/cometchat-flutter-v5-core/SKILL.md +1 -1
  131. package/skills/cometchat-flutter-v5-customization/SKILL.md +1 -1
  132. package/skills/cometchat-flutter-v5-events/SKILL.md +1 -1
  133. package/skills/cometchat-flutter-v5-messages/SKILL.md +1 -1
  134. package/skills/cometchat-flutter-v5-production/SKILL.md +1 -1
  135. package/skills/cometchat-flutter-v5-push/SKILL.md +1 -1
  136. package/skills/cometchat-flutter-v5-testing/SKILL.md +390 -0
  137. package/skills/cometchat-flutter-v5-theming/SKILL.md +1 -1
  138. package/skills/cometchat-flutter-v5-troubleshooting/SKILL.md +1 -1
  139. package/skills/cometchat-flutter-v5-users-groups/SKILL.md +1 -1
  140. package/skills/cometchat-flutter-v6/SKILL.md +1 -1
  141. package/skills/cometchat-flutter-v6-calls/SKILL.md +308 -243
  142. package/skills/cometchat-flutter-v6-calls/references/add-calls-to-existing-chat.md +134 -0
  143. package/skills/cometchat-flutter-v6-calls/references/call-layouts.md +102 -0
  144. package/skills/cometchat-flutter-v6-calls/references/call-session.md +229 -0
  145. package/skills/cometchat-flutter-v6-calls/references/device-management.md +99 -0
  146. package/skills/cometchat-flutter-v6-calls/references/group-calls.md +190 -0
  147. package/skills/cometchat-flutter-v6-calls/references/idle-timeout.md +80 -0
  148. package/skills/cometchat-flutter-v6-calls/references/in-call-chat.md +122 -0
  149. package/skills/cometchat-flutter-v6-calls/references/raise-hand.md +268 -0
  150. package/skills/cometchat-flutter-v6-calls/references/ringing-integration.md +171 -0
  151. package/skills/cometchat-flutter-v6-calls/references/server-push-bridge.md +93 -0
  152. package/skills/cometchat-flutter-v6-calls/references/share-invite.md +102 -0
  153. package/skills/cometchat-flutter-v6-components/SKILL.md +1 -1
  154. package/skills/cometchat-flutter-v6-conversations/SKILL.md +1 -1
  155. package/skills/cometchat-flutter-v6-core/SKILL.md +1 -1
  156. package/skills/cometchat-flutter-v6-customization/SKILL.md +1 -1
  157. package/skills/cometchat-flutter-v6-events/SKILL.md +1 -1
  158. package/skills/cometchat-flutter-v6-features/SKILL.md +1 -1
  159. package/skills/cometchat-flutter-v6-messages/SKILL.md +1 -1
  160. package/skills/cometchat-flutter-v6-migration/SKILL.md +1 -1
  161. package/skills/cometchat-flutter-v6-placement/SKILL.md +1 -1
  162. package/skills/cometchat-flutter-v6-production/SKILL.md +1 -1
  163. package/skills/cometchat-flutter-v6-push/SKILL.md +452 -0
  164. package/skills/cometchat-flutter-v6-testing/SKILL.md +319 -0
  165. package/skills/cometchat-flutter-v6-theming/SKILL.md +1 -1
  166. package/skills/cometchat-flutter-v6-troubleshooting/SKILL.md +1 -1
  167. package/skills/cometchat-flutter-v6-users-groups/SKILL.md +1 -1
  168. package/skills/cometchat-i18n/SKILL.md +358 -0
  169. package/skills/cometchat-ios/SKILL.md +5 -5
  170. package/skills/cometchat-ios-calls/SKILL.md +409 -0
  171. package/skills/cometchat-ios-calls/references/add-calls-to-existing-chat.md +172 -0
  172. package/skills/cometchat-ios-calls/references/avaudiosession-routing.md +189 -0
  173. package/skills/cometchat-ios-calls/references/call-layouts.md +136 -0
  174. package/skills/cometchat-ios-calls/references/call-session.md +221 -0
  175. package/skills/cometchat-ios-calls/references/callkit-and-pushkit.md +353 -0
  176. package/skills/cometchat-ios-calls/references/custom-ui.md +322 -0
  177. package/skills/cometchat-ios-calls/references/device-management.md +171 -0
  178. package/skills/cometchat-ios-calls/references/group-calls.md +396 -0
  179. package/skills/cometchat-ios-calls/references/idle-timeout.md +185 -0
  180. package/skills/cometchat-ios-calls/references/in-call-chat.md +183 -0
  181. package/skills/cometchat-ios-calls/references/migration-v4-to-v5.md +138 -0
  182. package/skills/cometchat-ios-calls/references/picture-in-picture.md +246 -0
  183. package/skills/cometchat-ios-calls/references/raise-hand.md +239 -0
  184. package/skills/cometchat-ios-calls/references/recording-screen-share.md +339 -0
  185. package/skills/cometchat-ios-calls/references/ringing-integration.md +142 -0
  186. package/skills/cometchat-ios-calls/references/server-apns-pushkit.md +267 -0
  187. package/skills/cometchat-ios-calls/references/share-invite.md +152 -0
  188. package/skills/cometchat-ios-calls/references/swiftui-uikit-hosting.md +246 -0
  189. package/skills/cometchat-ios-calls/references/voip-cert-setup.md +166 -0
  190. package/skills/cometchat-ios-components/SKILL.md +1 -1
  191. package/skills/cometchat-ios-core/SKILL.md +1 -1
  192. package/skills/cometchat-ios-customization/SKILL.md +1 -1
  193. package/skills/cometchat-ios-features/SKILL.md +1 -1
  194. package/skills/cometchat-ios-placement/SKILL.md +1 -1
  195. package/skills/cometchat-ios-production/SKILL.md +1 -1
  196. package/skills/cometchat-ios-push/SKILL.md +1 -1
  197. package/skills/cometchat-ios-testing/SKILL.md +371 -0
  198. package/skills/cometchat-ios-theming/SKILL.md +1 -1
  199. package/skills/cometchat-ios-troubleshooting/SKILL.md +1 -1
  200. package/skills/cometchat-native-bare-patterns/SKILL.md +45 -3
  201. package/skills/cometchat-native-calls/SKILL.md +599 -0
  202. package/skills/cometchat-native-calls/references/add-calls-to-existing-chat.md +203 -0
  203. package/skills/cometchat-native-calls/references/call-layouts.md +124 -0
  204. package/skills/cometchat-native-calls/references/call-session.md +182 -0
  205. package/skills/cometchat-native-calls/references/custom-ui.md +250 -0
  206. package/skills/cometchat-native-calls/references/device-management.md +135 -0
  207. package/skills/cometchat-native-calls/references/expo-vs-bare.md +179 -0
  208. package/skills/cometchat-native-calls/references/group-calls.md +291 -0
  209. package/skills/cometchat-native-calls/references/idle-timeout.md +128 -0
  210. package/skills/cometchat-native-calls/references/in-call-chat.md +143 -0
  211. package/skills/cometchat-native-calls/references/migration-v4-to-v5.md +72 -0
  212. package/skills/cometchat-native-calls/references/picture-in-picture.md +276 -0
  213. package/skills/cometchat-native-calls/references/raise-hand.md +203 -0
  214. package/skills/cometchat-native-calls/references/recording-screen-share.md +290 -0
  215. package/skills/cometchat-native-calls/references/ringing-integration.md +118 -0
  216. package/skills/cometchat-native-calls/references/server-push-bridge.md +135 -0
  217. package/skills/cometchat-native-calls/references/server-push-payloads.md +193 -0
  218. package/skills/cometchat-native-calls/references/share-invite.md +144 -0
  219. package/skills/cometchat-native-calls/references/voip-push-end-to-end.md +317 -0
  220. package/skills/cometchat-native-components/SKILL.md +1 -1
  221. package/skills/cometchat-native-core/SKILL.md +1 -1
  222. package/skills/cometchat-native-customization/SKILL.md +1 -1
  223. package/skills/cometchat-native-expo-patterns/SKILL.md +18 -1
  224. package/skills/cometchat-native-features/SKILL.md +1 -1
  225. package/skills/cometchat-native-placement/SKILL.md +1 -1
  226. package/skills/cometchat-native-production/SKILL.md +1 -1
  227. package/skills/cometchat-native-push/SKILL.md +1 -1
  228. package/skills/cometchat-native-testing/SKILL.md +1 -1
  229. package/skills/cometchat-native-theming/SKILL.md +1 -1
  230. package/skills/cometchat-native-troubleshooting/SKILL.md +1 -1
  231. package/skills/cometchat-nextjs-patterns/SKILL.md +1 -1
  232. package/skills/cometchat-placement/SKILL.md +1 -1
  233. package/skills/cometchat-production/SKILL.md +1 -1
  234. package/skills/cometchat-react-calls/SKILL.md +539 -0
  235. package/skills/cometchat-react-calls/references/add-calls-to-existing-chat.md +145 -0
  236. package/skills/cometchat-react-calls/references/call-layouts.md +161 -0
  237. package/skills/cometchat-react-calls/references/call-session.md +235 -0
  238. package/skills/cometchat-react-calls/references/custom-ui.md +201 -0
  239. package/skills/cometchat-react-calls/references/device-management.md +206 -0
  240. package/skills/cometchat-react-calls/references/group-calls.md +262 -0
  241. package/skills/cometchat-react-calls/references/idle-timeout.md +175 -0
  242. package/skills/cometchat-react-calls/references/in-call-chat.md +212 -0
  243. package/skills/cometchat-react-calls/references/migration-v4-to-v5.md +172 -0
  244. package/skills/cometchat-react-calls/references/picture-in-picture.md +245 -0
  245. package/skills/cometchat-react-calls/references/raise-hand.md +238 -0
  246. package/skills/cometchat-react-calls/references/recording-screen-share.md +143 -0
  247. package/skills/cometchat-react-calls/references/ringing-integration.md +255 -0
  248. package/skills/cometchat-react-calls/references/server-web-push-vapid.md +241 -0
  249. package/skills/cometchat-react-calls/references/share-invite.md +176 -0
  250. package/skills/cometchat-react-calls/references/testing-calls-on-web.md +198 -0
  251. package/skills/cometchat-react-calls/references/voip-and-web-push.md +165 -0
  252. package/skills/cometchat-react-patterns/SKILL.md +1 -1
  253. package/skills/cometchat-react-push/SKILL.md +448 -0
  254. package/skills/cometchat-react-router-patterns/SKILL.md +1 -1
  255. package/skills/cometchat-react-testing/SKILL.md +404 -0
  256. package/skills/cometchat-theming/SKILL.md +3 -3
  257. package/skills/cometchat-troubleshooting/SKILL.md +2 -2
@@ -0,0 +1,255 @@
1
+ # Ringing — call signaling with custom UI (web)
2
+
3
+ The Ringing flow uses the **Chat SDK's call entity** for signaling (initiate / ring / accept / reject / cancel) and the **Calls SDK** for the actual WebRTC session. This is the right pattern when you want a custom incoming-call UI on top of CometChat's signaling — not the kit's prebuilt `CometChatIncomingCall`.
4
+
5
+ **Three calling modes — pick the right one:**
6
+
7
+ | Mode | Driver | When to use |
8
+ |---|---|---|
9
+ | **Standard** | UI Kit (`CometChatCallButtons` + `CometChatIncomingCall`) | 80% case — chat-driven calls with prebuilt UI |
10
+ | **Ringing (this doc)** | Chat SDK call entity + Calls SDK session | Custom incoming/outgoing call UI on top of CometChat signaling |
11
+ | **Call Session** (see `call-session.md`) | Calls SDK `joinSession` directly | Meeting-room URLs, scheduled calls, no ringing |
12
+
13
+ **Canonical docs:** https://www.cometchat.com/docs/calls/javascript/ringing
14
+
15
+ ---
16
+
17
+ ## Architecture
18
+
19
+ ```
20
+ Caller CometChat Recipient
21
+ │ │ │
22
+ │ CometChat.initiateCall(call) │ │
23
+ ├────────────────────────────────────>│ │
24
+ │ │ onIncomingCallReceived │
25
+ │ ├───────────────────────────────>│
26
+ │ │ │
27
+ │ │ CometChat.acceptCall(sid) │
28
+ │ │<───────────────────────────────│
29
+ │ onOutgoingCallAccepted │ │
30
+ │<────────────────────────────────────┤ │
31
+ │ │ │
32
+ │ CometChatCalls.joinSession(token) │ CometChatCalls.joinSession(token)
33
+ ├────────────────────────────────────>│<───────────────────────────────│
34
+ │ ───── WebRTC session active ───── │
35
+ ```
36
+
37
+ The Chat SDK is the signaling channel; the Calls SDK is the media channel. Same sessionId binds them.
38
+
39
+ ---
40
+
41
+ ## Hard rules
42
+
43
+ 1. **Both SDKs must be initialized** — `CometChat.init` for signaling, `CometChatCalls.init` for media. Sequential, in that order.
44
+ 2. **`addCallListener` must be live BEFORE you start receiving calls.** Add it on app start, after login. Otherwise incoming-call events are missed.
45
+ 3. **`CometChatCalls.joinSession` happens AFTER `CometChat.acceptCall` resolves** — accepting tells the backend you're ready; joining the session enters WebRTC. Reversing this fires media before the chat-side state agrees.
46
+ 4. **End the call on BOTH SDKs.** `CometChatCalls.leaveSession()` for media, `CometChat.endCall(sessionId)` for the chat-side call record. Skipping either leaves a zombie.
47
+ 5. **Listen for `onCallEndedMessageReceived`** for the case where the OTHER party ended. Without it, your UI keeps showing "in-call" while the session is dead.
48
+ 6. **Custom timeout (default 45s)** — `CometChat.initiateCall(call, 60)` sets ringing duration. After timeout, the backend auto-cancels and the recipient stops seeing the call.
49
+
50
+ ---
51
+
52
+ ## Initiate (caller side)
53
+
54
+ ```ts
55
+ import { CometChat } from "@cometchat/chat-sdk-javascript";
56
+
57
+ async function initiateCall(receiverUid: string, callType: "audio" | "video" = "video") {
58
+ const call = new CometChat.Call(
59
+ receiverUid,
60
+ callType === "video" ? CometChat.CALL_TYPE.VIDEO : CometChat.CALL_TYPE.AUDIO,
61
+ CometChat.RECEIVER_TYPE.USER,
62
+ );
63
+ // Optional: 60-second ring timeout (default 45)
64
+ const outgoingCall = await CometChat.initiateCall(call, 60);
65
+ showOutgoingCallScreen(outgoingCall);
66
+ return outgoingCall;
67
+ }
68
+ ```
69
+
70
+ For **group calls**, change `RECEIVER_TYPE` to `CometChat.RECEIVER_TYPE.GROUP` and use the group GUID. Only group members get the ring.
71
+
72
+ ---
73
+
74
+ ## Listen for events (recipient + caller)
75
+
76
+ ```tsx
77
+ import { useEffect } from "react";
78
+
79
+ const LISTENER_ID = "app-call-listener";
80
+
81
+ export function useCometChatCallListener() {
82
+ useEffect(() => {
83
+ CometChat.addCallListener(
84
+ LISTENER_ID,
85
+ new CometChat.CallListener({
86
+ onIncomingCallReceived: (call) => {
87
+ // Show your custom incoming-call UI
88
+ showIncomingCallScreen(call);
89
+ },
90
+ onOutgoingCallAccepted: (acceptedCall) => {
91
+ // Other party accepted — start the session
92
+ hideOutgoingCallScreen();
93
+ startCallSession(acceptedCall.getSessionId());
94
+ },
95
+ onOutgoingCallRejected: (rejectedCall) => {
96
+ hideOutgoingCallScreen();
97
+ // Optional: toast "User declined"
98
+ },
99
+ onIncomingCallCancelled: (cancelledCall) => {
100
+ // Caller hung up before you accepted
101
+ hideIncomingCallScreen();
102
+ },
103
+ onCallEndedMessageReceived: (endedCall) => {
104
+ // Other party ended the active session
105
+ teardownCallUI();
106
+ },
107
+ }),
108
+ );
109
+ return () => CometChat.removeCallListener(LISTENER_ID);
110
+ }, []);
111
+ }
112
+ ```
113
+
114
+ Mount this hook at app root (e.g. in `App.tsx`) so listeners survive route changes — the same reasoning as for `<CometChatIncomingCall />`.
115
+
116
+ ---
117
+
118
+ ## Accept (recipient side)
119
+
120
+ ```ts
121
+ async function acceptIncomingCall(call: CometChat.Call) {
122
+ const accepted = await CometChat.acceptCall(call.getSessionId());
123
+ hideIncomingCallScreen();
124
+ startCallSession(accepted.getSessionId());
125
+ }
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Reject (recipient side)
131
+
132
+ ```ts
133
+ async function rejectIncomingCall(call: CometChat.Call) {
134
+ await CometChat.rejectCall(
135
+ call.getSessionId(),
136
+ CometChat.CALL_STATUS.REJECTED,
137
+ );
138
+ hideIncomingCallScreen();
139
+ }
140
+ ```
141
+
142
+ ---
143
+
144
+ ## Cancel outgoing (caller side, before recipient answers)
145
+
146
+ ```ts
147
+ async function cancelOutgoingCall(call: CometChat.Call) {
148
+ await CometChat.rejectCall(
149
+ call.getSessionId(),
150
+ CometChat.CALL_STATUS.CANCELLED,
151
+ );
152
+ hideOutgoingCallScreen();
153
+ }
154
+ ```
155
+
156
+ `rejectCall` is the same API for both recipient-rejecting and caller-cancelling — the `CALL_STATUS` enum distinguishes intent.
157
+
158
+ ---
159
+
160
+ ## Start the session (both parties, after accept)
161
+
162
+ ```tsx
163
+ async function startCallSession(sessionId: string) {
164
+ const container = document.getElementById("call-container")!;
165
+ const tokenResult = await CometChatCalls.generateToken(sessionId);
166
+
167
+ await CometChatCalls.joinSession(
168
+ tokenResult.token,
169
+ { sessionType: "VIDEO", layout: "TILE" },
170
+ container,
171
+ );
172
+
173
+ // Listen for session end (you ended it OR connection lost)
174
+ const unsub = CometChatCalls.addEventListener("onSessionLeft", () => {
175
+ endCall(sessionId);
176
+ unsub();
177
+ });
178
+ }
179
+ ```
180
+
181
+ The container element must exist in the DOM **before** calling `joinSession`. In React, render a div with `id="call-container"` inside your call screen component, and call `startCallSession` from `useEffect`.
182
+
183
+ ---
184
+
185
+ ## End the call
186
+
187
+ ```ts
188
+ async function endCall(sessionId: string) {
189
+ CometChatCalls.leaveSession();
190
+ await CometChat.endCall(sessionId);
191
+ teardownCallUI();
192
+ }
193
+ ```
194
+
195
+ ---
196
+
197
+ ## Custom incoming-call UI
198
+
199
+ ```tsx
200
+ function IncomingCallScreen({ call, onAccept, onReject }: Props) {
201
+ const caller = call.getCallInitiator();
202
+ return (
203
+ <div role="alertdialog" aria-labelledby="incoming-title" className="incoming-call-overlay">
204
+ <div className="card">
205
+ <img src={caller.getAvatar()} alt="" />
206
+ <h3 id="incoming-title">{caller.getName()}</h3>
207
+ <p>Incoming {call.getType()} call</p>
208
+ <div className="actions">
209
+ <button onClick={onReject} className="reject" aria-label="Decline call">Decline</button>
210
+ <button onClick={onAccept} className="accept" aria-label="Accept call">Accept</button>
211
+ </div>
212
+ </div>
213
+ <audio src="/sounds/ringtone.mp3" autoPlay loop />
214
+ </div>
215
+ );
216
+ }
217
+ ```
218
+
219
+ `role="alertdialog"` is the right ARIA role — interrupting modal that requires user action. `<audio autoPlay loop>` plays ringtone; pause it on accept/reject in the parent.
220
+
221
+ ---
222
+
223
+ ## Anti-patterns
224
+
225
+ 1. **`addCallListener` inside the chat route component.** Listener tears down on route change → calls received while user is on home screen are silently dropped.
226
+ 2. **Calling `joinSession` before `acceptCall` resolves.** Race — recipient enters WebRTC before backend marks the call as "ongoing." Caller's `onOutgoingCallAccepted` may never fire.
227
+ 3. **Forgetting to `removeCallListener` on unmount.** Multiple listeners → events fire N times. Causes "double accept" toasts.
228
+ 4. **No `onCallEndedMessageReceived` handler.** The other party ends the call → your UI stays "in-call" → user has to refresh.
229
+ 5. **Using `endCall` on the chat SDK without `leaveSession` on the calls SDK.** Camera light stays on; mic stays hot. The flex-shrink trap of calls.
230
+ 6. **Mounting `CometChatIncomingCall` (kit) AND custom listener AT THE SAME TIME.** Both fire — you get duplicate incoming-call UIs. Pick one mode per app.
231
+ 7. **`CometChat.initiateCall` without timeout when phone-ringing UX is the goal.** Default 45s is fine; if your UX rings longer, override explicitly so the recipient sees consistent UI.
232
+
233
+ ---
234
+
235
+ ## Verification checklist
236
+
237
+ - [ ] Both SDKs initialized in order (`CometChat.init` → `CometChatCalls.init`)
238
+ - [ ] `addCallListener` registered at app root (survives route changes)
239
+ - [ ] `removeCallListener` on unmount
240
+ - [ ] `acceptCall` precedes `joinSession`
241
+ - [ ] `endCall` (chat) AND `leaveSession` (calls) both fire on hangup
242
+ - [ ] `onCallEndedMessageReceived` tears down UI when other party ends
243
+ - [ ] `onIncomingCallCancelled` tears down UI when caller cancels mid-ring
244
+ - [ ] Custom UI: `role="alertdialog"`, ringtone audio, accept/decline buttons with aria-labels
245
+ - [ ] Smoke: 2 tabs, caller initiates → recipient sees custom incoming UI → accept → both join session → either ends → both UIs dismiss
246
+
247
+ ---
248
+
249
+ ## Pointers
250
+
251
+ - `cometchat-react-calls/SKILL.md` — architecture + seven hard rules
252
+ - `cometchat-react-calls/references/call-session.md` — Mode 3 (joinSession with no ringing)
253
+ - `cometchat-react-calls/references/in-call-chat.md` — chat panel during a call
254
+ - `cometchat-react-calls/references/recording.md` — recording mid-call
255
+ - Canonical docs: https://www.cometchat.com/docs/calls/javascript/ringing
@@ -0,0 +1,241 @@
1
+ # Server-side Web Push VAPID for browser ringing
2
+
3
+ Browsers don't have a true VoIP-push equivalent (the way iOS PushKit + Android FCM data-messages do). But **Web Push** with VAPID is the closest analog: when a CometChat call event fires, your server sends a push, the browser's Service Worker receives it (even with the tab closed), and posts a notification + plays a ringtone.
4
+
5
+ **Caveat — set expectations:** Web Push is best-effort. It works reliably when:
6
+ - The user has the tab open OR a recent installed PWA
7
+ - The browser is running (Chrome/Edge/Firefox running but minimized → ✓)
8
+ - Battery saver isn't aggressive
9
+
10
+ It does NOT work when:
11
+ - The user closed all browser windows
12
+ - iOS Safari except in PWA-only mode (16.4+)
13
+ - The user denied notification permission
14
+
15
+ For production-grade ringing on the web, recommend customers offer the iOS/Android app for unreliable-recipient scenarios.
16
+
17
+ **Canonical docs:** https://datatracker.ietf.org/doc/html/rfc8030 (Web Push), https://datatracker.ietf.org/doc/html/rfc8292 (VAPID), https://developer.mozilla.org/en-US/docs/Web/API/Push_API
18
+
19
+ ---
20
+
21
+ ## Hard rules
22
+
23
+ 1. **VAPID keys are mandatory.** Browsers will reject pushes without them since 2018.
24
+ 2. **Use the `web-push` library or its equivalents** — implementing VAPID JWT + AES128GCM payload encryption from scratch is error-prone.
25
+ 3. **HTTPS required.** Web Push doesn't work on HTTP except localhost.
26
+ 4. **Subscriptions expire.** Browsers rotate `endpoint` URLs every ~3 months on Chrome desktop. Implement re-subscription on push failure (410 Gone).
27
+ 5. **TTL ≤ 60s for VoIP-style ringing.** A push that arrives 5 minutes late is worse than no push.
28
+ 6. **Topic must be set** so a fresh ringing push displaces an older queued one (avoid stacking).
29
+
30
+ ---
31
+
32
+ ## Generate VAPID keys (one-time)
33
+
34
+ ```bash
35
+ npx web-push generate-vapid-keys
36
+ # Output:
37
+ # Public Key: BNa... (paste into your client code)
38
+ # Private Key: 3kL... (server-side env var only)
39
+ ```
40
+
41
+ Or via Node API:
42
+
43
+ ```js
44
+ import webpush from "web-push";
45
+ const keys = webpush.generateVAPIDKeys();
46
+ console.log("VAPID_PUBLIC_KEY=", keys.publicKey);
47
+ console.log("VAPID_PRIVATE_KEY=", keys.privateKey);
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Node.js template
53
+
54
+ ```js
55
+ // npm install web-push
56
+ import webpush from "web-push";
57
+
58
+ webpush.setVapidDetails(
59
+ `mailto:${process.env.VAPID_CONTACT_EMAIL}`,
60
+ process.env.VAPID_PUBLIC_KEY,
61
+ process.env.VAPID_PRIVATE_KEY,
62
+ );
63
+
64
+ export async function sendWebVoipPush(opts) {
65
+ const { recipientUid, sessionId, callerName, callerUid, callType } = opts;
66
+
67
+ // A user may have multiple subscriptions (laptop + work computer); fan out.
68
+ const subscriptions = await db.getWebPushSubscriptions(recipientUid);
69
+
70
+ const payload = JSON.stringify({
71
+ type: "incoming_call",
72
+ sessionId,
73
+ callerName,
74
+ callerUid,
75
+ callType,
76
+ timestamp: Date.now(),
77
+ });
78
+
79
+ await Promise.allSettled(
80
+ subscriptions.map(async (sub) => {
81
+ try {
82
+ await webpush.sendNotification(sub, payload, {
83
+ TTL: 30, // Hard Rule 5
84
+ urgency: "high", // urgency header
85
+ topic: `call-${sessionId}`.slice(0, 32), // Hard Rule 6 — replaces older with same topic
86
+ });
87
+ } catch (err) {
88
+ if (err.statusCode === 410 || err.statusCode === 404) {
89
+ // Endpoint dead — purge from DB
90
+ await db.removeWebPushSubscription(recipientUid, sub.endpoint);
91
+ return;
92
+ }
93
+ console.error("Web push failed", err);
94
+ }
95
+ }),
96
+ );
97
+ }
98
+ ```
99
+
100
+ ---
101
+
102
+ ## Python template (pywebpush)
103
+
104
+ ```python
105
+ # pip install pywebpush
106
+ from pywebpush import webpush, WebPushException
107
+ import json, os
108
+
109
+ VAPID_CLAIMS = {"sub": f"mailto:{os.environ['VAPID_CONTACT_EMAIL']}"}
110
+ VAPID_PRIVATE_KEY = os.environ["VAPID_PRIVATE_KEY"]
111
+
112
+ def send_web_voip_push(subscription_info: dict, payload: dict):
113
+ try:
114
+ webpush(
115
+ subscription_info=subscription_info,
116
+ data=json.dumps(payload),
117
+ vapid_private_key=VAPID_PRIVATE_KEY,
118
+ vapid_claims=VAPID_CLAIMS,
119
+ ttl=30,
120
+ headers={
121
+ "Urgency": "high",
122
+ "Topic": f"call-{payload['sessionId']}"[:32],
123
+ },
124
+ )
125
+ except WebPushException as ex:
126
+ if ex.response and ex.response.status_code in (404, 410):
127
+ return "expired"
128
+ raise
129
+ ```
130
+
131
+ ---
132
+
133
+ ## Receiver: Service Worker
134
+
135
+ `/public/sw.js`:
136
+
137
+ ```js
138
+ self.addEventListener("push", (event) => {
139
+ if (!event.data) return;
140
+ const data = event.data.json();
141
+ if (data.type !== "incoming_call") return;
142
+
143
+ event.waitUntil((async () => {
144
+ // 1. Show notification (must happen synchronously in `push` event,
145
+ // otherwise browser shows "[App] is sending notifications in the background"
146
+ // indicator and may unsubscribe you).
147
+ await self.registration.showNotification(`Incoming call from ${data.callerName}`, {
148
+ body: data.callType === "video" ? "Video call" : "Audio call",
149
+ tag: `call-${data.sessionId}`,
150
+ icon: "/icons/incoming-call-192.png",
151
+ badge: "/icons/badge-72.png",
152
+ requireInteraction: true, // stays until user acts
153
+ silent: false, // play default ringtone (some OSes ignore this for browser pushes)
154
+ vibrate: [500, 200, 500], // mobile haptic
155
+ actions: [
156
+ { action: "accept", title: "Accept" },
157
+ { action: "reject", title: "Decline" },
158
+ ],
159
+ data: {
160
+ sessionId: data.sessionId,
161
+ callerName: data.callerName,
162
+ callerUid: data.callerUid,
163
+ callType: data.callType,
164
+ },
165
+ });
166
+
167
+ // 2. Wake any open clients so the in-app ringer can fire too
168
+ const clients = await self.clients.matchAll({ type: "window", includeUncontrolled: true });
169
+ clients.forEach((client) => client.postMessage({ type: "incoming_call", ...data }));
170
+ })());
171
+ });
172
+
173
+ self.addEventListener("notificationclick", (event) => {
174
+ const { sessionId } = event.notification.data;
175
+ event.notification.close();
176
+ const url = `/call/${sessionId}?action=${event.action || "open"}`;
177
+
178
+ event.waitUntil((async () => {
179
+ const clients = await self.clients.matchAll({ type: "window" });
180
+ const existing = clients.find((c) => c.url.includes(`/call/${sessionId}`));
181
+ if (existing) {
182
+ return existing.focus();
183
+ }
184
+ return self.clients.openWindow(url);
185
+ })());
186
+ });
187
+ ```
188
+
189
+ Register in `main.tsx`:
190
+
191
+ ```tsx
192
+ useEffect(() => {
193
+ if (!("serviceWorker" in navigator)) return;
194
+ navigator.serviceWorker.register("/sw.js")
195
+ .then(async (reg) => {
196
+ const sub = await reg.pushManager.subscribe({
197
+ userVisibleOnly: true,
198
+ applicationServerKey: urlBase64ToUint8Array(import.meta.env.VITE_VAPID_PUBLIC_KEY),
199
+ });
200
+ await api.registerWebPushSubscription(currentUserUid, sub.toJSON());
201
+ })
202
+ .catch((err) => console.warn("SW register / push subscribe failed:", err));
203
+ }, []);
204
+ ```
205
+
206
+ ---
207
+
208
+ ## Anti-patterns
209
+
210
+ 1. **Embedding VAPID private key in client code.** Push spam by anyone who views source. Server-only.
211
+ 2. **Skipping `requireInteraction: true`.** Notification disappears in 5s on Chrome desktop — call seems to "ring once" then vanish.
212
+ 3. **No `tag` on the notification.** Multiple incoming-call notifications stack.
213
+ 4. **TTL: 0.** Notification dies the instant the device is offline. Use 30s minimum.
214
+ 5. **`silent: true`.** Browser doesn't ring, only shows the visual notification.
215
+ 6. **Forgetting to handle `notificationclick`.** User taps notification → nothing happens.
216
+ 7. **Implementing Web Push for iOS Safari without checking PWA-only mode.** iOS Safari requires the user to "Add to Home Screen" first — Web Push doesn't work in regular tabs.
217
+
218
+ ---
219
+
220
+ ## Verification checklist
221
+
222
+ - [ ] VAPID keys generated; private key in env var
223
+ - [ ] HTTPS (or localhost for dev)
224
+ - [ ] Subscription stored server-side keyed to user UID
225
+ - [ ] `topic` set so newer push replaces older
226
+ - [ ] `requireInteraction: true` + `tag` set on notification
227
+ - [ ] 410/404 responses purge dead subscriptions
228
+ - [ ] `notificationclick` opens a call route
229
+ - [ ] Smoke: 2 browsers, recipient minimizes window, caller dials, recipient sees notification + ringtone
230
+
231
+ ---
232
+
233
+ ## Pointers
234
+
235
+ - `cometchat-react-calls/SKILL.md` — calls architecture
236
+ - `cometchat-react-push/SKILL.md` — chat web push (sister, different payload)
237
+ - `cometchat-ios-calls/references/server-apns-pushkit.md` — iOS sibling
238
+ - `cometchat-android-v5-calls/references/server-fcm-voip.md` — Android sibling
239
+ - Web Push spec (RFC 8030): https://datatracker.ietf.org/doc/html/rfc8030
240
+ - VAPID (RFC 8292): https://datatracker.ietf.org/doc/html/rfc8292
241
+ - web-push npm: https://www.npmjs.com/package/web-push
@@ -0,0 +1,176 @@
1
+ # Share invite on web (React)
2
+
3
+ Let participants share the call link with others. The kit ships a share-invite button (hidden by default); your app intercepts the click and runs the platform's native share UI.
4
+
5
+ **Canonical docs:** https://www.cometchat.com/docs/calls/javascript/share-invite
6
+
7
+ ---
8
+
9
+ ## Hard rule: deep-link routing must work BEFORE you ship share-invite
10
+
11
+ A share button that copies a URL nobody can open is worse than no share button. Verify your app's `/call/:sessionId` route handles all four states:
12
+
13
+ 1. **User logged in, in-app** → join call directly
14
+ 2. **User logged in, fresh tab** → restore session, join call
15
+ 3. **User logged out** → login, then redirect to call
16
+ 4. **User doesn't have account** → signup flow with `?invite=...` query param
17
+
18
+ Build the deep-link route first, then turn on the share button.
19
+
20
+ ---
21
+
22
+ ## Show the kit's share button
23
+
24
+ ```tsx
25
+ const callSettings = {
26
+ hideShareInviteButton: false,
27
+ // ... rest
28
+ };
29
+ ```
30
+
31
+ ---
32
+
33
+ ## Handle the click
34
+
35
+ ```tsx
36
+ useEffect(() => {
37
+ const handler = () => shareCallInvite(sessionId);
38
+ CometChatCalls.addEventListener("onShareInviteButtonClicked", handler);
39
+ return () => {
40
+ CometChatCalls.removeEventListener("onShareInviteButtonClicked", handler);
41
+ };
42
+ }, [sessionId]);
43
+ ```
44
+
45
+ ---
46
+
47
+ ## Native Web Share API + clipboard fallback
48
+
49
+ ```tsx
50
+ async function shareCallInvite(sessionId: string) {
51
+ const url = `https://yourapp.com/call/${sessionId}`;
52
+ const shareData = {
53
+ title: "Join my call",
54
+ text: "I'm on a call — tap to join.",
55
+ url,
56
+ };
57
+
58
+ if (navigator.share && navigator.canShare?.(shareData)) {
59
+ try {
60
+ await navigator.share(shareData);
61
+ } catch (err) {
62
+ // User cancelled — that's fine, no toast needed
63
+ if ((err as Error).name !== "AbortError") {
64
+ console.warn("Share failed", err);
65
+ }
66
+ }
67
+ return;
68
+ }
69
+
70
+ // Fallback: clipboard
71
+ try {
72
+ await navigator.clipboard.writeText(url);
73
+ showToast("Link copied to clipboard");
74
+ } catch {
75
+ // Clipboard API requires user gesture + secure context (https) — must always have a manual fallback
76
+ promptManualCopy(url);
77
+ }
78
+ }
79
+ ```
80
+
81
+ `navigator.share` is iOS Safari + Android Chrome + recent desktop Chrome. `navigator.clipboard.writeText` is everywhere modern but **only works in secure contexts (https)** — localhost is fine; non-https staging hosts will throw.
82
+
83
+ ---
84
+
85
+ ## Custom share dialog (when you want more control)
86
+
87
+ ```tsx
88
+ function ShareDialog({ sessionId, onClose }: Props) {
89
+ const url = `https://yourapp.com/call/${sessionId}`;
90
+ return (
91
+ <dialog open className="share-dialog" role="dialog" aria-label="Share call invite">
92
+ <button onClick={() => navigator.clipboard.writeText(url)}>Copy link</button>
93
+ <a href={`mailto:?subject=Join%20my%20call&body=${encodeURIComponent(url)}`}>Email</a>
94
+ <a href={`sms:?body=${encodeURIComponent(url)}`}>SMS</a>
95
+ <button onClick={onClose}>Close</button>
96
+ </dialog>
97
+ );
98
+ }
99
+ ```
100
+
101
+ `mailto:` opens default email client; `sms:` opens default SMS app on mobile (no-op on most desktops). Both are universal — no provider lock-in.
102
+
103
+ ---
104
+
105
+ ## QR code for in-person sharing
106
+
107
+ For "share to the person sitting next to you" UX, a QR is faster than a link:
108
+
109
+ ```tsx
110
+ import QRCode from "qrcode.react";
111
+
112
+ function CallQR({ sessionId }: { sessionId: string }) {
113
+ const url = `https://yourapp.com/call/${sessionId}`;
114
+ return (
115
+ <div>
116
+ <QRCode value={url} size={192} />
117
+ <p>{url}</p>
118
+ </div>
119
+ );
120
+ }
121
+ ```
122
+
123
+ ---
124
+
125
+ ## Anti-patterns
126
+
127
+ 1. **Sharing the raw `sessionId` instead of a deep link.** Recipients can't open it.
128
+ 2. **Wiring share before the deep-link route works.** Recipients click → 404.
129
+ 3. **`navigator.clipboard.writeText` without a manual-copy fallback.** Fails in non-https contexts (e.g., embedded iframes, dev tunnels).
130
+ 4. **Showing "Link copied!" toast even when share was cancelled.** `AbortError` is the user dismissing the share sheet — silent.
131
+ 5. **Forgetting `removeEventListener` on unmount.** Listener accumulates → multiple share sheets per click.
132
+ 6. **Hard-coding `https://yourapp.com`.** Use `window.location.origin` or `import.meta.env.VITE_APP_URL`.
133
+
134
+ ---
135
+
136
+ ## Server-side: deep links + auth bridge
137
+
138
+ If invitee isn't logged in, `https://yourapp.com/call/SESSION_ID` should redirect them through login THEN to the call. Pattern:
139
+
140
+ ```ts
141
+ // pages/call/[sessionId].tsx (Next.js example)
142
+ export async function getServerSideProps({ params, req }) {
143
+ const session = await getSession(req);
144
+ if (!session) {
145
+ return {
146
+ redirect: {
147
+ destination: `/login?next=/call/${params.sessionId}`,
148
+ permanent: false,
149
+ },
150
+ };
151
+ }
152
+ return { props: { sessionId: params.sessionId } };
153
+ }
154
+ ```
155
+
156
+ For invitees without an account, send them to `/signup?invite=SESSION_ID` and store the pending invite in `localStorage` until signup completes.
157
+
158
+ ---
159
+
160
+ ## Verification checklist
161
+
162
+ - [ ] Deep-link route `/call/:sessionId` works in 4 states (logged in / logged out / no account / fresh tab)
163
+ - [ ] `hideShareInviteButton: false` in CallSettings
164
+ - [ ] `onShareInviteButtonClicked` listener cleaned up on unmount
165
+ - [ ] `navigator.share` used when available; clipboard fallback; manual-copy fallback for non-https
166
+ - [ ] AbortError silenced
167
+ - [ ] App URL not hard-coded — read from env
168
+ - [ ] Smoke: copy link → paste in incognito → can join call
169
+
170
+ ---
171
+
172
+ ## Pointers
173
+
174
+ - `cometchat-react-calls/SKILL.md` — call architecture
175
+ - `cometchat-react-calls/references/in-call-chat.md` — sister cross-cutting concern
176
+ - Canonical docs: https://www.cometchat.com/docs/calls/javascript/share-invite