@droponair/sdk-js 0.5.0 → 0.7.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/CHANGELOG.md +71 -0
- package/README.md +85 -1
- package/dist/attachment/attachment-client.d.ts +2 -2
- package/dist/attachment/attachment-client.js +2 -2
- package/dist/attachment/attachment-types.js +1 -1
- package/dist/core/messaging-client.d.ts +12 -0
- package/dist/core/messaging-client.js +100 -1
- package/dist/core/types.d.ts +39 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,77 @@ This project follows [Semantic Versioning](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [0.7.0], 2026-05-19
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **Conference moderation and waiting room.** Host designated to the call
|
|
13
|
+
initiator and transferable mid-call; host can appoint co-hosts. Host and
|
|
14
|
+
co-host can mute and remove participants and admit / reject users from a
|
|
15
|
+
waiting room. If the host leaves without transferring, the server
|
|
16
|
+
auto-promotes the longest-joined remaining participant.
|
|
17
|
+
- New `client` methods:
|
|
18
|
+
- `transferHost(callId, groupId, newHostUserId)`
|
|
19
|
+
- `appointCoHost(callId, groupId, userId)` / `revokeCoHost(callId, groupId, userId)`
|
|
20
|
+
- `muteParticipant(callId, groupId, targetUserId)` (signaling hint; the
|
|
21
|
+
target SDK applies the mute locally)
|
|
22
|
+
- `removeParticipant(callId, groupId, targetUserId)` (server-enforced; the
|
|
23
|
+
target is dropped from the call session)
|
|
24
|
+
- `requestWaitingRoomEntry(callId, groupId)` (would-be joiner side)
|
|
25
|
+
- `admitFromWaitingRoom(callId, groupId, userId)` / `rejectFromWaitingRoom(callId, groupId, userId)`
|
|
26
|
+
- New `GroupCallEventType` values: `'GROUP_CALL_HOST_TRANSFER'`,
|
|
27
|
+
`'GROUP_CALL_COHOST_APPOINT'`, `'GROUP_CALL_COHOST_REVOKE'`,
|
|
28
|
+
`'GROUP_CALL_ROLE_CHANGED'`, `'GROUP_CALL_MUTE_PARTICIPANT'`,
|
|
29
|
+
`'GROUP_CALL_REMOVE_PARTICIPANT'`, `'GROUP_CALL_PARTICIPANT_REMOVED'`,
|
|
30
|
+
`'GROUP_CALL_WAITING_ROOM_REQUEST'`, `'GROUP_CALL_WAITING_ROOM_JOINED'`,
|
|
31
|
+
`'GROUP_CALL_WAITING_ROOM_ADMIT'`, `'GROUP_CALL_WAITING_ROOM_ADMITTED'`,
|
|
32
|
+
`'GROUP_CALL_WAITING_ROOM_REJECT'`, `'GROUP_CALL_WAITING_ROOM_REJECTED'`.
|
|
33
|
+
- `GET /api/info` now advertises `"moderation"` in the `features` array.
|
|
34
|
+
|
|
35
|
+
### Notes
|
|
36
|
+
- Wire-level additive: legacy 0.6.x clients ignore the new signal type
|
|
37
|
+
strings (string discriminators on the existing `GroupCallFrame.type`).
|
|
38
|
+
No proto schema change, no `PROTOCOL_VERSION` bump.
|
|
39
|
+
- Availability depends on your plan. See the
|
|
40
|
+
[pricing page](https://www.droponair.com/pricing) and your dashboard
|
|
41
|
+
Subscription page for what's enabled on your app.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## [0.6.0], 2026-05-18
|
|
46
|
+
|
|
47
|
+
### Added
|
|
48
|
+
- **Screen sharing signaling.** New client APIs and event types so apps can
|
|
49
|
+
coordinate the start/stop of a screen-share track on top of the existing
|
|
50
|
+
WebRTC call. The SDK only signals - capture (`getDisplayMedia()` on web) and
|
|
51
|
+
track management remain in the app layer.
|
|
52
|
+
- **1:1 calls:**
|
|
53
|
+
- `client.startScreenShare(callId, payload?)` and `client.stopScreenShare(callId, payload?)`.
|
|
54
|
+
- New `CallEventType` values: `'CALL_SCREEN_SHARE_STARTED'`, `'CALL_SCREEN_SHARE_STOPPED'`.
|
|
55
|
+
- **Group calls:**
|
|
56
|
+
- `client.startGroupScreenShare(callId, groupId, payload?)` and `client.stopGroupScreenShare(callId, groupId, payload?)`.
|
|
57
|
+
- New `GroupCallEventType` values: `'GROUP_CALL_SCREEN_SHARE_STARTED'`, `'GROUP_CALL_SCREEN_SHARE_STOPPED'`.
|
|
58
|
+
- Server enforces a single concurrent sharer per group call. If another
|
|
59
|
+
participant already holds the slot when you call `startGroupScreenShare`,
|
|
60
|
+
you'll receive a `GROUP_CALL_SCREEN_SHARE_STOPPED` event with the current
|
|
61
|
+
holder's userId in `payload`, so the app can roll back its optimistic UI.
|
|
62
|
+
- When a sharer leaves the call, the server broadcasts a STOPPED event to
|
|
63
|
+
remaining participants before they see `PARTICIPANT_LEFT`.
|
|
64
|
+
- `GET /api/info` now advertises `"screen_sharing"` in the `features` array.
|
|
65
|
+
|
|
66
|
+
### Notes
|
|
67
|
+
- Wire-level additive: legacy 0.5.x clients ignore the new signal type strings
|
|
68
|
+
(string discriminators on existing `CallFrame.type` / `GroupCallFrame.type`).
|
|
69
|
+
No proto schema change, no `PROTOCOL_VERSION` bump.
|
|
70
|
+
- Screen content is opaque WebRTC media; the server never sees it. Use
|
|
71
|
+
`getDisplayMedia()` (web) / `MediaProjection` (Android) / `ReplayKit` (iOS)
|
|
72
|
+
in the app, add the track to the existing peer connection, then call
|
|
73
|
+
`startScreenShare(...)` to notify the peer.
|
|
74
|
+
- Plan gate: `screen_sharing` is enabled on PRO/GROWTH/PAYG/ENTERPRISE and
|
|
75
|
+
disabled on FREE. Check `subscription.featuresEnabled.screen_sharing` (server
|
|
76
|
+
side, on the customer's backend).
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
9
80
|
## [0.5.0], 2026-05-18
|
|
10
81
|
|
|
11
82
|
### Added
|
package/README.md
CHANGED
|
@@ -160,7 +160,7 @@ client.onMessage(async (msg) => {
|
|
|
160
160
|
| `sendMessage(toUserId, text, { attachments })` | `Promise<{ messageId }>` | Send with attachments |
|
|
161
161
|
|
|
162
162
|
- E2EE: a random AES-256-GCM file key encrypts the bytes; the file key is wrapped per recipient device using X25519 + HKDF (same model as message payloads). Server never sees the unwrapped file key.
|
|
163
|
-
-
|
|
163
|
+
- Availability and per-file / per-month limits depend on your plan and are enforced server-side before the presigned URL is issued. See the [pricing page](https://www.droponair.com/pricing) and your dashboard Subscription page for what's enabled on your app.
|
|
164
164
|
- Upload URL TTL = 15 min. Download URL TTL = 5 min. Download authorization checks that the requester is the original sender or in the captured recipient list.
|
|
165
165
|
|
|
166
166
|
### Broadcast Channels
|
|
@@ -209,6 +209,90 @@ client.onMessage(async (msg) => {
|
|
|
209
209
|
| `sendGroupCallSignal(type, callId, groupId, targetUserId, payload)` | `void` | Send SDP/ICE to a peer |
|
|
210
210
|
| `onGroupCallEvent(callback)` | `() => void` | Listen for group call events |
|
|
211
211
|
|
|
212
|
+
### Screen Sharing
|
|
213
|
+
|
|
214
|
+
Available since SDK `0.6.0`. The SDK only signals start/stop - capture (via the browser's `getDisplayMedia()`) and adding the resulting `MediaStreamTrack` to the existing peer connection are the app's responsibility.
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
// 1:1 call
|
|
218
|
+
async function shareMyScreen(callId: string, peerConnection: RTCPeerConnection) {
|
|
219
|
+
const stream = await navigator.mediaDevices.getDisplayMedia({ video: true });
|
|
220
|
+
const track = stream.getVideoTracks()[0];
|
|
221
|
+
peerConnection.addTrack(track, stream);
|
|
222
|
+
client.startScreenShare(callId);
|
|
223
|
+
track.onended = () => client.stopScreenShare(callId);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Group call
|
|
227
|
+
async function shareMyScreenInGroup(callId: string, groupId: string, peerConnections: RTCPeerConnection[]) {
|
|
228
|
+
const stream = await navigator.mediaDevices.getDisplayMedia({ video: true });
|
|
229
|
+
const track = stream.getVideoTracks()[0];
|
|
230
|
+
peerConnections.forEach(pc => pc.addTrack(track, stream));
|
|
231
|
+
client.startGroupScreenShare(callId, groupId);
|
|
232
|
+
track.onended = () => client.stopGroupScreenShare(callId, groupId);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
client.onCallEvent((evt) => {
|
|
236
|
+
if (evt.type === 'CALL_SCREEN_SHARE_STARTED') showShareIndicator(evt.callId);
|
|
237
|
+
if (evt.type === 'CALL_SCREEN_SHARE_STOPPED') hideShareIndicator(evt.callId);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
client.onGroupCallEvent((evt) => {
|
|
241
|
+
if (evt.type === 'GROUP_CALL_SCREEN_SHARE_STARTED') showShareIndicator(evt.callId, evt.payload); // payload = sharer userId
|
|
242
|
+
if (evt.type === 'GROUP_CALL_SCREEN_SHARE_STOPPED') hideShareIndicator(evt.callId);
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
| Method | Returns | Description |
|
|
247
|
+
|--------|---------|-------------|
|
|
248
|
+
| `startScreenShare(callId, payload?)` | `void` | Signal start in a 1:1 call |
|
|
249
|
+
| `stopScreenShare(callId, payload?)` | `void` | Signal stop in a 1:1 call |
|
|
250
|
+
| `startGroupScreenShare(callId, groupId, payload?)` | `void` | Signal start in a group call |
|
|
251
|
+
| `stopGroupScreenShare(callId, groupId, payload?)` | `void` | Signal stop in a group call |
|
|
252
|
+
|
|
253
|
+
- The server enforces **one concurrent sharer per group call**. If another participant already holds the slot when you call `startGroupScreenShare`, the SDK delivers a `GROUP_CALL_SCREEN_SHARE_STOPPED` event with the current holder's userId in `payload`, so you can roll back the optimistic UI.
|
|
254
|
+
- When a sharer leaves the call, the server broadcasts a STOPPED event to remaining participants before they see `PARTICIPANT_LEFT`.
|
|
255
|
+
- Availability depends on your plan. See the [pricing page](https://www.droponair.com/pricing) and your dashboard Subscription page for what's enabled on your app.
|
|
256
|
+
|
|
257
|
+
### Conference moderation and waiting room
|
|
258
|
+
|
|
259
|
+
Available since SDK `0.7.0`. The call initiator is the default host; host can transfer the role mid-call or appoint co-hosts. Host and co-host can mute, remove, and gate joiners through a waiting room. If the host leaves without transferring, the server auto-promotes the longest-joined remaining participant and broadcasts `GROUP_CALL_ROLE_CHANGED`.
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
// Host actions
|
|
263
|
+
client.transferHost(callId, groupId, 'alice');
|
|
264
|
+
client.appointCoHost(callId, groupId, 'bob');
|
|
265
|
+
client.muteParticipant(callId, groupId, 'eve'); // signaling hint
|
|
266
|
+
client.removeParticipant(callId, groupId, 'mallory'); // server-enforced drop
|
|
267
|
+
|
|
268
|
+
// Waiting room
|
|
269
|
+
client.requestWaitingRoomEntry(callId, groupId); // would-be joiner side
|
|
270
|
+
client.admitFromWaitingRoom(callId, groupId, 'newbie'); // host / co-host
|
|
271
|
+
client.rejectFromWaitingRoom(callId, groupId, 'troll');
|
|
272
|
+
|
|
273
|
+
client.onGroupCallEvent((evt) => {
|
|
274
|
+
if (evt.type === 'GROUP_CALL_ROLE_CHANGED') updateHostUi(evt.payload); // {"userId":"...","role":"HOST|CO_HOST|PARTICIPANT"}
|
|
275
|
+
if (evt.type === 'GROUP_CALL_WAITING_ROOM_JOINED') showWaitingApprovalPrompt(evt.payload);
|
|
276
|
+
if (evt.type === 'GROUP_CALL_WAITING_ROOM_ADMITTED') joinTheCall(evt.callId, evt.groupId);
|
|
277
|
+
if (evt.type === 'GROUP_CALL_MUTE_PARTICIPANT') muteMyMicLocally();
|
|
278
|
+
if (evt.type === 'GROUP_CALL_PARTICIPANT_REMOVED') exitCallUi();
|
|
279
|
+
});
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
| Method | Returns | Description |
|
|
283
|
+
|--------|---------|-------------|
|
|
284
|
+
| `transferHost(callId, groupId, newHostUserId)` | `void` | Host only |
|
|
285
|
+
| `appointCoHost(callId, groupId, userId)` | `void` | Host only |
|
|
286
|
+
| `revokeCoHost(callId, groupId, userId)` | `void` | Host only |
|
|
287
|
+
| `muteParticipant(callId, groupId, targetUserId)` | `void` | Host / co-host. Signaling hint - the target SDK mutes the local mic. |
|
|
288
|
+
| `removeParticipant(callId, groupId, targetUserId)` | `void` | Host / co-host. Server drops the target from the call session. |
|
|
289
|
+
| `requestWaitingRoomEntry(callId, groupId)` | `void` | Would-be joiner |
|
|
290
|
+
| `admitFromWaitingRoom(callId, groupId, userId)` | `void` | Host / co-host |
|
|
291
|
+
| `rejectFromWaitingRoom(callId, groupId, userId)` | `void` | Host / co-host |
|
|
292
|
+
|
|
293
|
+
- Mute is intentionally a signaling hint: WebRTC media tracks can't be muted from outside the producing client. Remove is server-enforced - the target SDK receives `GROUP_CALL_PARTICIPANT_REMOVED` and the others receive `PARTICIPANT_LEFT`.
|
|
294
|
+
- Availability depends on your plan. See the [pricing page](https://www.droponair.com/pricing) and your dashboard Subscription page for what's enabled on your app.
|
|
295
|
+
|
|
212
296
|
## Security
|
|
213
297
|
|
|
214
298
|
- **X25519 ECDH** key agreement for shared secrets
|
|
@@ -21,8 +21,8 @@ interface AttachmentClientDeps {
|
|
|
21
21
|
logError: (stage: string, data?: Record<string, unknown>) => void;
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
-
* SDK-side coordinator for
|
|
25
|
-
* - createUploadSession: reserves an attachmentId + presigned PUT URL
|
|
24
|
+
* SDK-side coordinator for attachments. Handles:
|
|
25
|
+
* - createUploadSession: reserves an attachmentId + presigned PUT URL.
|
|
26
26
|
* - uploadBytes: PUTs bytes directly to the customer's bucket.
|
|
27
27
|
* - finalize: confirms upload + commits sha256.
|
|
28
28
|
* - prepareAttachmentAndUpload: convenience wrapper that does upload session,
|
|
@@ -20,8 +20,8 @@ async function sha256Hex(bytes) {
|
|
|
20
20
|
return hex.join('');
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
-
* SDK-side coordinator for
|
|
24
|
-
* - createUploadSession: reserves an attachmentId + presigned PUT URL
|
|
23
|
+
* SDK-side coordinator for attachments. Handles:
|
|
24
|
+
* - createUploadSession: reserves an attachmentId + presigned PUT URL.
|
|
25
25
|
* - uploadBytes: PUTs bytes directly to the customer's bucket.
|
|
26
26
|
* - finalize: confirms upload + commits sha256.
|
|
27
27
|
* - prepareAttachmentAndUpload: convenience wrapper that does upload session,
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
//
|
|
5
5
|
// DropOnAir never holds file bytes. The SDK uploads/downloads directly
|
|
6
6
|
// against the customer's storage bucket via short-lived presigned URLs
|
|
7
|
-
// minted by
|
|
7
|
+
// minted by the DropOnAir platform. For E2EE messages a per-attachment AES-256-GCM file
|
|
8
8
|
// key is wrapped per recipient device using the same X25519 + HKDF path
|
|
9
9
|
// used for message payloads.
|
|
10
10
|
// ---------------------------------------------------------------------------
|
|
@@ -136,7 +136,17 @@ export declare class MessagingClient implements DropOnAirClient {
|
|
|
136
136
|
leaveGroupCall(callId: string): Promise<void>;
|
|
137
137
|
endGroupCall(callId: string): Promise<void>;
|
|
138
138
|
sendGroupCallSignal(type: 'GROUP_CALL_SDP_OFFER' | 'GROUP_CALL_SDP_ANSWER' | 'GROUP_CALL_ICE_CANDIDATE', callId: string, groupId: string, targetUserId: string, payload: string): void;
|
|
139
|
+
startGroupScreenShare(callId: string, groupId: string, payload?: string): void;
|
|
140
|
+
stopGroupScreenShare(callId: string, groupId: string, payload?: string): void;
|
|
139
141
|
onGroupCallEvent(callback: GroupCallEventCallback): () => void;
|
|
142
|
+
transferHost(callId: string, groupId: string, newHostUserId: string): void;
|
|
143
|
+
appointCoHost(callId: string, groupId: string, userId: string): void;
|
|
144
|
+
revokeCoHost(callId: string, groupId: string, userId: string): void;
|
|
145
|
+
muteParticipant(callId: string, groupId: string, targetUserId: string): void;
|
|
146
|
+
removeParticipant(callId: string, groupId: string, targetUserId: string): void;
|
|
147
|
+
requestWaitingRoomEntry(callId: string, groupId: string): void;
|
|
148
|
+
admitFromWaitingRoom(callId: string, groupId: string, userId: string): void;
|
|
149
|
+
rejectFromWaitingRoom(callId: string, groupId: string, userId: string): void;
|
|
140
150
|
private sendGroupCallFrame;
|
|
141
151
|
/**
|
|
142
152
|
* Initiate an outgoing call.
|
|
@@ -149,6 +159,8 @@ export declare class MessagingClient implements DropOnAirClient {
|
|
|
149
159
|
endCall(callId: string): Promise<void>;
|
|
150
160
|
toggleVideo(callId: string, enabled: boolean): void;
|
|
151
161
|
sendCallSignal(type: 'CALL_SDP_OFFER' | 'CALL_SDP_ANSWER' | 'CALL_ICE_CANDIDATE', callId: string, payload: string): void;
|
|
162
|
+
startScreenShare(callId: string, payload?: string): void;
|
|
163
|
+
stopScreenShare(callId: string, payload?: string): void;
|
|
152
164
|
fetchTurnCredentials(): Promise<TurnCredentials>;
|
|
153
165
|
private sendCallFrame;
|
|
154
166
|
private emitCallEvent;
|
|
@@ -254,7 +254,7 @@ class MessagingClient {
|
|
|
254
254
|
logError: (stage, data) => this.logError(stage, data),
|
|
255
255
|
});
|
|
256
256
|
}
|
|
257
|
-
// Attachment API (
|
|
257
|
+
// Attachment API (PROTOCOL_VERSION 4+)
|
|
258
258
|
async prepareAttachmentAndUpload(bytes, options) {
|
|
259
259
|
return this.attachmentClient.prepareAttachmentAndUpload(bytes, options);
|
|
260
260
|
}
|
|
@@ -740,10 +740,103 @@ class MessagingClient {
|
|
|
740
740
|
sendGroupCallSignal(type, callId, groupId, targetUserId, payload) {
|
|
741
741
|
this.sendGroupCallFrame({ type, callId, groupId, targetUserId, payload });
|
|
742
742
|
}
|
|
743
|
+
startGroupScreenShare(callId, groupId, payload) {
|
|
744
|
+
this.sendGroupCallFrame({
|
|
745
|
+
type: 'GROUP_CALL_SCREEN_SHARE_STARTED',
|
|
746
|
+
callId,
|
|
747
|
+
groupId,
|
|
748
|
+
targetUserId: '',
|
|
749
|
+
payload: payload ?? '',
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
stopGroupScreenShare(callId, groupId, payload) {
|
|
753
|
+
this.sendGroupCallFrame({
|
|
754
|
+
type: 'GROUP_CALL_SCREEN_SHARE_STOPPED',
|
|
755
|
+
callId,
|
|
756
|
+
groupId,
|
|
757
|
+
targetUserId: '',
|
|
758
|
+
payload: payload ?? '',
|
|
759
|
+
});
|
|
760
|
+
}
|
|
743
761
|
onGroupCallEvent(callback) {
|
|
744
762
|
this.groupCallListeners.add(callback);
|
|
745
763
|
return () => this.groupCallListeners.delete(callback);
|
|
746
764
|
}
|
|
765
|
+
// Group call moderation + waiting room. The server validates host/co-host
|
|
766
|
+
// authority and the `featuresEnabled.moderation` plan flag; on denial it
|
|
767
|
+
// emits a LIMIT_REACHED event with reason="MODERATION_NOT_ENABLED".
|
|
768
|
+
transferHost(callId, groupId, newHostUserId) {
|
|
769
|
+
this.sendGroupCallFrame({
|
|
770
|
+
type: 'GROUP_CALL_HOST_TRANSFER',
|
|
771
|
+
callId,
|
|
772
|
+
groupId,
|
|
773
|
+
targetUserId: '',
|
|
774
|
+
payload: JSON.stringify({ userId: newHostUserId }),
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
appointCoHost(callId, groupId, userId) {
|
|
778
|
+
this.sendGroupCallFrame({
|
|
779
|
+
type: 'GROUP_CALL_COHOST_APPOINT',
|
|
780
|
+
callId,
|
|
781
|
+
groupId,
|
|
782
|
+
targetUserId: '',
|
|
783
|
+
payload: JSON.stringify({ userId }),
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
revokeCoHost(callId, groupId, userId) {
|
|
787
|
+
this.sendGroupCallFrame({
|
|
788
|
+
type: 'GROUP_CALL_COHOST_REVOKE',
|
|
789
|
+
callId,
|
|
790
|
+
groupId,
|
|
791
|
+
targetUserId: '',
|
|
792
|
+
payload: JSON.stringify({ userId }),
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
muteParticipant(callId, groupId, targetUserId) {
|
|
796
|
+
this.sendGroupCallFrame({
|
|
797
|
+
type: 'GROUP_CALL_MUTE_PARTICIPANT',
|
|
798
|
+
callId,
|
|
799
|
+
groupId,
|
|
800
|
+
targetUserId: '',
|
|
801
|
+
payload: JSON.stringify({ targetUserId }),
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
removeParticipant(callId, groupId, targetUserId) {
|
|
805
|
+
this.sendGroupCallFrame({
|
|
806
|
+
type: 'GROUP_CALL_REMOVE_PARTICIPANT',
|
|
807
|
+
callId,
|
|
808
|
+
groupId,
|
|
809
|
+
targetUserId: '',
|
|
810
|
+
payload: JSON.stringify({ targetUserId }),
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
requestWaitingRoomEntry(callId, groupId) {
|
|
814
|
+
this.sendGroupCallFrame({
|
|
815
|
+
type: 'GROUP_CALL_WAITING_ROOM_REQUEST',
|
|
816
|
+
callId,
|
|
817
|
+
groupId,
|
|
818
|
+
targetUserId: '',
|
|
819
|
+
payload: '',
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
admitFromWaitingRoom(callId, groupId, userId) {
|
|
823
|
+
this.sendGroupCallFrame({
|
|
824
|
+
type: 'GROUP_CALL_WAITING_ROOM_ADMIT',
|
|
825
|
+
callId,
|
|
826
|
+
groupId,
|
|
827
|
+
targetUserId: '',
|
|
828
|
+
payload: JSON.stringify({ userId }),
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
rejectFromWaitingRoom(callId, groupId, userId) {
|
|
832
|
+
this.sendGroupCallFrame({
|
|
833
|
+
type: 'GROUP_CALL_WAITING_ROOM_REJECT',
|
|
834
|
+
callId,
|
|
835
|
+
groupId,
|
|
836
|
+
targetUserId: '',
|
|
837
|
+
payload: JSON.stringify({ userId }),
|
|
838
|
+
});
|
|
839
|
+
}
|
|
747
840
|
sendGroupCallFrame(frame) {
|
|
748
841
|
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
749
842
|
throw new Error('DropOnAir websocket is not connected');
|
|
@@ -781,6 +874,12 @@ class MessagingClient {
|
|
|
781
874
|
sendCallSignal(type, callId, payload) {
|
|
782
875
|
this.sendCallFrame({ type, callId, payload });
|
|
783
876
|
}
|
|
877
|
+
startScreenShare(callId, payload) {
|
|
878
|
+
this.sendCallFrame({ type: 'CALL_SCREEN_SHARE_STARTED', callId, payload: payload ?? '' });
|
|
879
|
+
}
|
|
880
|
+
stopScreenShare(callId, payload) {
|
|
881
|
+
this.sendCallFrame({ type: 'CALL_SCREEN_SHARE_STOPPED', callId, payload: payload ?? '' });
|
|
882
|
+
}
|
|
784
883
|
async fetchTurnCredentials() {
|
|
785
884
|
const jwt = await this.getValidDropOnAirJwt(false);
|
|
786
885
|
const url = `${this.httpUrl}/api/v1/turn/credentials`;
|
package/dist/core/types.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export interface DecryptedMessage {
|
|
|
11
11
|
timestamp: number;
|
|
12
12
|
plaintext: string;
|
|
13
13
|
/**
|
|
14
|
-
* Attachment pointers (
|
|
14
|
+
* Attachment pointers (PROTOCOL_VERSION 4+).
|
|
15
15
|
* Empty/undefined for messages with no attachments. To fetch and decrypt the
|
|
16
16
|
* bytes, call {@code client.downloadAttachment(ref)}.
|
|
17
17
|
*/
|
|
@@ -57,7 +57,7 @@ export interface BroadcastMessage {
|
|
|
57
57
|
sequenceNumber: number;
|
|
58
58
|
}
|
|
59
59
|
export type BroadcastCallback = (message: BroadcastMessage) => void;
|
|
60
|
-
export type CallEventType = 'CALL_INVITE' | 'CALL_RINGING' | 'CALL_ACCEPTED' | 'CALL_REJECTED' | 'CALL_ENDED' | 'CALL_CANCELLED' | 'CALL_SDP_OFFER' | 'CALL_SDP_ANSWER' | 'CALL_ICE_CANDIDATE' | 'CALL_VIDEO_TOGGLE' | 'CALL_DENIED_LIMIT_REACHED';
|
|
60
|
+
export type CallEventType = 'CALL_INVITE' | 'CALL_RINGING' | 'CALL_ACCEPTED' | 'CALL_REJECTED' | 'CALL_ENDED' | 'CALL_CANCELLED' | 'CALL_SDP_OFFER' | 'CALL_SDP_ANSWER' | 'CALL_ICE_CANDIDATE' | 'CALL_VIDEO_TOGGLE' | 'CALL_SCREEN_SHARE_STARTED' | 'CALL_SCREEN_SHARE_STOPPED' | 'CALL_DENIED_LIMIT_REACHED';
|
|
61
61
|
export interface CallEvent {
|
|
62
62
|
type: CallEventType | string;
|
|
63
63
|
callId?: string;
|
|
@@ -92,7 +92,7 @@ export interface DecryptedGroupMessage {
|
|
|
92
92
|
plaintext: string;
|
|
93
93
|
}
|
|
94
94
|
export type GroupMessageCallback = (message: DecryptedGroupMessage) => void;
|
|
95
|
-
export type GroupCallEventType = 'GROUP_CALL_INVITE' | 'GROUP_CALL_RINGING' | 'GROUP_CALL_JOIN' | 'GROUP_CALL_LEAVE' | 'GROUP_CALL_END' | 'GROUP_CALL_ENDED' | 'GROUP_CALL_PARTICIPANT_JOINED' | 'GROUP_CALL_PARTICIPANT_LEFT' | 'GROUP_CALL_ALREADY_ACTIVE' | 'GROUP_CALL_SDP_OFFER' | 'GROUP_CALL_SDP_ANSWER' | 'GROUP_CALL_ICE_CANDIDATE';
|
|
95
|
+
export type GroupCallEventType = 'GROUP_CALL_INVITE' | 'GROUP_CALL_RINGING' | 'GROUP_CALL_JOIN' | 'GROUP_CALL_LEAVE' | 'GROUP_CALL_END' | 'GROUP_CALL_ENDED' | 'GROUP_CALL_PARTICIPANT_JOINED' | 'GROUP_CALL_PARTICIPANT_LEFT' | 'GROUP_CALL_PARTICIPANT_REMOVED' | 'GROUP_CALL_ALREADY_ACTIVE' | 'GROUP_CALL_SDP_OFFER' | 'GROUP_CALL_SDP_ANSWER' | 'GROUP_CALL_ICE_CANDIDATE' | 'GROUP_CALL_SCREEN_SHARE_STARTED' | 'GROUP_CALL_SCREEN_SHARE_STOPPED' | 'GROUP_CALL_HOST_TRANSFER' | 'GROUP_CALL_COHOST_APPOINT' | 'GROUP_CALL_COHOST_REVOKE' | 'GROUP_CALL_ROLE_CHANGED' | 'GROUP_CALL_MUTE_PARTICIPANT' | 'GROUP_CALL_REMOVE_PARTICIPANT' | 'GROUP_CALL_WAITING_ROOM_REQUEST' | 'GROUP_CALL_WAITING_ROOM_JOINED' | 'GROUP_CALL_WAITING_ROOM_ADMIT' | 'GROUP_CALL_WAITING_ROOM_ADMITTED' | 'GROUP_CALL_WAITING_ROOM_REJECT' | 'GROUP_CALL_WAITING_ROOM_REJECTED';
|
|
96
96
|
export interface GroupCallEvent {
|
|
97
97
|
type: GroupCallEventType | string;
|
|
98
98
|
callId: string;
|
|
@@ -212,6 +212,16 @@ export interface DropOnAirClient {
|
|
|
212
212
|
* The payload is forwarded opaquely, the server does NOT inspect or store it.
|
|
213
213
|
*/
|
|
214
214
|
sendCallSignal(type: 'CALL_SDP_OFFER' | 'CALL_SDP_ANSWER' | 'CALL_ICE_CANDIDATE', callId: string, payload: string): void;
|
|
215
|
+
/**
|
|
216
|
+
* Signal that this user started sharing their screen in a 1:1 call
|
|
217
|
+
* (PROTOCOL_VERSION 4+). The actual screen MediaStreamTrack
|
|
218
|
+
* is added to the existing peer connection by the application; the SDK only
|
|
219
|
+
* broadcasts the start/stop notification so the remote SDK can update its UI.
|
|
220
|
+
* Optional `payload` (JSON string) can carry track-id or app-specific metadata.
|
|
221
|
+
*/
|
|
222
|
+
startScreenShare(callId: string, payload?: string): void;
|
|
223
|
+
/** Signal that this user stopped sharing their screen in a 1:1 call. */
|
|
224
|
+
stopScreenShare(callId: string, payload?: string): void;
|
|
215
225
|
/** Register a listener for all incoming call events. Returns an unsubscribe function. */
|
|
216
226
|
onCallEvent(callback: CallEventCallback): () => void;
|
|
217
227
|
/** Fetch short-lived TURN credentials for ICE negotiation. */
|
|
@@ -244,6 +254,32 @@ export interface DropOnAirClient {
|
|
|
244
254
|
endGroupCall(callId: string): Promise<void>;
|
|
245
255
|
/** Send a signaling frame to a specific peer in a group call. */
|
|
246
256
|
sendGroupCallSignal(type: 'GROUP_CALL_SDP_OFFER' | 'GROUP_CALL_SDP_ANSWER' | 'GROUP_CALL_ICE_CANDIDATE', callId: string, groupId: string, targetUserId: string, payload: string): void;
|
|
257
|
+
/**
|
|
258
|
+
* Signal that this user started sharing their screen in a group call.
|
|
259
|
+
* The server enforces one concurrent sharer per group call; if another
|
|
260
|
+
* participant already holds the slot, the SDK will receive a
|
|
261
|
+
* `GROUP_CALL_SCREEN_SHARE_STOPPED` event echoing the current holder's
|
|
262
|
+
* userId in `payload` so the app can roll back its optimistic UI.
|
|
263
|
+
*/
|
|
264
|
+
startGroupScreenShare(callId: string, groupId: string, payload?: string): void;
|
|
265
|
+
/** Signal that this user stopped sharing their screen in a group call. */
|
|
266
|
+
stopGroupScreenShare(callId: string, groupId: string, payload?: string): void;
|
|
247
267
|
/** Register a listener for group call events. Returns an unsubscribe function. */
|
|
248
268
|
onGroupCallEvent(callback: GroupCallEventCallback): () => void;
|
|
269
|
+
/** Transfer host to another participant (current host only). */
|
|
270
|
+
transferHost(callId: string, groupId: string, newHostUserId: string): void;
|
|
271
|
+
/** Appoint a participant as co-host (host only). */
|
|
272
|
+
appointCoHost(callId: string, groupId: string, userId: string): void;
|
|
273
|
+
/** Revoke a participant's co-host status (host only). */
|
|
274
|
+
revokeCoHost(callId: string, groupId: string, userId: string): void;
|
|
275
|
+
/** Mute a participant's mic (host / co-host). Signaling hint; the target SDK applies it locally. */
|
|
276
|
+
muteParticipant(callId: string, groupId: string, targetUserId: string): void;
|
|
277
|
+
/** Remove a participant from the call (host / co-host). Server-enforced. */
|
|
278
|
+
removeParticipant(callId: string, groupId: string, targetUserId: string): void;
|
|
279
|
+
/** Request entry into a call's waiting room. Sent by would-be joiners; host receives WAITING_ROOM_JOINED. */
|
|
280
|
+
requestWaitingRoomEntry(callId: string, groupId: string): void;
|
|
281
|
+
/** Admit a user from the waiting room (host / co-host). */
|
|
282
|
+
admitFromWaitingRoom(callId: string, groupId: string, userId: string): void;
|
|
283
|
+
/** Reject a user waiting to join (host / co-host). */
|
|
284
|
+
rejectFromWaitingRoom(callId: string, groupId: string, userId: string): void;
|
|
249
285
|
}
|
package/dist/version.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* MINOR, additive feature (e.g. multi-device payloads, new call event type)
|
|
8
8
|
* PATCH, bug-fix / perf improvement with no wire or API change
|
|
9
9
|
*/
|
|
10
|
-
export declare const SDK_VERSION = "0.
|
|
10
|
+
export declare const SDK_VERSION = "0.7.0";
|
|
11
11
|
/**
|
|
12
12
|
* Binary encrypted-payload format version.
|
|
13
13
|
* Included as the first byte of every encrypted payload so receivers can
|
package/dist/version.js
CHANGED
|
@@ -10,7 +10,7 @@ exports.PROTOCOL_VERSION = exports.PAYLOAD_FORMAT_VERSION = exports.SDK_VERSION
|
|
|
10
10
|
* MINOR, additive feature (e.g. multi-device payloads, new call event type)
|
|
11
11
|
* PATCH, bug-fix / perf improvement with no wire or API change
|
|
12
12
|
*/
|
|
13
|
-
exports.SDK_VERSION = '0.
|
|
13
|
+
exports.SDK_VERSION = '0.7.0';
|
|
14
14
|
/**
|
|
15
15
|
* Binary encrypted-payload format version.
|
|
16
16
|
* Included as the first byte of every encrypted payload so receivers can
|