@anonotf/connect 0.3.1 → 0.4.1
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 +65 -1
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.1",
|
|
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));
|
|
@@ -99,7 +101,12 @@ export class Calls {
|
|
|
99
101
|
end() {
|
|
100
102
|
this._socket.emit('end-call', { otherParty: this._anyPeerId() });
|
|
101
103
|
this._cleanupAll();
|
|
102
|
-
|
|
104
|
+
// Deliberately does NOT emit 'callEnded' here — you already know
|
|
105
|
+
// the call ended, you just called end(). Firing it anyway created
|
|
106
|
+
// an infinite loop for any consumer whose 'callEnded' handler
|
|
107
|
+
// reasonably calls end()/cleanup again (the natural thing to do).
|
|
108
|
+
// 'callEnded' is for when the call ends from elsewhere — the
|
|
109
|
+
// other party hanging up, a peer connection failing, etc.
|
|
103
110
|
}
|
|
104
111
|
|
|
105
112
|
/** Notify everyone in the call that you've started/stopped a local recording. */
|
|
@@ -140,6 +147,61 @@ export class Calls {
|
|
|
140
147
|
}
|
|
141
148
|
}
|
|
142
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Starts sharing your screen instead of your camera — swaps the
|
|
152
|
+
* outgoing video track on every peer connection in the mesh, same
|
|
153
|
+
* mechanism as camera flip. Remembers whether you had a camera
|
|
154
|
+
* track running so stopScreenShare() can restore it afterward.
|
|
155
|
+
* Audio is untouched either way.
|
|
156
|
+
*
|
|
157
|
+
* Fires 'screenShareStarted'/'screenShareEnded' — the latter also
|
|
158
|
+
* fires automatically if the user stops sharing from the browser's
|
|
159
|
+
* own "Stop sharing" control rather than your UI.
|
|
160
|
+
*/
|
|
161
|
+
async startScreenShare() {
|
|
162
|
+
if (this._screenTrack) return; // already sharing
|
|
163
|
+
const displayStream = await navigator.mediaDevices.getDisplayMedia({ video: true, audio: false });
|
|
164
|
+
const screenTrack = displayStream.getVideoTracks()[0];
|
|
165
|
+
|
|
166
|
+
this._hadCameraBeforeShare = !!this._localStream?.getVideoTracks().length;
|
|
167
|
+
this._screenTrack = screenTrack;
|
|
168
|
+
|
|
169
|
+
await this.replaceVideoTrack(screenTrack);
|
|
170
|
+
this._emit('screenShareStarted');
|
|
171
|
+
|
|
172
|
+
// Browser's native "Stop sharing" UI ends the track directly —
|
|
173
|
+
// this catches that case so state stays accurate even if the
|
|
174
|
+
// person never calls stopScreenShare() themselves.
|
|
175
|
+
screenTrack.onended = () => {
|
|
176
|
+
if (this._screenTrack === screenTrack) this.stopScreenShare();
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Stops screen sharing and restores your camera (if you had one
|
|
182
|
+
* running before sharing started) across the whole mesh.
|
|
183
|
+
*/
|
|
184
|
+
async stopScreenShare() {
|
|
185
|
+
if (!this._screenTrack) return;
|
|
186
|
+
this._screenTrack.stop();
|
|
187
|
+
this._screenTrack = null;
|
|
188
|
+
|
|
189
|
+
if (this._hadCameraBeforeShare) {
|
|
190
|
+
const cameraStream = await navigator.mediaDevices.getUserMedia({ video: true });
|
|
191
|
+
await this.replaceVideoTrack(cameraStream.getVideoTracks()[0]);
|
|
192
|
+
} else if (this._localStream) {
|
|
193
|
+
// Wasn't sending video before sharing (audio-only call) — go
|
|
194
|
+
// back to that, rather than silently turning video on.
|
|
195
|
+
const track = this._localStream.getVideoTracks()[0];
|
|
196
|
+
if (track) { this._localStream.removeTrack(track); track.stop(); }
|
|
197
|
+
}
|
|
198
|
+
this._emit('screenShareEnded');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
get isScreenSharing() {
|
|
202
|
+
return !!this._screenTrack;
|
|
203
|
+
}
|
|
204
|
+
|
|
143
205
|
/** Mute/unmute your own mic across the whole mesh — just disables the local track, no renegotiation needed. */
|
|
144
206
|
setMicEnabled(enabled) {
|
|
145
207
|
this._localStream?.getAudioTracks().forEach((t) => { t.enabled = enabled; });
|
|
@@ -285,6 +347,8 @@ export class Calls {
|
|
|
285
347
|
_cleanupAll() {
|
|
286
348
|
this._localStream?.getTracks().forEach((t) => t.stop());
|
|
287
349
|
this._localStream = null;
|
|
350
|
+
this._screenTrack?.stop();
|
|
351
|
+
this._screenTrack = null;
|
|
288
352
|
for (const pc of this._peers.values()) pc.close();
|
|
289
353
|
this._peers.clear();
|
|
290
354
|
this.roomId = null;
|