@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.
- package/bin/install.js +298 -41
- package/package.json +1 -1
- package/skills/cometchat/SKILL.md +146 -27
- 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 +1 -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 +1 -1
- 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,245 @@
|
|
|
1
|
+
# Picture-in-Picture on web
|
|
2
|
+
|
|
3
|
+
Web has two PiP APIs, used for different things:
|
|
4
|
+
|
|
5
|
+
1. **Video PiP** (`HTMLVideoElement.requestPictureInPicture()`) — lets a single `<video>` element float in a system-managed window above all browser tabs. Standard since Chrome 70 / Safari 13 / Firefox 71. Fine for one remote participant.
|
|
6
|
+
|
|
7
|
+
2. **Document PiP** (`window.documentPictureInPicture.requestWindow()`) — lets you put arbitrary HTML (custom call UI with controls, multi-tile grid, roster) in a floating window. Chrome 116+ only. Falls back gracefully where unsupported.
|
|
8
|
+
|
|
9
|
+
This reference covers both, plus when to pick which.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## When to use Video PiP vs Document PiP
|
|
14
|
+
|
|
15
|
+
| Scenario | Pick |
|
|
16
|
+
|---|---|
|
|
17
|
+
| 1:1 call, just want the remote face floating while user works | Video PiP |
|
|
18
|
+
| Multi-party call, want the active speaker + a small roster floating | Document PiP if Chrome 116+, else fall back to Video PiP |
|
|
19
|
+
| Want call controls (mute/end) visible in the PiP window | Document PiP only — Video PiP doesn't allow custom controls |
|
|
20
|
+
| Cross-browser support including Safari + Firefox | Video PiP (with fallback when neither works) |
|
|
21
|
+
|
|
22
|
+
The skill defaults to Video PiP for cross-browser compatibility; Document PiP is opt-in for Chromium-only apps.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Video PiP — the simple path
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
// CustomOngoingCallView.tsx — extends the version in references/custom-ui.md
|
|
30
|
+
const remoteVideoRef = useRef<HTMLVideoElement>(null);
|
|
31
|
+
const [pipActive, setPipActive] = useState(false);
|
|
32
|
+
|
|
33
|
+
async function enterPiP() {
|
|
34
|
+
const video = remoteVideoRef.current;
|
|
35
|
+
if (!video) return;
|
|
36
|
+
if (!document.pictureInPictureEnabled) {
|
|
37
|
+
setError("Picture-in-Picture isn't supported in this browser");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
await video.requestPictureInPicture();
|
|
42
|
+
setPipActive(true);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
// user denied, video not yet playing, etc.
|
|
45
|
+
console.warn("PiP request failed:", err);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
const video = remoteVideoRef.current;
|
|
51
|
+
if (!video) return;
|
|
52
|
+
const onEnter = () => setPipActive(true);
|
|
53
|
+
const onLeave = () => setPipActive(false);
|
|
54
|
+
video.addEventListener("enterpictureinpicture", onEnter);
|
|
55
|
+
video.addEventListener("leavepictureinpicture", onLeave);
|
|
56
|
+
return () => {
|
|
57
|
+
video.removeEventListener("enterpictureinpicture", onEnter);
|
|
58
|
+
video.removeEventListener("leavepictureinpicture", onLeave);
|
|
59
|
+
};
|
|
60
|
+
}, []);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The `<video>` element keeps playing — PiP doesn't pause or remount. CSS doesn't apply (the OS owns the floating window). Hide the in-page video when PiP is active to avoid the "two videos playing" UX:
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
<video
|
|
67
|
+
ref={remoteVideoRef}
|
|
68
|
+
autoPlay
|
|
69
|
+
playsInline
|
|
70
|
+
style={{ display: pipActive ? "none" : "block" }}
|
|
71
|
+
/>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Document PiP — the rich path (Chrome 116+)
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
const [pipWindow, setPipWindow] = useState<Window | null>(null);
|
|
80
|
+
|
|
81
|
+
async function enterDocumentPiP() {
|
|
82
|
+
// Feature detect
|
|
83
|
+
if (!("documentPictureInPicture" in window)) {
|
|
84
|
+
return enterPiP(); // fall through to video PiP
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const pipWin = await (window as unknown as {
|
|
88
|
+
documentPictureInPicture: { requestWindow: (opts: { width: number; height: number }) => Promise<Window> };
|
|
89
|
+
}).documentPictureInPicture.requestWindow({
|
|
90
|
+
width: 360,
|
|
91
|
+
height: 480,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Copy the call container into the PiP window
|
|
95
|
+
const container = document.getElementById("ongoing-call-root");
|
|
96
|
+
if (container) {
|
|
97
|
+
pipWin.document.body.appendChild(container);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// PiP window has its own document — copy stylesheets so kit styling works
|
|
101
|
+
for (const styleSheet of Array.from(document.styleSheets)) {
|
|
102
|
+
try {
|
|
103
|
+
const cssRules = Array.from(styleSheet.cssRules ?? []).map((r) => r.cssText).join("\n");
|
|
104
|
+
const style = pipWin.document.createElement("style");
|
|
105
|
+
style.textContent = cssRules;
|
|
106
|
+
pipWin.document.head.appendChild(style);
|
|
107
|
+
} catch {
|
|
108
|
+
// cross-origin stylesheets throw — copy <link> href instead
|
|
109
|
+
if (styleSheet.href) {
|
|
110
|
+
const link = pipWin.document.createElement("link");
|
|
111
|
+
link.rel = "stylesheet";
|
|
112
|
+
link.href = styleSheet.href;
|
|
113
|
+
pipWin.document.head.appendChild(link);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// When the user closes the PiP window (system X button), restore the container
|
|
119
|
+
pipWin.addEventListener("pagehide", () => {
|
|
120
|
+
const restored = pipWin.document.getElementById("ongoing-call-root");
|
|
121
|
+
if (restored && document.getElementById("call-host")) {
|
|
122
|
+
document.getElementById("call-host")!.appendChild(restored);
|
|
123
|
+
}
|
|
124
|
+
setPipWindow(null);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
setPipWindow(pipWin);
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The `container` keeps its event handlers and React fiber attached — clicking "End" inside the PiP window still calls your React handlers. This is the magic of Document PiP that single-video PiP doesn't give you.
|
|
132
|
+
|
|
133
|
+
**Caveat:** stylesheets are copied at PiP-open time. If you change the theme mid-PiP (light/dark toggle), styles in the PiP window go stale. Add a `MutationObserver` or just don't allow theme switching while PiP is active.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Browser support matrix
|
|
138
|
+
|
|
139
|
+
| Browser | Video PiP | Document PiP |
|
|
140
|
+
|---|---|---|
|
|
141
|
+
| Chrome 70+ desktop | ✓ | Chrome 116+ |
|
|
142
|
+
| Edge 79+ desktop | ✓ | Edge 116+ |
|
|
143
|
+
| Safari 13+ desktop | ✓ | ✗ (no plans yet) |
|
|
144
|
+
| Firefox 71+ desktop | ✓ (custom toggle UI, not standard API) | ✗ |
|
|
145
|
+
| Chrome mobile (Android) | ✓ system-PiP equivalent | ✗ |
|
|
146
|
+
| Safari iOS | iPad: ✓; iPhone: limited | ✗ |
|
|
147
|
+
|
|
148
|
+
Feature-detect both. Don't render the "Enter PiP" button when neither is supported.
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
const canVideoPiP = typeof document !== "undefined" && document.pictureInPictureEnabled;
|
|
152
|
+
const canDocPiP = typeof window !== "undefined" && "documentPictureInPicture" in window;
|
|
153
|
+
const showPipButton = canVideoPiP || canDocPiP;
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Auto-enter PiP on tab switch
|
|
159
|
+
|
|
160
|
+
A "tab visibility" pattern many call UX teams want — auto-enter PiP when the user switches away from the call tab:
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
useEffect(() => {
|
|
164
|
+
const onVisibilityChange = () => {
|
|
165
|
+
if (document.visibilityState === "hidden" && remoteVideoRef.current) {
|
|
166
|
+
remoteVideoRef.current.requestPictureInPicture().catch(() => {});
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
170
|
+
return () => document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
171
|
+
}, []);
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Browsers reject auto-PiP requests not tied to user gestures in some contexts (Safari is strictest). Use `await navigator.mediaSession.setActionHandler("enterpictureinpicture", ...)` for a cleaner API where supported.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Auto-leave PiP on hangup
|
|
179
|
+
|
|
180
|
+
When the call ends, exit PiP cleanly:
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
function endCall() {
|
|
184
|
+
if (document.pictureInPictureElement) {
|
|
185
|
+
document.exitPictureInPicture();
|
|
186
|
+
}
|
|
187
|
+
if (pipWindow) {
|
|
188
|
+
pipWindow.close();
|
|
189
|
+
setPipWindow(null);
|
|
190
|
+
}
|
|
191
|
+
CometChatCalls.leaveSession();
|
|
192
|
+
// ...rest of cleanup
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Without this, the PiP window stays floating after the call ends, showing a frozen frame.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## PiP + custom UI integration
|
|
201
|
+
|
|
202
|
+
If you're using `enableDefaultLayout(true)` (kit-rendered call UI), PiP works on the kit's internal `<video>` element. Reach into it via:
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
const callContainer = document.getElementById("calls-container");
|
|
206
|
+
const video = callContainer?.querySelector("video"); // kit renders one or more
|
|
207
|
+
if (video instanceof HTMLVideoElement) {
|
|
208
|
+
await video.requestPictureInPicture();
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Brittle — kit DOM structure can change between versions. Custom UI (Document PiP path above) is more stable.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Anti-patterns
|
|
217
|
+
|
|
218
|
+
1. **Calling `requestPictureInPicture()` from `useEffect` on mount.** Browsers reject — must be in response to user gesture. Wire to a button.
|
|
219
|
+
2. **Forgetting to hide the in-page `<video>` while PiP is active.** Two videos play, audio doubles, layout breaks.
|
|
220
|
+
3. **Document PiP without copying stylesheets.** PiP window renders unstyled; user sees raw HTML.
|
|
221
|
+
4. **Not exiting PiP on hangup.** Frozen frame floats after call ends.
|
|
222
|
+
5. **Document PiP detection via `'documentPictureInPicture' in document`.** It's on `window`, not `document`. Common typo.
|
|
223
|
+
6. **Auto-PiP on every visibility change, including page reload.** User reloads → unintended PiP. Gate on call active + user-initiated focus loss.
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Verification checklist
|
|
228
|
+
|
|
229
|
+
- [ ] PiP button only renders if `document.pictureInPictureEnabled` OR `'documentPictureInPicture' in window`
|
|
230
|
+
- [ ] PiP request triggered from a click handler, not `useEffect`
|
|
231
|
+
- [ ] In-page video hidden while PiP active (or repositioned)
|
|
232
|
+
- [ ] `enterpictureinpicture` / `leavepictureinpicture` listeners update local state
|
|
233
|
+
- [ ] Hangup path calls `document.exitPictureInPicture()` if active
|
|
234
|
+
- [ ] Document PiP path copies stylesheets to the PiP window's document
|
|
235
|
+
- [ ] Document PiP path restores the container to the main window on `pagehide`
|
|
236
|
+
- [ ] Real-browser smoke: Chrome desktop (both APIs) + Safari desktop (Video PiP) + Firefox (Video PiP)
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Pointers
|
|
241
|
+
|
|
242
|
+
- Custom UI integration: `references/custom-ui.md`
|
|
243
|
+
- Kit-default layout: kit handles internal video PiP via the kit's own controls
|
|
244
|
+
- Document PiP spec: https://wicg.github.io/document-picture-in-picture/
|
|
245
|
+
- `cometchat-react-calls` SKILL.md — base hard rules
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# Raise hand on web
|
|
2
|
+
|
|
3
|
+
Lets participants signal they want to speak without interrupting the current speaker. The SDK ships first-party support — four method calls, two events, one settings flag. No custom signaling needed.
|
|
4
|
+
|
|
5
|
+
**Canonical docs:** https://www.cometchat.com/docs/calls/javascript/raise-hand
|
|
6
|
+
**Use it for:** classrooms, large group calls, town halls, any call with > ~5 participants where verbal turn-taking gets messy.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## SDK API (web Calls SDK)
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { CometChatCalls } from "@cometchat/calls-sdk-javascript";
|
|
14
|
+
|
|
15
|
+
// Local user raises hand
|
|
16
|
+
CometChatCalls.raiseHand();
|
|
17
|
+
|
|
18
|
+
// Local user lowers hand
|
|
19
|
+
CometChatCalls.lowerHand();
|
|
20
|
+
|
|
21
|
+
// Subscribe to other participants' hand state
|
|
22
|
+
CometChatCalls.addEventListener("onParticipantHandRaised", (participant) => {
|
|
23
|
+
// participant.uid, participant.name available
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
CometChatCalls.addEventListener("onParticipantHandLowered", (participant) => {
|
|
27
|
+
// ...
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The SDK ships a built-in raise-hand button in the default control panel. Hide it via call settings if you're rolling custom UI:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
const callSettings = new CometChatCalls.CallSettingsBuilder()
|
|
35
|
+
.setSessionID(sessionId)
|
|
36
|
+
.hideRaiseHandButton(true) // suppress the SDK's button — your UI takes over
|
|
37
|
+
.build();
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## When to use built-in vs custom
|
|
43
|
+
|
|
44
|
+
| Scenario | Use |
|
|
45
|
+
|---|---|
|
|
46
|
+
| Default kit UI is fine; just want raise-hand | Built-in (don't pass `hideRaiseHandButton`) |
|
|
47
|
+
| Custom call UI (your own control panel) | Custom — call `raiseHand()` / `lowerHand()` from your buttons |
|
|
48
|
+
| Need different host vs participant UI | Custom — query group scope, render different controls |
|
|
49
|
+
| Need raise-hand list (host sees who's raised) | Custom — maintain local Map<uid, raisedAt> via the listeners |
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Custom raise-hand UX — three pieces
|
|
54
|
+
|
|
55
|
+
### 1. Local participant button (toggle)
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
import { useState } from "react";
|
|
59
|
+
import { CometChatCalls } from "@cometchat/calls-sdk-javascript";
|
|
60
|
+
|
|
61
|
+
function RaiseHandButton() {
|
|
62
|
+
const [raised, setRaised] = useState(false);
|
|
63
|
+
|
|
64
|
+
function toggle() {
|
|
65
|
+
if (raised) {
|
|
66
|
+
CometChatCalls.lowerHand();
|
|
67
|
+
setRaised(false);
|
|
68
|
+
} else {
|
|
69
|
+
CometChatCalls.raiseHand();
|
|
70
|
+
setRaised(true);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<button onClick={toggle} aria-pressed={raised}>
|
|
76
|
+
{raised ? "✋ Lower" : "✋ Raise hand"}
|
|
77
|
+
</button>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Visual hint: render the icon with `aria-pressed={raised}` so screen readers announce the toggle state. (See `cometchat-a11y` for the broader rule.)
|
|
83
|
+
|
|
84
|
+
### 2. Raised-hands roster (host view)
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
import { useEffect, useState } from "react";
|
|
88
|
+
import { CometChatCalls } from "@cometchat/calls-sdk-javascript";
|
|
89
|
+
|
|
90
|
+
interface RaisedParticipant { uid: string; name: string; raisedAt: number; }
|
|
91
|
+
|
|
92
|
+
function RaisedHandsList() {
|
|
93
|
+
const [raised, setRaised] = useState<Map<string, RaisedParticipant>>(new Map());
|
|
94
|
+
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
const onRaised = (p: { uid: string; name: string }) => {
|
|
97
|
+
setRaised(prev => {
|
|
98
|
+
const next = new Map(prev);
|
|
99
|
+
next.set(p.uid, { ...p, raisedAt: Date.now() });
|
|
100
|
+
return next;
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
const onLowered = (p: { uid: string }) => {
|
|
104
|
+
setRaised(prev => {
|
|
105
|
+
const next = new Map(prev);
|
|
106
|
+
next.delete(p.uid);
|
|
107
|
+
return next;
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
CometChatCalls.addEventListener("onParticipantHandRaised", onRaised);
|
|
112
|
+
CometChatCalls.addEventListener("onParticipantHandLowered", onLowered);
|
|
113
|
+
|
|
114
|
+
return () => {
|
|
115
|
+
CometChatCalls.removeEventListener("onParticipantHandRaised", onRaised);
|
|
116
|
+
CometChatCalls.removeEventListener("onParticipantHandLowered", onLowered);
|
|
117
|
+
};
|
|
118
|
+
}, []);
|
|
119
|
+
|
|
120
|
+
// Sort oldest-first — fairness queue
|
|
121
|
+
const sorted = Array.from(raised.values()).sort((a, b) => a.raisedAt - b.raisedAt);
|
|
122
|
+
if (sorted.length === 0) return null;
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<ul aria-label="Raised hands queue">
|
|
126
|
+
{sorted.map(p => (
|
|
127
|
+
<li key={p.uid}>
|
|
128
|
+
✋ {p.name} <span style={{ color: "#888" }}>{secondsAgo(p.raisedAt)}</span>
|
|
129
|
+
</li>
|
|
130
|
+
))}
|
|
131
|
+
</ul>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function secondsAgo(t: number) { return `${Math.round((Date.now() - t) / 1000)}s ago`; }
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Sort by `raisedAt` ascending = first-raised-first-called, which feels fair to participants. Don't sort alphabetically.
|
|
139
|
+
|
|
140
|
+
### 3. Toast notification for the host
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
const onRaised = (p: { name: string }) => {
|
|
145
|
+
toast.info(`${p.name} raised their hand`, { duration: 4000 });
|
|
146
|
+
};
|
|
147
|
+
CometChatCalls.addEventListener("onParticipantHandRaised", onRaised);
|
|
148
|
+
return () => CometChatCalls.removeEventListener("onParticipantHandRaised", onRaised);
|
|
149
|
+
}, []);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Use `aria-live="polite"` on the toast region so screen readers announce — same a11y pattern as new-message announcements (cf. `cometchat-a11y`).
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Lower-by-host pattern
|
|
157
|
+
|
|
158
|
+
The SDK exposes `lowerHand()` only for the local user. To let a host lower someone else's hand, you need a moderator action via the participant-management API:
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
// Host action — requires moderator/admin scope on the group
|
|
162
|
+
async function lowerParticipantHand(uid: string) {
|
|
163
|
+
// SDK doesn't expose remoteLowerHand directly. Two options:
|
|
164
|
+
// A) Send a custom message to the participant; their client lowers itself
|
|
165
|
+
// B) Use the moderator mute/kick API as the boundary
|
|
166
|
+
|
|
167
|
+
// Option A: lightweight, requires the participant's client to listen
|
|
168
|
+
await CometChat.sendCustomMessage(new CometChat.CustomMessage(
|
|
169
|
+
uid, CometChat.RECEIVER_TYPE.USER, "lower_hand", {}
|
|
170
|
+
));
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
On the receiving side:
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
CometChat.addMessageListener("raise-hand-control", new CometChat.MessageListener({
|
|
178
|
+
onCustomMessageReceived: (msg) => {
|
|
179
|
+
if (msg.getType() === "lower_hand") {
|
|
180
|
+
CometChatCalls.lowerHand();
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
}));
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
This is application-level signaling, not SDK-built-in. Document the contract in your team's call protocols.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Hide button on rendered surfaces
|
|
191
|
+
|
|
192
|
+
If using the kit's `<CometChatOngoingCall />` and want raise-hand off entirely (e.g. 1:1 calls don't need it):
|
|
193
|
+
|
|
194
|
+
```tsx
|
|
195
|
+
import { CometChatCalls } from "@cometchat/calls-sdk-javascript";
|
|
196
|
+
|
|
197
|
+
const settings = new CometChatCalls.CallSettingsBuilder()
|
|
198
|
+
.setSessionID(sessionId)
|
|
199
|
+
.hideRaiseHandButton(true)
|
|
200
|
+
.build();
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
For 1:1 calls, default to hidden. For group calls > 5 participants, default to shown.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Anti-patterns
|
|
208
|
+
|
|
209
|
+
1. **Polling for raised-hand state.** The SDK fires events on change — listen, don't poll. Polling burns CPU.
|
|
210
|
+
2. **Sorting raised-hands alphabetically.** First-raised-first-called is the fair queue. Sort by `raisedAt` ascending.
|
|
211
|
+
3. **Auto-lowering hands after a timer.** Some people raise their hand and wait. Letting the SDK manage state means the participant lowers when called or via their own button.
|
|
212
|
+
4. **Showing raise-hand button in 1:1 calls.** Visually noisy and pointless. Gate on `participantCount > 2`.
|
|
213
|
+
5. **Skipping the listener cleanup in `useEffect`'s return.** Stacked listeners fire multiple times when the component re-mounts — duplicate toasts, duplicate roster entries.
|
|
214
|
+
6. **Treating "hand raised" as a permission grant.** Raise-hand is a request, not a mute override. The host still has to unmute the participant separately.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Verification checklist
|
|
219
|
+
|
|
220
|
+
- [ ] `raiseHand()` / `lowerHand()` calls in your code (not just relying on the SDK button)
|
|
221
|
+
- [ ] Both `onParticipantHandRaised` and `onParticipantHandLowered` listeners registered
|
|
222
|
+
- [ ] Listeners cleaned up in component unmount (return from `useEffect`)
|
|
223
|
+
- [ ] Roster sorted by `raisedAt` ascending
|
|
224
|
+
- [ ] `hideRaiseHandButton: true` in call settings IF custom UI is used (otherwise duplicates)
|
|
225
|
+
- [ ] Toast / badge UI uses `aria-live="polite"` (a11y)
|
|
226
|
+
- [ ] Browser smoke: 3 tabs, hand-raise from 2 of them, host's roster shows both in raise-order
|
|
227
|
+
- [ ] Lower-hand smoke: tab A raises, lowers, host's roster updates without page refresh
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Pointers
|
|
232
|
+
|
|
233
|
+
- `cometchat-react-calls` SKILL.md — the seven hard rules (still apply for raise-hand UI)
|
|
234
|
+
- `references/group-calls.md` — group call architecture (raise-hand is a group-call feature)
|
|
235
|
+
- `references/custom-ui.md` — custom call UI patterns
|
|
236
|
+
- `cometchat-a11y` — toast announcements for raised-hand events
|
|
237
|
+
- Canonical docs: https://www.cometchat.com/docs/calls/javascript/raise-hand
|
|
238
|
+
- For deeper SDK reference (other event types, presenter-mode interplay): query the docs MCP at `https://www.cometchat.com/docs/mcp`
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Recording + screen sharing on web
|
|
2
|
+
|
|
3
|
+
Both features ship with the Calls SDK; both have web-specific gotchas the kit's defaults don't handle.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Recording
|
|
8
|
+
|
|
9
|
+
### Server-side: enable in the dashboard first
|
|
10
|
+
|
|
11
|
+
Recording is a paid feature gated by your CometChat plan. Enable it in **Dashboard → Chat & Messaging → Calls → Recording**. Without that, the client-side flag below is a no-op.
|
|
12
|
+
|
|
13
|
+
### Client-side: opt-in per session
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
const settings = new CometChatCalls.CallSettingsBuilder()
|
|
17
|
+
.setSessionID(sessionId)
|
|
18
|
+
.setIsAudioOnly(false)
|
|
19
|
+
.enableRecording(true) // ← server starts recording when session begins
|
|
20
|
+
.setShowRecordingButton(true) // ← user-toggleable mid-call
|
|
21
|
+
.build();
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Two flags, two behaviors:
|
|
25
|
+
|
|
26
|
+
- `enableRecording(true)` — recording starts the moment the session begins. Server-side flag.
|
|
27
|
+
- `setShowRecordingButton(true)` — exposes a "Record" toggle in the default control panel. User decides when to start/stop. Custom-UI code must wire its own button.
|
|
28
|
+
|
|
29
|
+
**Compliance note:** in some jurisdictions you must notify all participants before recording starts. The default kit UI shows a small "Recording" indicator; if you're using custom UI, you must render this yourself. The skill's verification checklist flags this.
|
|
30
|
+
|
|
31
|
+
### Recording lifecycle events
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
const listener = new CometChatCalls.OngoingCallListener({
|
|
35
|
+
onRecordingStarted: (rec: unknown) => {
|
|
36
|
+
// server confirmed recording is active
|
|
37
|
+
},
|
|
38
|
+
onRecordingStopped: (rec: unknown) => {
|
|
39
|
+
// server stopped — file will appear in dashboard within ~30 seconds
|
|
40
|
+
},
|
|
41
|
+
onRecordingFailed: (error: unknown) => {
|
|
42
|
+
// surface to UI — usually plan limits or storage quota
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Where the recordings go
|
|
48
|
+
|
|
49
|
+
CometChat hosts the file. It appears in **Dashboard → Calls → Recordings** with a download link. The skill points users at the dashboard path; there is no client-side download API.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Screen sharing
|
|
54
|
+
|
|
55
|
+
### Two roles: presenter + viewer
|
|
56
|
+
|
|
57
|
+
- **Presenter** (the user sharing their screen) — calls `CometChatCalls.startScreenShare()` and receives a `MediaStream` from `getDisplayMedia`
|
|
58
|
+
- **Viewer** (everyone else) — sees the presenter's screen as another video tile, no special API call needed
|
|
59
|
+
|
|
60
|
+
Browser support: Chrome/Edge (full), Firefox (full), Safari 13+ (full). On mobile browsers, `getDisplayMedia` is supported on iOS 16+ Safari and recent Android Chrome.
|
|
61
|
+
|
|
62
|
+
### Presenter — start sharing
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
async function startScreenShare() {
|
|
66
|
+
try {
|
|
67
|
+
await CometChatCalls.startScreenShare();
|
|
68
|
+
// SDK handled getDisplayMedia + signaling; UI updates via onScreenShareStarted
|
|
69
|
+
} catch (err: unknown) {
|
|
70
|
+
if ((err as Error).name === "NotAllowedError") {
|
|
71
|
+
// user clicked "Cancel" on the picker — no error UI needed
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
setError("Couldn't start screen share");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Stop sharing:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
CometChatCalls.endScreenShare();
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The browser also fires its own "Stop sharing" button (the system overlay Chrome shows during a screen-share). The SDK listens for this too; `onScreenShareEnded` fires either way.
|
|
86
|
+
|
|
87
|
+
### Viewer — listen for screen-share events
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
const listener = new CometChatCalls.OngoingCallListener({
|
|
91
|
+
onScreenShareStarted: (presenterUid: string, stream: MediaStream) => {
|
|
92
|
+
// attach the stream to a <video> element
|
|
93
|
+
if (screenShareVideoRef.current) {
|
|
94
|
+
screenShareVideoRef.current.srcObject = stream;
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
onScreenShareEnded: () => {
|
|
98
|
+
if (screenShareVideoRef.current) {
|
|
99
|
+
screenShareVideoRef.current.srcObject = null;
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Compose the screen-share tile alongside the camera tiles in your custom layout.
|
|
106
|
+
|
|
107
|
+
### Audio passthrough during screen share
|
|
108
|
+
|
|
109
|
+
By default, `getDisplayMedia` captures video only. To capture system audio (for sharing a video with sound), pass `audio: true`:
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
// Browser-level API — the SDK's startScreenShare wraps this internally
|
|
113
|
+
const stream = await navigator.mediaDevices.getDisplayMedia({
|
|
114
|
+
video: true,
|
|
115
|
+
audio: true,
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Browser support is uneven — Chrome desktop has it; Firefox does not; Safari has it for tab capture but not full-screen.
|
|
120
|
+
|
|
121
|
+
The Calls SDK's `startScreenShare()` does NOT request audio by default in v4. If you need audio passthrough, use the lower-level `getDisplayMedia` API directly + pipe the audio track via a custom track-add hook (covered in `custom-ui.md`).
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Combining recording + screen-share
|
|
126
|
+
|
|
127
|
+
Server-side recording captures the active video composition, including screen-share when a participant is sharing. The recording file is one MP4 with the layout the kit was rendering at the time.
|
|
128
|
+
|
|
129
|
+
If you're using custom UI, the recording captures what the SDK sends to the server — not your custom DOM. The composition is determined by the SDK's internal layout, not your CSS.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Browser permissions for screen-share
|
|
134
|
+
|
|
135
|
+
Like `getUserMedia`, `getDisplayMedia` requires HTTPS or `localhost`. It also requires an active user gesture (click/tap) — you cannot start it from a `useEffect` or timer. The skill scaffolds the API call inside an onClick handler.
|
|
136
|
+
|
|
137
|
+
System-level: macOS 10.15+ asks the user once to grant Chrome/Safari/Firefox permission to record the screen (System Preferences → Security & Privacy → Screen Recording). If the user denies, `getDisplayMedia` throws `NotAllowedError` with no remediation path inside the browser — surface a "Open System Preferences" instruction.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Cleanup
|
|
142
|
+
|
|
143
|
+
Both recording and screen-share are part of the call session. `CometChatCalls.leaveSession()` stops both automatically. Custom UI must NOT separately call `getTracks().forEach(t => t.stop())` on the SDK's screen-share stream — the SDK owns it. Stop only the streams YOUR code created (e.g. local preview).
|