@anonotf/connect 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +2 -2
- package/src/calls.js +59 -0
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anonotf/connect",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Client SDK for AnonOtF — calls, group calls, live streaming, chat, and voice notes.",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Client SDK for AnonOtF — calls, group calls, live streaming, chat, and voice notes, without touching raw WebRTC or Socket.IO directly.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": ["src"],
|
package/src/calls.js
CHANGED
|
@@ -35,6 +35,8 @@ export class Calls {
|
|
|
35
35
|
this._localStream = null;
|
|
36
36
|
this.roomId = null; // set once a call is accepted/joined — null when idle
|
|
37
37
|
this._inCall = false; // true from accept/connect onward, used to decide whether to mesh-connect to newcomers
|
|
38
|
+
this._screenTrack = null; // active screen-capture track, if currently sharing
|
|
39
|
+
this._hadCameraBeforeShare = false; // so stopScreenShare() knows whether to restore the camera or just go video-off
|
|
38
40
|
|
|
39
41
|
this._socket.on('incoming-call', (data) => this._emit('incomingCall', data));
|
|
40
42
|
this._socket.on('call-accepted', (data) => this._onCallAccepted(data));
|
|
@@ -140,6 +142,61 @@ export class Calls {
|
|
|
140
142
|
}
|
|
141
143
|
}
|
|
142
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Starts sharing your screen instead of your camera — swaps the
|
|
147
|
+
* outgoing video track on every peer connection in the mesh, same
|
|
148
|
+
* mechanism as camera flip. Remembers whether you had a camera
|
|
149
|
+
* track running so stopScreenShare() can restore it afterward.
|
|
150
|
+
* Audio is untouched either way.
|
|
151
|
+
*
|
|
152
|
+
* Fires 'screenShareStarted'/'screenShareEnded' — the latter also
|
|
153
|
+
* fires automatically if the user stops sharing from the browser's
|
|
154
|
+
* own "Stop sharing" control rather than your UI.
|
|
155
|
+
*/
|
|
156
|
+
async startScreenShare() {
|
|
157
|
+
if (this._screenTrack) return; // already sharing
|
|
158
|
+
const displayStream = await navigator.mediaDevices.getDisplayMedia({ video: true, audio: false });
|
|
159
|
+
const screenTrack = displayStream.getVideoTracks()[0];
|
|
160
|
+
|
|
161
|
+
this._hadCameraBeforeShare = !!this._localStream?.getVideoTracks().length;
|
|
162
|
+
this._screenTrack = screenTrack;
|
|
163
|
+
|
|
164
|
+
await this.replaceVideoTrack(screenTrack);
|
|
165
|
+
this._emit('screenShareStarted');
|
|
166
|
+
|
|
167
|
+
// Browser's native "Stop sharing" UI ends the track directly —
|
|
168
|
+
// this catches that case so state stays accurate even if the
|
|
169
|
+
// person never calls stopScreenShare() themselves.
|
|
170
|
+
screenTrack.onended = () => {
|
|
171
|
+
if (this._screenTrack === screenTrack) this.stopScreenShare();
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Stops screen sharing and restores your camera (if you had one
|
|
177
|
+
* running before sharing started) across the whole mesh.
|
|
178
|
+
*/
|
|
179
|
+
async stopScreenShare() {
|
|
180
|
+
if (!this._screenTrack) return;
|
|
181
|
+
this._screenTrack.stop();
|
|
182
|
+
this._screenTrack = null;
|
|
183
|
+
|
|
184
|
+
if (this._hadCameraBeforeShare) {
|
|
185
|
+
const cameraStream = await navigator.mediaDevices.getUserMedia({ video: true });
|
|
186
|
+
await this.replaceVideoTrack(cameraStream.getVideoTracks()[0]);
|
|
187
|
+
} else if (this._localStream) {
|
|
188
|
+
// Wasn't sending video before sharing (audio-only call) — go
|
|
189
|
+
// back to that, rather than silently turning video on.
|
|
190
|
+
const track = this._localStream.getVideoTracks()[0];
|
|
191
|
+
if (track) { this._localStream.removeTrack(track); track.stop(); }
|
|
192
|
+
}
|
|
193
|
+
this._emit('screenShareEnded');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
get isScreenSharing() {
|
|
197
|
+
return !!this._screenTrack;
|
|
198
|
+
}
|
|
199
|
+
|
|
143
200
|
/** Mute/unmute your own mic across the whole mesh — just disables the local track, no renegotiation needed. */
|
|
144
201
|
setMicEnabled(enabled) {
|
|
145
202
|
this._localStream?.getAudioTracks().forEach((t) => { t.enabled = enabled; });
|
|
@@ -285,6 +342,8 @@ export class Calls {
|
|
|
285
342
|
_cleanupAll() {
|
|
286
343
|
this._localStream?.getTracks().forEach((t) => t.stop());
|
|
287
344
|
this._localStream = null;
|
|
345
|
+
this._screenTrack?.stop();
|
|
346
|
+
this._screenTrack = null;
|
|
288
347
|
for (const pc of this._peers.values()) pc.close();
|
|
289
348
|
this._peers.clear();
|
|
290
349
|
this.roomId = null;
|