@afosecure/meetingsdk 1.0.7 → 1.1.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/LICENSE +1 -1
- package/dist/index.d.mts +4 -5
- package/dist/index.d.ts +4 -5
- package/dist/index.js +98 -60
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +99 -61
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/LICENSE
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import
|
|
3
|
-
import react__default from 'react';
|
|
2
|
+
import React from 'react';
|
|
4
3
|
|
|
5
4
|
type Events = {
|
|
6
5
|
onMicToggled?: (peerId: string, enabled: boolean) => void;
|
|
@@ -171,7 +170,7 @@ type MeetingContextValue = {
|
|
|
171
170
|
};
|
|
172
171
|
declare const MeetingProvider: ({ config, children, }: {
|
|
173
172
|
config: MeetingConfig;
|
|
174
|
-
children:
|
|
173
|
+
children: React.ReactNode;
|
|
175
174
|
}) => react_jsx_runtime.JSX.Element;
|
|
176
175
|
declare const useMeetingContext: () => MeetingContextValue;
|
|
177
176
|
|
|
@@ -201,8 +200,8 @@ declare const useMeeting: (handlers?: {
|
|
|
201
200
|
declare const useParticipants: () => Participant[];
|
|
202
201
|
|
|
203
202
|
declare const useRemoteMedia: (participantId: string) => {
|
|
204
|
-
videoRef:
|
|
205
|
-
audioRef:
|
|
203
|
+
videoRef: (node: HTMLVideoElement | null) => void;
|
|
204
|
+
audioRef: (node: HTMLAudioElement | null) => void;
|
|
206
205
|
isCamActive: boolean;
|
|
207
206
|
isMicEnabled: boolean;
|
|
208
207
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import
|
|
3
|
-
import react__default from 'react';
|
|
2
|
+
import React from 'react';
|
|
4
3
|
|
|
5
4
|
type Events = {
|
|
6
5
|
onMicToggled?: (peerId: string, enabled: boolean) => void;
|
|
@@ -171,7 +170,7 @@ type MeetingContextValue = {
|
|
|
171
170
|
};
|
|
172
171
|
declare const MeetingProvider: ({ config, children, }: {
|
|
173
172
|
config: MeetingConfig;
|
|
174
|
-
children:
|
|
173
|
+
children: React.ReactNode;
|
|
175
174
|
}) => react_jsx_runtime.JSX.Element;
|
|
176
175
|
declare const useMeetingContext: () => MeetingContextValue;
|
|
177
176
|
|
|
@@ -201,8 +200,8 @@ declare const useMeeting: (handlers?: {
|
|
|
201
200
|
declare const useParticipants: () => Participant[];
|
|
202
201
|
|
|
203
202
|
declare const useRemoteMedia: (participantId: string) => {
|
|
204
|
-
videoRef:
|
|
205
|
-
audioRef:
|
|
203
|
+
videoRef: (node: HTMLVideoElement | null) => void;
|
|
204
|
+
audioRef: (node: HTMLAudioElement | null) => void;
|
|
206
205
|
isCamActive: boolean;
|
|
207
206
|
isMicEnabled: boolean;
|
|
208
207
|
};
|
package/dist/index.js
CHANGED
|
@@ -415,7 +415,6 @@ var VideoSDKCore = class {
|
|
|
415
415
|
if (!p?.id || p.id === this.myId) continue;
|
|
416
416
|
this.state.addParticipant(p);
|
|
417
417
|
this.events.onUserJoined?.(p);
|
|
418
|
-
await this.createOffer(p.id);
|
|
419
418
|
}
|
|
420
419
|
break;
|
|
421
420
|
case "JOINED": {
|
|
@@ -438,8 +437,8 @@ var VideoSDKCore = class {
|
|
|
438
437
|
case "ANSWER": {
|
|
439
438
|
const pc = this.peers[msg.sender];
|
|
440
439
|
if (!pc) return;
|
|
441
|
-
if (pc.signalingState
|
|
442
|
-
console.warn("
|
|
440
|
+
if (pc.signalingState === "stable") {
|
|
441
|
+
console.warn("Late answer received, restarting negotiation");
|
|
443
442
|
return;
|
|
444
443
|
}
|
|
445
444
|
await pc.setRemoteDescription({
|
|
@@ -541,6 +540,14 @@ var VideoSDKCore = class {
|
|
|
541
540
|
// ---------------- PEER ----------------
|
|
542
541
|
createPeer(id) {
|
|
543
542
|
if (!this.localStream) throw new Error("No local stream");
|
|
543
|
+
console.log(
|
|
544
|
+
"Adding tracks",
|
|
545
|
+
this.localStream.getTracks().map((t) => ({
|
|
546
|
+
kind: t.kind,
|
|
547
|
+
enabled: t.enabled,
|
|
548
|
+
state: t.readyState
|
|
549
|
+
}))
|
|
550
|
+
);
|
|
544
551
|
const pc = new RTCPeerConnection({
|
|
545
552
|
iceServers: [
|
|
546
553
|
{
|
|
@@ -569,7 +576,8 @@ var VideoSDKCore = class {
|
|
|
569
576
|
} else {
|
|
570
577
|
this.state.updateParticipantMedia(id, {
|
|
571
578
|
stream: incomingStream,
|
|
572
|
-
cameraTrack: incomingStream.getVideoTracks()[0]
|
|
579
|
+
cameraTrack: incomingStream.getVideoTracks()[0],
|
|
580
|
+
audioTrack: incomingStream.getAudioTracks()[0]
|
|
573
581
|
});
|
|
574
582
|
this.events.onTrack?.(incomingStream, id);
|
|
575
583
|
}
|
|
@@ -605,17 +613,10 @@ var VideoSDKCore = class {
|
|
|
605
613
|
}
|
|
606
614
|
// ---------------- OFFER ----------------
|
|
607
615
|
async createOffer(id, isRenegotiation = false) {
|
|
608
|
-
if (!isRenegotiation && this.initiators.has(id)) return;
|
|
609
|
-
if (!isRenegotiation) {
|
|
610
|
-
this.initiators.add(id);
|
|
611
|
-
}
|
|
612
616
|
if (!this.peers[id]) {
|
|
613
617
|
this.peers[id] = this.createPeer(id);
|
|
614
618
|
}
|
|
615
619
|
const pc = this.peers[id];
|
|
616
|
-
if (pc.signalingState !== "stable") {
|
|
617
|
-
return;
|
|
618
|
-
}
|
|
619
620
|
const offer = await pc.createOffer();
|
|
620
621
|
await pc.setLocalDescription(offer);
|
|
621
622
|
this.send({
|
|
@@ -667,39 +668,55 @@ var VideoSDKCore = class {
|
|
|
667
668
|
this.state.removeParticipant(id);
|
|
668
669
|
}
|
|
669
670
|
async startScreenShare() {
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
this.screenStream = await navigator.mediaDevices.getDisplayMedia({
|
|
674
|
-
video: true,
|
|
675
|
-
audio: true
|
|
676
|
-
});
|
|
677
|
-
this.isScreenSharing = true;
|
|
678
|
-
this.state.updateLocalParticipant({
|
|
679
|
-
media: {
|
|
680
|
-
isScreenSharing: true,
|
|
681
|
-
screenStream: this.screenStream
|
|
671
|
+
try {
|
|
672
|
+
if (this.state.presenterId && this.state.presenterId !== this.myId) {
|
|
673
|
+
throw new Error("Another user is already sharing their screen.");
|
|
682
674
|
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
this.
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
this.screenSenders[peerId] = [];
|
|
690
|
-
this.screenStream.getTracks().forEach((track) => {
|
|
691
|
-
const sender = pc.addTrack(track, this.screenStream);
|
|
692
|
-
this.screenSenders[peerId].push(sender);
|
|
675
|
+
if (!navigator.mediaDevices?.getDisplayMedia) {
|
|
676
|
+
throw new Error("Screen sharing not supported on this device");
|
|
677
|
+
}
|
|
678
|
+
this.screenStream = await navigator.mediaDevices.getDisplayMedia({
|
|
679
|
+
video: true
|
|
680
|
+
// audio: true,
|
|
693
681
|
});
|
|
694
|
-
this.
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
682
|
+
this.isScreenSharing = true;
|
|
683
|
+
this.state.updateLocalParticipant({
|
|
684
|
+
media: {
|
|
685
|
+
isScreenSharing: true,
|
|
686
|
+
screenStream: this.screenStream,
|
|
687
|
+
screenTrack: this.screenStream.getVideoTracks()[0]
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
this.state.setPresenterId(this.myId);
|
|
691
|
+
this.screenStream.getVideoTracks()[0].onended = () => {
|
|
692
|
+
this.stopScreenShare();
|
|
693
|
+
};
|
|
694
|
+
Object.entries(this.peers).forEach(([peerId, pc]) => {
|
|
695
|
+
this.screenSenders[peerId] = [];
|
|
696
|
+
this.screenStream.getTracks().forEach((track) => {
|
|
697
|
+
const sender = pc.addTrack(track, this.screenStream);
|
|
698
|
+
this.screenSenders[peerId].push(sender);
|
|
699
|
+
});
|
|
700
|
+
this.createOffer(peerId, true);
|
|
701
|
+
});
|
|
702
|
+
this.send({
|
|
703
|
+
type: "SCREEN_SHARE_START",
|
|
704
|
+
sender: this.myId,
|
|
705
|
+
room_id: this.roomId,
|
|
706
|
+
stream_id: this.screenStream.id.replace(/[{}]/g, "")
|
|
707
|
+
});
|
|
708
|
+
return this.screenStream;
|
|
709
|
+
} catch (err) {
|
|
710
|
+
this.emitError(
|
|
711
|
+
"SCREEN_SHARE_FAILED",
|
|
712
|
+
err?.message || "Failed to start screen sharing",
|
|
713
|
+
err,
|
|
714
|
+
true
|
|
715
|
+
);
|
|
716
|
+
this.isScreenSharing = false;
|
|
717
|
+
this.screenStream = null;
|
|
718
|
+
throw err;
|
|
719
|
+
}
|
|
703
720
|
}
|
|
704
721
|
stopScreenShare() {
|
|
705
722
|
if (!this.screenStream) return;
|
|
@@ -937,7 +954,6 @@ var useLocalParticipant = () => {
|
|
|
937
954
|
video.srcObject = stream;
|
|
938
955
|
video.autoplay = true;
|
|
939
956
|
video.playsInline = true;
|
|
940
|
-
video.muted = true;
|
|
941
957
|
video.play().catch((err) => {
|
|
942
958
|
console.warn(`Autoplay failed for local view:`, err);
|
|
943
959
|
});
|
|
@@ -984,31 +1000,53 @@ var useParticipants = () => {
|
|
|
984
1000
|
var import_react6 = require("react");
|
|
985
1001
|
var useRemoteMedia = (participantId) => {
|
|
986
1002
|
const { sdk } = useMeetingContext();
|
|
987
|
-
const videoRef = (0, import_react6.useRef)(null);
|
|
988
|
-
const audioRef = (0, import_react6.useRef)(null);
|
|
989
1003
|
const [participant, setParticipant] = (0, import_react6.useState)(
|
|
990
1004
|
() => sdk.state.getParticipant(participantId) || null
|
|
991
1005
|
);
|
|
1006
|
+
const buildMediaStream = (participant2) => {
|
|
1007
|
+
if (!participant2?.media) return null;
|
|
1008
|
+
const stream = new MediaStream();
|
|
1009
|
+
const videoTrack = participant2.media.stream?.getVideoTracks?.()?.[0];
|
|
1010
|
+
const audioTrack = participant2.media.stream?.getAudioTracks?.()?.[0];
|
|
1011
|
+
if (videoTrack) stream.addTrack(videoTrack);
|
|
1012
|
+
if (audioTrack) stream.addTrack(audioTrack);
|
|
1013
|
+
return stream;
|
|
1014
|
+
};
|
|
992
1015
|
(0, import_react6.useEffect)(() => {
|
|
993
1016
|
const unsub = sdk.state.subscribe(`participant:${participantId}`, () => {
|
|
994
|
-
const
|
|
995
|
-
|
|
996
|
-
setParticipant({ ...updated });
|
|
997
|
-
}
|
|
1017
|
+
const p = sdk.state.getParticipant(participantId);
|
|
1018
|
+
setParticipant(p ? { ...p } : null);
|
|
998
1019
|
});
|
|
999
1020
|
return unsub;
|
|
1000
1021
|
}, [participantId, sdk]);
|
|
1001
|
-
(0, import_react6.
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1022
|
+
const videoRef = (0, import_react6.useCallback)(
|
|
1023
|
+
(node) => {
|
|
1024
|
+
if (!node) return;
|
|
1025
|
+
const stream = participant?.media?.stream;
|
|
1026
|
+
if (!stream) return;
|
|
1027
|
+
node.pause();
|
|
1028
|
+
node.srcObject = stream;
|
|
1029
|
+
node.muted = true;
|
|
1030
|
+
node.playsInline = true;
|
|
1031
|
+
node.autoplay = true;
|
|
1032
|
+
node.play().catch(() => {
|
|
1033
|
+
});
|
|
1034
|
+
},
|
|
1035
|
+
[participant?.media?.stream]
|
|
1036
|
+
);
|
|
1037
|
+
const audioRef = (0, import_react6.useCallback)(
|
|
1038
|
+
(node) => {
|
|
1039
|
+
if (!node) return;
|
|
1040
|
+
const stream = participant?.media?.stream;
|
|
1041
|
+
if (!stream) return;
|
|
1042
|
+
node.pause();
|
|
1043
|
+
node.srcObject = stream;
|
|
1044
|
+
node.muted = false;
|
|
1045
|
+
node.play().catch(() => {
|
|
1046
|
+
});
|
|
1047
|
+
},
|
|
1048
|
+
[participant?.media?.stream]
|
|
1049
|
+
);
|
|
1012
1050
|
return {
|
|
1013
1051
|
videoRef,
|
|
1014
1052
|
audioRef,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/react/useLocalParticipant.tsx","../src/react/MeetingProvider.tsx","../src/config/ws.ts","../src/core/MeetingState.ts","../src/core/VideoCore.ts","../src/react/useMeetingStore.ts","../src/react/useMeeting.ts","../src/react/useParticipants.ts","../src/react/useRemoteMedia.ts"],"sourcesContent":["export { useLocalParticipant } from \"./react/useLocalParticipant\";\r\n\r\n// Core exports\r\nexport { VideoSDKCore } from \"./core/VideoCore\";\r\nexport { MeetingState } from \"./core/MeetingState\";\r\nexport type { Participant, ChatInput } from \"./types/meeting\";\r\n\r\n// React hooks and components\r\nexport { MeetingProvider, useMeetingContext } from \"./react/MeetingProvider\";\r\nexport { useMeeting } from \"./react/useMeeting\";\r\nexport { useParticipants } from \"./react/useParticipants\";\r\nexport { useRemoteMedia } from \"./react/useRemoteMedia\";\r\n","import { useCallback, useEffect, useRef, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useLocalParticipant = () => {\r\n const { sdk } = useMeetingContext();\r\n\r\n // Guard the initial state to ensure it matches Participant | null\r\n const [localParticipant, setLocalParticipant] = useState<Participant | null>(\r\n () => {\r\n const current = sdk.state.localParticipant;\r\n return current && current.id ? (current as Participant) : null;\r\n },\r\n );\r\n\r\n useEffect(() => {\r\n const unsubscribe = sdk.state.subscribe(\"localParticipant\", () => {\r\n const current = sdk.state.localParticipant;\r\n\r\n // Safe Type-Guard: Only update if the object has a finalized id\r\n if (current && current.id) {\r\n setLocalParticipant({ ...current } as Participant);\r\n } else {\r\n setLocalParticipant(null);\r\n }\r\n });\r\n\r\n return unsubscribe;\r\n }, [sdk]);\r\n\r\n const lastStreamRef = useRef<MediaStream | null>(null);\r\n\r\n const videoRef = useCallback(\r\n (video: HTMLVideoElement | null) => {\r\n if (!video) return;\r\n\r\n const stream = localParticipant?.media?.stream;\r\n if (!stream) return;\r\n\r\n if (lastStreamRef.current === stream) return;\r\n lastStreamRef.current = stream;\r\n\r\n video.srcObject = stream;\r\n video.autoplay = true;\r\n video.playsInline = true;\r\n video.muted = true;\r\n\r\n video.play().catch((err) => {\r\n console.warn(`Autoplay failed for local view:`, err);\r\n });\r\n },\r\n [localParticipant?.media?.stream],\r\n );\r\n\r\n return {\r\n participant: localParticipant,\r\n videoRef,\r\n };\r\n};\r\n","import React, { createContext, useContext, useMemo, useRef } from \"react\";\r\nimport { VideoSDKCore } from \"../core/VideoCore\";\r\nimport {\r\n ChatInput,\r\n ChatMessage,\r\n MeetingConfig,\r\n Participant,\r\n PubSubTopic,\r\n} from \"../types/meeting\";\r\nimport { useMeetingStore } from \"./useMeetingStore\";\r\n\r\ntype PubSubHandle = {\r\n messages: ChatMessage[];\r\n publish: (input: ChatInput) => void;\r\n};\r\n\r\ntype MeetingContextValue = {\r\n sdk: VideoSDKCore;\r\n\r\n join: (config: MeetingConfig) => Promise<void>;\r\n leave: () => void;\r\n\r\n toggleMic: () => void;\r\n toggleCam: () => void;\r\n\r\n startScreenShare: () => Promise<MediaStream>;\r\n stopScreenShare: () => void;\r\n\r\n sendMessage: (input: ChatInput) => void;\r\n\r\n meetingId: string | null;\r\n localParticipant: Participant | null;\r\n participants: Map<string, Participant>;\r\n messages: ChatMessage[];\r\n presenterId: string | null;\r\n usePubSub: (topic: PubSubTopic) => PubSubHandle;\r\n onError: (cb: (err: any) => void) => () => void;\r\n};\r\n\r\nconst MeetingContext = createContext<MeetingContextValue | null>(null);\r\n\r\nexport const MeetingProvider = ({\r\n config,\r\n children,\r\n}: {\r\n config: MeetingConfig;\r\n children: React.ReactNode;\r\n}) => {\r\n const sdkRef = useRef<VideoSDKCore | null>(null);\r\n const errorListeners = useRef(new Set<(err: any) => void>());\r\n if (!sdkRef.current) {\r\n sdkRef.current = new VideoSDKCore({\r\n onError: (err) => {\r\n errorListeners.current.forEach((fn) => fn(err));\r\n },\r\n });\r\n }\r\n\r\n const sdk = sdkRef.current;\r\n const presenterId = useMeetingStore(\r\n sdk.state,\r\n \"presenter\",\r\n (s) => s.presenterId,\r\n );\r\n const participants = useMeetingStore(\r\n sdk.state,\r\n \"participants\",\r\n (s) => s.participants,\r\n );\r\n const localParticipant = useMeetingStore(\r\n sdk.state,\r\n \"localParticipant\",\r\n (s) => s.localParticipant,\r\n );\r\n const messages = useMeetingStore(sdk.state, \"chat\", (s) =>\r\n s.getChatMessages(),\r\n );\r\n\r\n const value = useMemo<MeetingContextValue>(() => {\r\n if (!sdkRef.current) {\r\n sdkRef.current = new VideoSDKCore({\r\n onError: (err) => {\r\n errorListeners.current.forEach((fn) => fn(err));\r\n },\r\n });\r\n }\r\n\r\n return {\r\n sdk,\r\n\r\n join: (joinConfig: MeetingConfig) =>\r\n sdk.joinMeeting({\r\n ...config,\r\n ...joinConfig,\r\n }),\r\n leave: () => sdk.disconnect(),\r\n toggleMic: sdk.toggleMic.bind(sdk),\r\n toggleCam: sdk.toggleCam.bind(sdk),\r\n startScreenShare: sdk.startScreenShare.bind(sdk),\r\n stopScreenShare: sdk.stopScreenShare.bind(sdk),\r\n sendMessage: sdk.sendChatMessage.bind(sdk),\r\n\r\n meetingId: sdk.getMeetingId(),\r\n localParticipant,\r\n participants,\r\n messages,\r\n presenterId,\r\n usePubSub: (topic: PubSubTopic) => {\r\n if (topic !== \"SECURE_CHAT\") {\r\n throw new Error(`Unsupported PubSub argument: \"${topic}\"`);\r\n }\r\n return {\r\n messages: sdk.state.getChatMessages(),\r\n publish: sdk.sendChatMessage.bind(sdk),\r\n };\r\n },\r\n onError: (cb: (err: any) => void) => {\r\n errorListeners.current.add(cb);\r\n\r\n return () => {\r\n errorListeners.current.delete(cb);\r\n };\r\n },\r\n };\r\n }, [config, sdk, localParticipant, participants, messages, presenterId]);\r\n\r\n return (\r\n <MeetingContext.Provider value={value}>{children}</MeetingContext.Provider>\r\n );\r\n};\r\n\r\nexport const useMeetingContext = () => {\r\n const ctx = useContext(MeetingContext);\r\n if (!ctx)\r\n throw new Error(\"useMeetingContext must be used inside <MeetingProvider>\");\r\n return ctx;\r\n};\r\n","export const SDK_CONFIG = {\r\n wsUrl: \"wss://rust-video-server-sfyf.onrender.com/ws\",\r\n};\r\n","import {\r\n ChatMessage,\r\n Listener,\r\n Participant,\r\n ParticipantMedia,\r\n StateScope,\r\n} from \"../types/meeting\";\r\n\r\ntype LocalParticipantPatch = {\r\n id?: string;\r\n name?: string;\r\n media?: Partial<ParticipantMedia>;\r\n};\r\n\r\nexport class MeetingState {\r\n participants = new Map<string, Participant>();\r\n localParticipant: Participant | null = null;\r\n localStream: MediaStream | null = null;\r\n chatMessages = new Map<string, ChatMessage>();\r\n presenterId: string | null = null;\r\n\r\n private listeners = new Map<StateScope, Set<Listener>>();\r\n\r\n // ---- reactive system ----\r\n\r\n subscribe(scope: StateScope, fn: Listener): () => void {\r\n if (!this.listeners.has(scope)) {\r\n this.listeners.set(scope, new Set());\r\n }\r\n this.listeners.get(scope)!.add(fn);\r\n\r\n return () => {\r\n this.listeners.get(scope)?.delete(fn);\r\n };\r\n }\r\n\r\n notify(scope: StateScope) {\r\n this.listeners.get(scope)?.forEach((fn) => fn());\r\n }\r\n\r\n setPresenterId(id: string | null) {\r\n if (this.presenterId === id) return;\r\n this.presenterId = id;\r\n this.notify(\"presenter\");\r\n this.notify(\"participants\");\r\n }\r\n\r\n // ---- participants ----\r\n\r\n addParticipant(p: Participant) {\r\n if (this.participants.has(p.id)) return false;\r\n // Fix: Immutable Map update\r\n const next = new Map(this.participants);\r\n next.set(p.id, p);\r\n this.participants = next;\r\n\r\n this.notify(\"participants\");\r\n return true;\r\n }\r\n\r\n removeParticipant(id: string) {\r\n // Fix: Immutable Map update\r\n const next = new Map(this.participants);\r\n next.delete(id);\r\n this.participants = next;\r\n\r\n this.notify(\"participants\");\r\n }\r\n\r\n updateParticipantMedia(\r\n id: string,\r\n patch: Partial<NonNullable<Participant[\"media\"]>>,\r\n ) {\r\n const p = this.participants.get(id);\r\n if (!p) return;\r\n\r\n const updated: Participant = {\r\n ...p,\r\n media: {\r\n stream: null,\r\n screenStream: undefined,\r\n cameraTrack: undefined,\r\n screenTrack: undefined,\r\n audioTrack: undefined,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n ...p.media, // preserve existing media items if they happen to exist\r\n ...patch, // apply the incoming stream updates\r\n },\r\n };\r\n\r\n // Keep your clean immutable map update\r\n const next = new Map(this.participants);\r\n next.set(id, updated);\r\n this.participants = next;\r\n\r\n this.notify(`participant:${id}`);\r\n this.notify(\"participants\");\r\n }\r\n\r\n updateLocalParticipant(patch: LocalParticipantPatch) {\r\n const prev = this.localParticipant;\r\n\r\n if (!prev) {\r\n this.localParticipant = {\r\n id: patch.id ?? \"\",\r\n name: patch.name ?? \"\",\r\n media: {\r\n stream: patch.media?.stream ?? null, // ◄ FIX: Capture the stream from the patch here\r\n screenStream: patch.media?.screenStream,\r\n cameraTrack: patch.media?.cameraTrack,\r\n screenTrack: patch.media?.screenTrack,\r\n audioTrack: patch.media?.audioTrack,\r\n micEnabled: patch.media?.micEnabled ?? true,\r\n camEnabled: patch.media?.camEnabled ?? true,\r\n isScreenSharing: patch.media?.isScreenSharing ?? false,\r\n },\r\n };\r\n\r\n this.notify(\"localParticipant\");\r\n return;\r\n }\r\n\r\n const prevMedia = prev.media ?? {\r\n stream: null,\r\n screenStream: undefined,\r\n cameraTrack: undefined,\r\n screenTrack: undefined,\r\n audioTrack: undefined,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n };\r\n\r\n const nextMedia: ParticipantMedia = {\r\n stream: patch.media?.stream ?? prevMedia.stream,\r\n screenStream: patch.media?.screenStream ?? prevMedia.screenStream,\r\n cameraTrack: patch.media?.cameraTrack ?? prevMedia.cameraTrack,\r\n screenTrack: patch.media?.screenTrack ?? prevMedia.screenTrack,\r\n audioTrack: patch.media?.audioTrack ?? prevMedia.audioTrack,\r\n micEnabled: patch.media?.micEnabled ?? prevMedia.micEnabled,\r\n camEnabled: patch.media?.camEnabled ?? prevMedia.camEnabled,\r\n isScreenSharing:\r\n patch.media?.isScreenSharing ?? prevMedia.isScreenSharing,\r\n };\r\n\r\n this.localParticipant = {\r\n ...prev,\r\n id: patch.id ?? prev.id,\r\n name: patch.name ?? prev.name,\r\n media: nextMedia,\r\n };\r\n\r\n this.notify(\"localParticipant\");\r\n }\r\n\r\n // ---- chat ----\r\n\r\n addChatMessage(msg: ChatMessage) {\r\n this.chatMessages.set(msg.id, msg);\r\n this.notify(\"chat\");\r\n }\r\n\r\n getChatMessages() {\r\n return Array.from(this.chatMessages.values()).sort(\r\n (a, b) => a.timestamp - b.timestamp,\r\n );\r\n }\r\n\r\n clearChat() {\r\n this.chatMessages.clear();\r\n this.notify(\"chat\");\r\n }\r\n\r\n // ---- helpers ----\r\n\r\n getParticipants() {\r\n return Array.from(this.participants.values());\r\n }\r\n\r\n getParticipant(id: string) {\r\n return this.participants.get(id) ?? null;\r\n }\r\n\r\n resetRemoteState() {\r\n this.participants.clear();\r\n this.chatMessages.clear();\r\n this.presenterId = null;\r\n this.notify(\"participants\");\r\n this.notify(\"chat\");\r\n this.notify(\"presenter\");\r\n }\r\n}\r\n","import { SDK_CONFIG } from \"../config/ws\";\r\nimport {\r\n ChatInput,\r\n ChatMessage,\r\n Events,\r\n MeetingConfig,\r\n SDKError,\r\n} from \"../types/meeting\";\r\nimport { MeetingState } from \"./MeetingState\";\r\n\r\nexport class VideoSDKCore {\r\n private ws: WebSocket | null = null;\r\n private peers: Record<string, RTCPeerConnection> = {};\r\n private initiators = new Set<string>();\r\n\r\n private myId: string;\r\n private roomId: string | null = null;\r\n private localStream: MediaStream | null = null;\r\n private screenStream: MediaStream | null = null;\r\n private isScreenSharing = false;\r\n private screenSenders: Record<string, RTCRtpSender[]> = {};\r\n\r\n private pingInterval: any = null;\r\n private pendingIceCandidates: Record<string, RTCIceCandidateInit[]> = {};\r\n private reconnectAttempts = 0;\r\n private reconnectTimer?: number;\r\n private participantName = \"\";\r\n public readonly state: MeetingState;\r\n private joinResolver?: () => void;\r\n private joinRejecter?: (e: any) => void;\r\n private emitError(\r\n code: string,\r\n message: string,\r\n raw?: any,\r\n recoverable = true,\r\n ) {\r\n const err: SDKError = {\r\n code,\r\n message,\r\n raw,\r\n roomId: this.roomId,\r\n userId: this.myId,\r\n recoverable,\r\n };\r\n\r\n this.events.onError?.(err);\r\n\r\n this.joinRejecter?.(err);\r\n this.joinRejecter = undefined;\r\n\r\n console.error(\"[MeetingSDK Error]\", err);\r\n }\r\n\r\n constructor(\r\n private events: Events = {},\r\n private url: string = SDK_CONFIG.wsUrl,\r\n ) {\r\n this.state = new MeetingState();\r\n this.events = events;\r\n this.url = url;\r\n\r\n this.myId = localStorage.getItem(\"vsdk_id\") || crypto.randomUUID();\r\n\r\n localStorage.setItem(\"vsdk_id\", this.myId);\r\n }\r\n\r\n // ---------------- STREAM ----------------\r\n async initLocal(video: HTMLVideoElement, name: string) {\r\n this.participantName = name;\r\n\r\n if (!this.localStream) {\r\n this.localStream = await navigator.mediaDevices.getUserMedia({\r\n video: true,\r\n audio: true,\r\n });\r\n }\r\n\r\n video.srcObject = this.localStream;\r\n\r\n // Fix: Supply mandatory fields to satisfy the Participant type constraint\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n stream: this.localStream,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n },\r\n });\r\n\r\n this.state.localStream = this.localStream;\r\n }\r\n\r\n // ---------------- CONNECT ----------------\r\n async connect(roomId: string, name: string) {\r\n this.roomId = roomId;\r\n\r\n this.reset();\r\n\r\n return new Promise<void>((resolve, reject) => {\r\n this.joinResolver = resolve;\r\n this.joinRejecter = reject;\r\n this.ws = new WebSocket(this.url);\r\n\r\n this.ws.onopen = () => {\r\n this.send({\r\n type: \"JOIN\",\r\n room_id: roomId,\r\n user_id: this.myId,\r\n sender_name: name,\r\n });\r\n };\r\n\r\n this.ws.onerror = (err) => {\r\n this.emitError(\"WS_ERROR\", \"WebSocket encountered an error\", err, true);\r\n };\r\n\r\n this.ws.onclose = (e) => {\r\n this.emitError(\r\n \"WS_CLOSED\",\r\n `Connection closed (${e.code}) ${e.reason || \"\"}`,\r\n e,\r\n true,\r\n );\r\n\r\n // If join never resolved, fail the promise\r\n this.joinRejecter?.({\r\n code: \"WS_CLOSED\",\r\n message: \"Connection closed before join completed\",\r\n raw: e,\r\n });\r\n\r\n this.joinRejecter = undefined;\r\n\r\n this.scheduleReconnect();\r\n };\r\n\r\n this.ws.onmessage = async (e) => {\r\n await this.handle(JSON.parse(e.data));\r\n };\r\n });\r\n }\r\n\r\n async joinMeeting(config: MeetingConfig) {\r\n const { roomId, name, audioMuted = false, videoMuted = false } = config;\r\n\r\n if (!roomId || !name) {\r\n throw new Error(\"roomId and name are required to join meeting\");\r\n }\r\n\r\n this.participantName = name;\r\n\r\n // Reuse existing stream if initLocal already configured it\r\n if (!this.localStream) {\r\n this.localStream = await navigator.mediaDevices.getUserMedia({\r\n video: true,\r\n audio: true,\r\n });\r\n }\r\n\r\n this.localStream.getAudioTracks().forEach((t) => {\r\n t.enabled = !audioMuted;\r\n });\r\n this.localStream.getVideoTracks().forEach((t) => {\r\n t.enabled = !videoMuted;\r\n });\r\n\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n stream: this.localStream,\r\n micEnabled: !audioMuted,\r\n camEnabled: !videoMuted,\r\n isScreenSharing: false,\r\n },\r\n });\r\n\r\n this.state.localStream = this.localStream;\r\n\r\n await this.connect(roomId, name);\r\n }\r\n\r\n /** Expose the roomId without making it fully public */\r\n getMeetingId(): string | null {\r\n return this.roomId;\r\n }\r\n\r\n toggleMic() {\r\n const mediaState = this.state.localParticipant?.media;\r\n if (!mediaState) return;\r\n\r\n // Flip the state tracked in localParticipant.media\r\n const nextEnabled = !mediaState.micEnabled;\r\n\r\n // Mirror the state change down to the actual hardware tracks\r\n this.localStream\r\n ?.getAudioTracks()\r\n .forEach((t) => (t.enabled = nextEnabled));\r\n\r\n // Update state layer\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n ...mediaState,\r\n micEnabled: nextEnabled,\r\n },\r\n });\r\n\r\n // Notify peers\r\n this.send({\r\n type: \"MEDIA_STATE\",\r\n kind: \"audio\",\r\n enabled: nextEnabled,\r\n });\r\n }\r\n\r\n toggleCam() {\r\n const mediaState = this.state.localParticipant?.media;\r\n if (!mediaState) return;\r\n\r\n // Flip the state tracked in localParticipant.media\r\n const nextEnabled = !mediaState.camEnabled;\r\n\r\n // Mirror the state change down to the actual hardware tracks\r\n this.localStream\r\n ?.getVideoTracks()\r\n .forEach((t) => (t.enabled = nextEnabled));\r\n\r\n // Update state layer\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n ...mediaState,\r\n camEnabled: nextEnabled,\r\n },\r\n });\r\n\r\n // Notify peers\r\n this.send({\r\n type: \"MEDIA_STATE\",\r\n kind: \"video\",\r\n enabled: nextEnabled,\r\n });\r\n }\r\n\r\n private scheduleReconnect() {\r\n if (!this.roomId) return;\r\n\r\n const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);\r\n\r\n clearTimeout(this.reconnectTimer);\r\n\r\n this.reconnectTimer = window.setTimeout(async () => {\r\n try {\r\n await this.connect(this.roomId!, this.participantName);\r\n\r\n this.reconnectAttempts = 0;\r\n } catch {\r\n this.reconnectAttempts++;\r\n this.scheduleReconnect();\r\n }\r\n }, delay);\r\n }\r\n\r\n private startHeartbeat() {\r\n this.stopHeartbeat();\r\n\r\n this.pingInterval = setInterval(() => {\r\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;\r\n\r\n this.send({\r\n type: \"PING\",\r\n client_ts: Date.now(),\r\n });\r\n }, 20000); // every 20s\r\n }\r\n private stopHeartbeat() {\r\n if (this.pingInterval) {\r\n clearInterval(this.pingInterval);\r\n this.pingInterval = null;\r\n }\r\n }\r\n\r\n // ---------------- RESET ----------------\r\n private reset() {\r\n Object.values(this.peers).forEach((pc) => pc.close());\r\n\r\n this.peers = {};\r\n this.initiators.clear();\r\n this.pendingIceCandidates = {};\r\n\r\n this.state.resetRemoteState();\r\n }\r\n\r\n // ---------------- HANDLE SIGNALS ----------------\r\n private async handle(msg: any) {\r\n if (msg.sender === this.myId) return;\r\n\r\n switch (msg.type) {\r\n case \"EXISTING_USERS\":\r\n if (msg.presenterId) {\r\n this.state.setPresenterId(msg.presenterId);\r\n\r\n // Trigger your event so the UI knows to render the stage\r\n this.events.onScreenShareStarted?.(msg.presenterId, null!);\r\n }\r\n\r\n for (const p of msg.participants || []) {\r\n if (!p?.id || p.id === this.myId) continue;\r\n this.state.addParticipant(p);\r\n this.events.onUserJoined?.(p);\r\n await this.createOffer(p.id);\r\n }\r\n break;\r\n\r\n case \"JOINED\": {\r\n this.startHeartbeat();\r\n this.joinResolver?.();\r\n this.joinResolver = undefined;\r\n this.joinRejecter = undefined;\r\n break;\r\n }\r\n case \"USER_JOINED\": {\r\n const p = msg.participant;\r\n\r\n if (!p?.id || p.id === this.myId) return;\r\n\r\n this.state.addParticipant(p);\r\n\r\n this.events.onUserJoined?.(p);\r\n\r\n break;\r\n }\r\n\r\n case \"OFFER\":\r\n await this.handleOffer(msg.payload, msg.sender);\r\n break;\r\n\r\n case \"ANSWER\": {\r\n const pc = this.peers[msg.sender];\r\n if (!pc) return;\r\n\r\n if (pc.signalingState !== \"have-local-offer\") {\r\n console.warn(\"Ignoring invalid answer:\", pc.signalingState);\r\n return;\r\n }\r\n\r\n await pc.setRemoteDescription({\r\n type: \"answer\",\r\n sdp: msg.payload,\r\n });\r\n\r\n // CRITICAL WEBRTC FIX: Flush cached ICE paths now that remoteDescription is set!\r\n await this.flushIce(msg.sender, pc);\r\n\r\n break;\r\n }\r\n\r\n case \"ICE\": {\r\n const candidate = JSON.parse(msg.payload);\r\n\r\n let pc = this.peers[msg.sender];\r\n\r\n if (!pc) {\r\n this.pendingIceCandidates[msg.sender] ??= [];\r\n this.pendingIceCandidates[msg.sender].push(candidate);\r\n break;\r\n }\r\n\r\n if (!pc.remoteDescription) {\r\n this.pendingIceCandidates[msg.sender] ??= [];\r\n this.pendingIceCandidates[msg.sender].push(candidate);\r\n break;\r\n }\r\n\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (err) {\r\n console.warn(\"ICE error:\", err);\r\n }\r\n\r\n break;\r\n }\r\n\r\n case \"USER_LEFT\":\r\n const peerId = msg.participant.id;\r\n this.closePeer(peerId);\r\n\r\n this.state.removeParticipant(peerId);\r\n\r\n this.events.onUserLeft?.(peerId);\r\n\r\n break;\r\n\r\n case \"MEDIA_STATE_CHANGE\": {\r\n const peerId = msg.peerId;\r\n const { kind, enabled } = msg;\r\n\r\n // 1. Sync the app state layer for UI rendering components\r\n if (kind === \"audio\") {\r\n this.state.updateParticipantMedia(peerId, { micEnabled: enabled });\r\n this.events.onMicToggled?.(peerId, enabled);\r\n } else if (kind === \"video\") {\r\n this.state.updateParticipantMedia(peerId, { camEnabled: enabled });\r\n this.events.onCamToggled?.(peerId, enabled);\r\n }\r\n\r\n break;\r\n }\r\n\r\n case \"CHAT_MESSAGE\": {\r\n const newMsg = msg.data;\r\n\r\n if (newMsg.sender_id === this.myId) break; // already added optimistically\r\n this.state.addChatMessage({\r\n id: newMsg.id,\r\n text: newMsg.message,\r\n sender_id: newMsg.sender_id,\r\n sender_name: newMsg.sender_name,\r\n timestamp: new Date(newMsg.timestamp).getTime(),\r\n target: newMsg.target,\r\n });\r\n\r\n this.events.onChatMessage?.(msg);\r\n break;\r\n }\r\n case \"SCREEN_SHARE_START\": {\r\n const peerId = msg.peerId;\r\n\r\n this.state.updateParticipantMedia(peerId, {\r\n isScreenSharing: true,\r\n remoteScreenStreamId: msg.stream_id,\r\n });\r\n\r\n if (!this.state.presenterId) {\r\n this.state.setPresenterId(peerId);\r\n }\r\n\r\n // Fix: Use screenStream instead of the regular camera stream\r\n const screenStream =\r\n this.state.getParticipant(peerId)?.media?.screenStream;\r\n\r\n this.events.onScreenShareStarted?.(peerId, screenStream || null!);\r\n break;\r\n }\r\n\r\n case \"SCREEN_SHARE_STOP\": {\r\n const peerId = msg.peerId;\r\n this.state.updateParticipantMedia(peerId, { isScreenSharing: false });\r\n if (this.state.presenterId === peerId) {\r\n this.state.setPresenterId(null);\r\n }\r\n this.events.onScreenShareStopped?.(peerId);\r\n break;\r\n }\r\n case \"ERROR\": {\r\n const fatal = msg?.fatal === true;\r\n\r\n this.emitError(\r\n \"WS_ERROR\",\r\n msg?.message || \"Unknown error\",\r\n msg,\r\n !fatal,\r\n );\r\n\r\n if (fatal) {\r\n this.disconnect();\r\n }\r\n\r\n return;\r\n }\r\n }\r\n }\r\n\r\n // ---------------- PEER ----------------\r\n private createPeer(id: string) {\r\n if (!this.localStream) throw new Error(\"No local stream\");\r\n\r\n const pc = new RTCPeerConnection({\r\n iceServers: [\r\n {\r\n urls: [\r\n \"stun:stun.l.google.com:19302\",\r\n \"stun:stun1.l.google.com:19302\",\r\n ],\r\n },\r\n ],\r\n });\r\n\r\n pc.ontrack = (event) => {\r\n const incomingStream = event.streams[0];\r\n const participant = this.state.getParticipant(id);\r\n\r\n const isScreenStream =\r\n incomingStream.id === participant?.media?.remoteScreenStreamId;\r\n\r\n if (isScreenStream) {\r\n const videoTrack =\r\n event.track.kind === \"video\"\r\n ? event.track\r\n : incomingStream.getVideoTracks()[0] ||\r\n participant?.media?.screenTrack;\r\n\r\n this.state.updateParticipantMedia(id, {\r\n screenStream: incomingStream,\r\n screenTrack: videoTrack,\r\n\r\n isScreenSharing: true,\r\n });\r\n\r\n // CRITICAL FIX: Late joiners never get SCREEN_SHARE_START.\r\n // We must set the presenter ID here if it isn't set, otherwise the grid won't render the stage.\r\n if (!this.state.presenterId) {\r\n this.state.setPresenterId(id);\r\n }\r\n\r\n this.events.onScreenShareStarted?.(id, incomingStream);\r\n } else {\r\n this.state.updateParticipantMedia(id, {\r\n stream: incomingStream,\r\n cameraTrack: incomingStream.getVideoTracks()[0],\r\n });\r\n this.events.onTrack?.(incomingStream, id);\r\n }\r\n };\r\n\r\n pc.onicecandidate = (e) => {\r\n if (!e.candidate) return;\r\n this.send({\r\n type: \"ICE\",\r\n payload: JSON.stringify(e.candidate),\r\n sender: this.myId,\r\n target: id,\r\n });\r\n };\r\n\r\n pc.onconnectionstatechange = () => {\r\n if (pc.connectionState === \"failed\") {\r\n try {\r\n pc.restartIce();\r\n } catch {}\r\n }\r\n };\r\n\r\n this.localStream.getTracks().forEach((track) => {\r\n pc.addTrack(track, this.localStream!);\r\n });\r\n\r\n if (this.isScreenSharing && this.screenStream) {\r\n this.screenSenders[id] = [];\r\n this.screenStream.getTracks().forEach((track) => {\r\n const sender = pc.addTrack(track, this.screenStream!);\r\n this.screenSenders[id].push(sender);\r\n });\r\n }\r\n\r\n return pc;\r\n }\r\n\r\n // ---------------- OFFER ----------------\r\n private async createOffer(id: string, isRenegotiation = false) {\r\n // Only apply the initial glare gate if this is not a track renegotiation\r\n if (!isRenegotiation && this.initiators.has(id)) return;\r\n\r\n if (!isRenegotiation) {\r\n this.initiators.add(id);\r\n }\r\n\r\n if (!this.peers[id]) {\r\n this.peers[id] = this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n if (pc.signalingState !== \"stable\") {\r\n return;\r\n }\r\n\r\n const offer = await pc.createOffer();\r\n await pc.setLocalDescription(offer);\r\n\r\n this.send({\r\n type: \"OFFER\",\r\n payload: offer.sdp,\r\n sender: this.myId,\r\n target: id,\r\n });\r\n }\r\n\r\n // ---------------- ANSWER ----------------\r\n private async handleOffer(sdp: string, id: string) {\r\n if (!this.peers[id]) {\r\n this.peers[id] = this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n await pc.setRemoteDescription({\r\n type: \"offer\",\r\n sdp,\r\n });\r\n\r\n const pending = this.pendingIceCandidates[id] || [];\r\n\r\n for (const candidate of pending) {\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (err) {\r\n console.warn(err);\r\n }\r\n }\r\n\r\n delete this.pendingIceCandidates[id];\r\n\r\n const answer = await pc.createAnswer();\r\n\r\n await pc.setLocalDescription(answer);\r\n await this.flushIce(id, pc);\r\n\r\n this.send({\r\n type: \"ANSWER\",\r\n payload: answer.sdp,\r\n sender: this.myId,\r\n target: id,\r\n });\r\n }\r\n\r\n // ---------------- CLEANUP ----------------\r\n private closePeer(id: string) {\r\n const pc = this.peers[id];\r\n\r\n if (!pc) return;\r\n\r\n pc.ontrack = null;\r\n pc.onicecandidate = null;\r\n pc.onconnectionstatechange = null;\r\n\r\n pc.close();\r\n\r\n delete this.peers[id];\r\n\r\n this.initiators.delete(id);\r\n\r\n this.state.removeParticipant(id);\r\n }\r\n\r\n async startScreenShare() {\r\n if (this.state.presenterId && this.state.presenterId !== this.myId) {\r\n throw new Error(\"Another user is already sharing their screen.\");\r\n }\r\n\r\n this.screenStream = await navigator.mediaDevices.getDisplayMedia({\r\n video: true,\r\n audio: true,\r\n });\r\n\r\n this.isScreenSharing = true;\r\n\r\n this.state.updateLocalParticipant({\r\n media: {\r\n isScreenSharing: true,\r\n screenStream: this.screenStream,\r\n },\r\n });\r\n\r\n this.state.setPresenterId(this.myId);\r\n // Handle the user clicking browser's built-in \"Stop Sharing\" button\r\n this.screenStream.getVideoTracks()[0].onended = () => {\r\n this.stopScreenShare();\r\n };\r\n\r\n Object.entries(this.peers).forEach(([peerId, pc]) => {\r\n this.screenSenders[peerId] = [];\r\n this.screenStream!.getTracks().forEach((track) => {\r\n const sender = pc.addTrack(track, this.screenStream!);\r\n this.screenSenders[peerId].push(sender);\r\n });\r\n\r\n // Renegotiate peer connection descriptors to notify remote side of new track footprint\r\n this.createOffer(peerId, true);\r\n });\r\n\r\n this.send({\r\n type: \"SCREEN_SHARE_START\",\r\n sender: this.myId,\r\n room_id: this.roomId,\r\n stream_id: this.screenStream.id.replace(/[{}]/g, \"\"),\r\n });\r\n\r\n return this.screenStream;\r\n }\r\n\r\n stopScreenShare() {\r\n if (!this.screenStream) return;\r\n\r\n this.screenStream.getTracks().forEach((t) => t.stop());\r\n\r\n // Remove tracks cleanly from WebRTC channel pathways across your peers\r\n Object.entries(this.peers).forEach(([peerId, pc]) => {\r\n const senders = this.screenSenders[peerId] || [];\r\n senders.forEach((sender) => {\r\n try {\r\n pc.removeTrack(sender);\r\n } catch (err) {\r\n console.warn(err);\r\n }\r\n });\r\n delete this.screenSenders[peerId];\r\n\r\n // Renegotiate layout expectations to scale down stream bounds\r\n this.createOffer(peerId, true);\r\n });\r\n\r\n this.screenStream = null;\r\n this.isScreenSharing = false;\r\n\r\n this.state.updateLocalParticipant({\r\n media: {\r\n isScreenSharing: false,\r\n screenStream: null,\r\n screenTrack: undefined,\r\n },\r\n });\r\n\r\n if (this.state.presenterId === this.myId) {\r\n this.state.setPresenterId(null);\r\n }\r\n\r\n this.send({\r\n type: \"SCREEN_SHARE_STOP\",\r\n sender: this.myId,\r\n room_id: this.roomId,\r\n });\r\n }\r\n\r\n sendChatMessage(payload: ChatInput) {\r\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\r\n console.warn(\"WS not connected\");\r\n return;\r\n }\r\n\r\n if (!this.roomId) {\r\n console.warn(\"No roomId set\");\r\n return;\r\n }\r\n\r\n const isPrivate = !!payload?.target;\r\n\r\n const senderName = this.state.localParticipant?.name || \"Anonymous\";\r\n\r\n const msg: ChatMessage = {\r\n id: crypto.randomUUID(),\r\n sender_id: this.myId,\r\n sender_name: senderName,\r\n text: payload.message.trim(),\r\n timestamp: Date.now(),\r\n reply_to: payload.reply_to ?? null,\r\n target: payload.target ?? null,\r\n };\r\n\r\n // optimistic UI update\r\n this.state.addChatMessage(msg);\r\n\r\n // send protocol payload (clean + consistent)\r\n this.send({\r\n type: \"CHAT_MESSAGE\",\r\n message: payload.message.trim(),\r\n user_id: this.myId,\r\n sender_name: senderName,\r\n room_id: this.roomId,\r\n target: isPrivate ? (payload.target ?? null) : null,\r\n reply_to: payload.reply_to ?? null,\r\n\r\n client_ts: Date.now(),\r\n });\r\n }\r\n\r\n disconnect() {\r\n this.stopScreenShare();\r\n\r\n Object.values(this.peers).forEach((pc) => pc.close());\r\n this.peers = {};\r\n this.initiators.clear();\r\n\r\n this.stopHeartbeat();\r\n\r\n if (this.ws) {\r\n this.ws.close();\r\n this.ws = null;\r\n }\r\n\r\n if (this.localStream) {\r\n this.localStream.getTracks().forEach((track) => track.stop());\r\n this.localStream = null;\r\n }\r\n\r\n this.roomId = null;\r\n\r\n // Clear and notify\r\n this.state.localParticipant = null;\r\n this.state.notify(\"localParticipant\");\r\n\r\n this.state.participants.clear();\r\n this.state.notify(\"participants\");\r\n\r\n this.state.clearChat();\r\n this.state.setPresenterId(null);\r\n }\r\n\r\n private async flushIce(id: string, pc: RTCPeerConnection) {\r\n const pending = this.pendingIceCandidates[id];\r\n if (!pending?.length) return;\r\n\r\n for (const candidate of pending) {\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (e) {\r\n console.warn(\"ICE flush error\", e);\r\n }\r\n }\r\n\r\n delete this.pendingIceCandidates[id];\r\n }\r\n\r\n private send(msg: any) {\r\n this.ws?.send(JSON.stringify(msg));\r\n }\r\n}\r\n","import { useEffect, useState } from \"react\";\r\nimport { StateScope } from \"../types/meeting\";\r\nimport { MeetingState } from \"../core/MeetingState\";\r\n\r\nexport function useMeetingStore<T>(\r\n stateManager: MeetingState,\r\n scope: StateScope,\r\n selector: (state: MeetingState) => T,\r\n): T {\r\n const [state, setState] = useState(() => selector(stateManager));\r\n\r\n useEffect(() => {\r\n // Update local react state whenever the SDK notifies this scope\r\n const unsubscribe = stateManager.subscribe(scope, () => {\r\n setState(selector(stateManager));\r\n });\r\n\r\n return unsubscribe;\r\n }, [stateManager, scope, selector]);\r\n\r\n return state;\r\n}\r\n","import { useEffect } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\n\r\nexport const useMeeting = (handlers?: { onError?: (err: any) => void }) => {\r\n const ctx = useMeetingContext();\r\n\r\n useEffect(() => {\r\n if (!handlers?.onError) return;\r\n\r\n const unsubscribe = ctx.onError(handlers.onError);\r\n return unsubscribe;\r\n }, [handlers?.onError]);\r\n\r\n return ctx;\r\n};\r\n","import { useEffect, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useParticipants = () => {\r\n const { sdk } = useMeetingContext();\r\n\r\n const [participants, setParticipants] = useState<Participant[]>(() =>\r\n sdk.state.getParticipants(),\r\n );\r\n\r\n useEffect(() => {\r\n const update = () => {\r\n setParticipants(sdk.state.getParticipants());\r\n };\r\n\r\n update();\r\n\r\n const unsub = sdk.state.subscribe(\"participants\", update);\r\n\r\n return unsub;\r\n }, [sdk]);\r\n\r\n return participants;\r\n};\r\n","import { useEffect, useRef, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useRemoteMedia = (participantId: string) => {\r\n const { sdk } = useMeetingContext();\r\n\r\n // Create refs to access the DOM elements directly\r\n const videoRef = useRef<HTMLVideoElement>(null);\r\n const audioRef = useRef<HTMLAudioElement>(null);\r\n\r\n const [participant, setParticipant] = useState<Participant | null>(\r\n () => sdk.state.getParticipant(participantId) || null,\r\n );\r\n\r\n // Sync state from SDK to React\r\n useEffect(() => {\r\n const unsub = sdk.state.subscribe(`participant:${participantId}`, () => {\r\n const updated = sdk.state.getParticipant(participantId);\r\n if (updated) {\r\n setParticipant({ ...updated });\r\n }\r\n });\r\n return unsub;\r\n }, [participantId, sdk]);\r\n\r\n // Handle stream attachment whenever the stream changes\r\n useEffect(() => {\r\n const stream = participant?.media?.stream;\r\n\r\n if (stream) {\r\n if (videoRef.current && videoRef.current.srcObject !== stream) {\r\n videoRef.current.srcObject = stream;\r\n }\r\n if (audioRef.current && audioRef.current.srcObject !== stream) {\r\n audioRef.current.srcObject = stream;\r\n }\r\n }\r\n }, [participant?.media?.stream]);\r\n\r\n return {\r\n videoRef,\r\n audioRef,\r\n isCamActive: !!participant?.media?.camEnabled,\r\n isMicEnabled: !!participant?.media?.micEnabled,\r\n };\r\n};\r\n"],"mappings":"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","names":["import_react","import_react","peerId","import_react","import_react","import_react"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/react/useLocalParticipant.tsx","../src/react/MeetingProvider.tsx","../src/config/ws.ts","../src/core/MeetingState.ts","../src/core/VideoCore.ts","../src/react/useMeetingStore.ts","../src/react/useMeeting.ts","../src/react/useParticipants.ts","../src/react/useRemoteMedia.ts"],"sourcesContent":["export { useLocalParticipant } from \"./react/useLocalParticipant\";\r\n\r\n// Core exports\r\nexport { VideoSDKCore } from \"./core/VideoCore\";\r\nexport { MeetingState } from \"./core/MeetingState\";\r\nexport type { Participant, ChatInput } from \"./types/meeting\";\r\n\r\n// React hooks and components\r\nexport { MeetingProvider, useMeetingContext } from \"./react/MeetingProvider\";\r\nexport { useMeeting } from \"./react/useMeeting\";\r\nexport { useParticipants } from \"./react/useParticipants\";\r\nexport { useRemoteMedia } from \"./react/useRemoteMedia\";\r\n","import { useCallback, useEffect, useRef, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useLocalParticipant = () => {\r\n const { sdk } = useMeetingContext();\r\n\r\n // Guard the initial state to ensure it matches Participant | null\r\n const [localParticipant, setLocalParticipant] = useState<Participant | null>(\r\n () => {\r\n const current = sdk.state.localParticipant;\r\n return current && current.id ? (current as Participant) : null;\r\n },\r\n );\r\n\r\n useEffect(() => {\r\n const unsubscribe = sdk.state.subscribe(\"localParticipant\", () => {\r\n const current = sdk.state.localParticipant;\r\n\r\n // Safe Type-Guard: Only update if the object has a finalized id\r\n if (current && current.id) {\r\n setLocalParticipant({ ...current } as Participant);\r\n } else {\r\n setLocalParticipant(null);\r\n }\r\n });\r\n\r\n return unsubscribe;\r\n }, [sdk]);\r\n\r\n const lastStreamRef = useRef<MediaStream | null>(null);\r\n\r\n const videoRef = useCallback(\r\n (video: HTMLVideoElement | null) => {\r\n if (!video) return;\r\n\r\n const stream = localParticipant?.media?.stream;\r\n if (!stream) return;\r\n\r\n if (lastStreamRef.current === stream) return;\r\n lastStreamRef.current = stream;\r\n\r\n video.srcObject = stream;\r\n video.autoplay = true;\r\n video.playsInline = true;\r\n\r\n video.play().catch((err) => {\r\n console.warn(`Autoplay failed for local view:`, err);\r\n });\r\n },\r\n [localParticipant?.media?.stream],\r\n );\r\n\r\n return {\r\n participant: localParticipant,\r\n videoRef,\r\n };\r\n};\r\n","import React, { createContext, useContext, useMemo, useRef } from \"react\";\r\nimport { VideoSDKCore } from \"../core/VideoCore\";\r\nimport {\r\n ChatInput,\r\n ChatMessage,\r\n MeetingConfig,\r\n Participant,\r\n PubSubTopic,\r\n} from \"../types/meeting\";\r\nimport { useMeetingStore } from \"./useMeetingStore\";\r\n\r\ntype PubSubHandle = {\r\n messages: ChatMessage[];\r\n publish: (input: ChatInput) => void;\r\n};\r\n\r\ntype MeetingContextValue = {\r\n sdk: VideoSDKCore;\r\n\r\n join: (config: MeetingConfig) => Promise<void>;\r\n leave: () => void;\r\n\r\n toggleMic: () => void;\r\n toggleCam: () => void;\r\n\r\n startScreenShare: () => Promise<MediaStream>;\r\n stopScreenShare: () => void;\r\n\r\n sendMessage: (input: ChatInput) => void;\r\n\r\n meetingId: string | null;\r\n localParticipant: Participant | null;\r\n participants: Map<string, Participant>;\r\n messages: ChatMessage[];\r\n presenterId: string | null;\r\n usePubSub: (topic: PubSubTopic) => PubSubHandle;\r\n onError: (cb: (err: any) => void) => () => void;\r\n};\r\n\r\nconst MeetingContext = createContext<MeetingContextValue | null>(null);\r\n\r\nexport const MeetingProvider = ({\r\n config,\r\n children,\r\n}: {\r\n config: MeetingConfig;\r\n children: React.ReactNode;\r\n}) => {\r\n const sdkRef = useRef<VideoSDKCore | null>(null);\r\n const errorListeners = useRef(new Set<(err: any) => void>());\r\n if (!sdkRef.current) {\r\n sdkRef.current = new VideoSDKCore({\r\n onError: (err) => {\r\n errorListeners.current.forEach((fn) => fn(err));\r\n },\r\n });\r\n }\r\n\r\n const sdk = sdkRef.current;\r\n const presenterId = useMeetingStore(\r\n sdk.state,\r\n \"presenter\",\r\n (s) => s.presenterId,\r\n );\r\n const participants = useMeetingStore(\r\n sdk.state,\r\n \"participants\",\r\n (s) => s.participants,\r\n );\r\n const localParticipant = useMeetingStore(\r\n sdk.state,\r\n \"localParticipant\",\r\n (s) => s.localParticipant,\r\n );\r\n const messages = useMeetingStore(sdk.state, \"chat\", (s) =>\r\n s.getChatMessages(),\r\n );\r\n\r\n const value = useMemo<MeetingContextValue>(() => {\r\n if (!sdkRef.current) {\r\n sdkRef.current = new VideoSDKCore({\r\n onError: (err) => {\r\n errorListeners.current.forEach((fn) => fn(err));\r\n },\r\n });\r\n }\r\n\r\n return {\r\n sdk,\r\n\r\n join: (joinConfig: MeetingConfig) =>\r\n sdk.joinMeeting({\r\n ...config,\r\n ...joinConfig,\r\n }),\r\n leave: () => sdk.disconnect(),\r\n toggleMic: sdk.toggleMic.bind(sdk),\r\n toggleCam: sdk.toggleCam.bind(sdk),\r\n startScreenShare: sdk.startScreenShare.bind(sdk),\r\n stopScreenShare: sdk.stopScreenShare.bind(sdk),\r\n sendMessage: sdk.sendChatMessage.bind(sdk),\r\n\r\n meetingId: sdk.getMeetingId(),\r\n localParticipant,\r\n participants,\r\n messages,\r\n presenterId,\r\n usePubSub: (topic: PubSubTopic) => {\r\n if (topic !== \"SECURE_CHAT\") {\r\n throw new Error(`Unsupported PubSub argument: \"${topic}\"`);\r\n }\r\n return {\r\n messages: sdk.state.getChatMessages(),\r\n publish: sdk.sendChatMessage.bind(sdk),\r\n };\r\n },\r\n onError: (cb: (err: any) => void) => {\r\n errorListeners.current.add(cb);\r\n\r\n return () => {\r\n errorListeners.current.delete(cb);\r\n };\r\n },\r\n };\r\n }, [config, sdk, localParticipant, participants, messages, presenterId]);\r\n\r\n return (\r\n <MeetingContext.Provider value={value}>{children}</MeetingContext.Provider>\r\n );\r\n};\r\n\r\nexport const useMeetingContext = () => {\r\n const ctx = useContext(MeetingContext);\r\n if (!ctx)\r\n throw new Error(\"useMeetingContext must be used inside <MeetingProvider>\");\r\n return ctx;\r\n};\r\n","export const SDK_CONFIG = {\r\n wsUrl: \"wss://rust-video-server-sfyf.onrender.com/ws\",\r\n};\r\n","import {\r\n ChatMessage,\r\n Listener,\r\n Participant,\r\n ParticipantMedia,\r\n StateScope,\r\n} from \"../types/meeting\";\r\n\r\ntype LocalParticipantPatch = {\r\n id?: string;\r\n name?: string;\r\n media?: Partial<ParticipantMedia>;\r\n};\r\n\r\nexport class MeetingState {\r\n participants = new Map<string, Participant>();\r\n localParticipant: Participant | null = null;\r\n localStream: MediaStream | null = null;\r\n chatMessages = new Map<string, ChatMessage>();\r\n presenterId: string | null = null;\r\n\r\n private listeners = new Map<StateScope, Set<Listener>>();\r\n\r\n // ---- reactive system ----\r\n\r\n subscribe(scope: StateScope, fn: Listener): () => void {\r\n if (!this.listeners.has(scope)) {\r\n this.listeners.set(scope, new Set());\r\n }\r\n this.listeners.get(scope)!.add(fn);\r\n\r\n return () => {\r\n this.listeners.get(scope)?.delete(fn);\r\n };\r\n }\r\n\r\n notify(scope: StateScope) {\r\n this.listeners.get(scope)?.forEach((fn) => fn());\r\n }\r\n\r\n setPresenterId(id: string | null) {\r\n if (this.presenterId === id) return;\r\n this.presenterId = id;\r\n this.notify(\"presenter\");\r\n this.notify(\"participants\");\r\n }\r\n\r\n // ---- participants ----\r\n\r\n addParticipant(p: Participant) {\r\n if (this.participants.has(p.id)) return false;\r\n // Fix: Immutable Map update\r\n const next = new Map(this.participants);\r\n next.set(p.id, p);\r\n this.participants = next;\r\n\r\n this.notify(\"participants\");\r\n return true;\r\n }\r\n\r\n removeParticipant(id: string) {\r\n // Fix: Immutable Map update\r\n const next = new Map(this.participants);\r\n next.delete(id);\r\n this.participants = next;\r\n\r\n this.notify(\"participants\");\r\n }\r\n\r\n updateParticipantMedia(\r\n id: string,\r\n patch: Partial<NonNullable<Participant[\"media\"]>>,\r\n ) {\r\n const p = this.participants.get(id);\r\n if (!p) return;\r\n\r\n const updated: Participant = {\r\n ...p,\r\n media: {\r\n stream: null,\r\n screenStream: undefined,\r\n cameraTrack: undefined,\r\n screenTrack: undefined,\r\n audioTrack: undefined,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n ...p.media, // preserve existing media items if they happen to exist\r\n ...patch, // apply the incoming stream updates\r\n },\r\n };\r\n\r\n // Keep your clean immutable map update\r\n const next = new Map(this.participants);\r\n next.set(id, updated);\r\n this.participants = next;\r\n\r\n this.notify(`participant:${id}`);\r\n this.notify(\"participants\");\r\n }\r\n\r\n updateLocalParticipant(patch: LocalParticipantPatch) {\r\n const prev = this.localParticipant;\r\n\r\n if (!prev) {\r\n this.localParticipant = {\r\n id: patch.id ?? \"\",\r\n name: patch.name ?? \"\",\r\n media: {\r\n stream: patch.media?.stream ?? null, // ◄ FIX: Capture the stream from the patch here\r\n screenStream: patch.media?.screenStream,\r\n cameraTrack: patch.media?.cameraTrack,\r\n screenTrack: patch.media?.screenTrack,\r\n audioTrack: patch.media?.audioTrack,\r\n micEnabled: patch.media?.micEnabled ?? true,\r\n camEnabled: patch.media?.camEnabled ?? true,\r\n isScreenSharing: patch.media?.isScreenSharing ?? false,\r\n },\r\n };\r\n\r\n this.notify(\"localParticipant\");\r\n return;\r\n }\r\n\r\n const prevMedia = prev.media ?? {\r\n stream: null,\r\n screenStream: undefined,\r\n cameraTrack: undefined,\r\n screenTrack: undefined,\r\n audioTrack: undefined,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n };\r\n\r\n const nextMedia: ParticipantMedia = {\r\n stream: patch.media?.stream ?? prevMedia.stream,\r\n screenStream: patch.media?.screenStream ?? prevMedia.screenStream,\r\n cameraTrack: patch.media?.cameraTrack ?? prevMedia.cameraTrack,\r\n screenTrack: patch.media?.screenTrack ?? prevMedia.screenTrack,\r\n audioTrack: patch.media?.audioTrack ?? prevMedia.audioTrack,\r\n micEnabled: patch.media?.micEnabled ?? prevMedia.micEnabled,\r\n camEnabled: patch.media?.camEnabled ?? prevMedia.camEnabled,\r\n isScreenSharing:\r\n patch.media?.isScreenSharing ?? prevMedia.isScreenSharing,\r\n };\r\n\r\n this.localParticipant = {\r\n ...prev,\r\n id: patch.id ?? prev.id,\r\n name: patch.name ?? prev.name,\r\n media: nextMedia,\r\n };\r\n\r\n this.notify(\"localParticipant\");\r\n }\r\n\r\n // ---- chat ----\r\n\r\n addChatMessage(msg: ChatMessage) {\r\n this.chatMessages.set(msg.id, msg);\r\n this.notify(\"chat\");\r\n }\r\n\r\n getChatMessages() {\r\n return Array.from(this.chatMessages.values()).sort(\r\n (a, b) => a.timestamp - b.timestamp,\r\n );\r\n }\r\n\r\n clearChat() {\r\n this.chatMessages.clear();\r\n this.notify(\"chat\");\r\n }\r\n\r\n // ---- helpers ----\r\n\r\n getParticipants() {\r\n return Array.from(this.participants.values());\r\n }\r\n\r\n getParticipant(id: string) {\r\n return this.participants.get(id) ?? null;\r\n }\r\n\r\n resetRemoteState() {\r\n this.participants.clear();\r\n this.chatMessages.clear();\r\n this.presenterId = null;\r\n this.notify(\"participants\");\r\n this.notify(\"chat\");\r\n this.notify(\"presenter\");\r\n }\r\n}\r\n","import { SDK_CONFIG } from \"../config/ws\";\r\nimport {\r\n ChatInput,\r\n ChatMessage,\r\n Events,\r\n MeetingConfig,\r\n SDKError,\r\n} from \"../types/meeting\";\r\nimport { MeetingState } from \"./MeetingState\";\r\n\r\nexport class VideoSDKCore {\r\n private ws: WebSocket | null = null;\r\n private peers: Record<string, RTCPeerConnection> = {};\r\n private initiators = new Set<string>();\r\n\r\n private myId: string;\r\n private roomId: string | null = null;\r\n private localStream: MediaStream | null = null;\r\n private screenStream: MediaStream | null = null;\r\n private isScreenSharing = false;\r\n private screenSenders: Record<string, RTCRtpSender[]> = {};\r\n\r\n private pingInterval: any = null;\r\n private pendingIceCandidates: Record<string, RTCIceCandidateInit[]> = {};\r\n private reconnectAttempts = 0;\r\n private reconnectTimer?: number;\r\n private participantName = \"\";\r\n public readonly state: MeetingState;\r\n private joinResolver?: () => void;\r\n private joinRejecter?: (e: any) => void;\r\n private emitError(\r\n code: string,\r\n message: string,\r\n raw?: any,\r\n recoverable = true,\r\n ) {\r\n const err: SDKError = {\r\n code,\r\n message,\r\n raw,\r\n roomId: this.roomId,\r\n userId: this.myId,\r\n recoverable,\r\n };\r\n\r\n this.events.onError?.(err);\r\n\r\n this.joinRejecter?.(err);\r\n this.joinRejecter = undefined;\r\n\r\n console.error(\"[MeetingSDK Error]\", err);\r\n }\r\n\r\n constructor(\r\n private events: Events = {},\r\n private url: string = SDK_CONFIG.wsUrl,\r\n ) {\r\n this.state = new MeetingState();\r\n this.events = events;\r\n this.url = url;\r\n\r\n this.myId = localStorage.getItem(\"vsdk_id\") || crypto.randomUUID();\r\n\r\n localStorage.setItem(\"vsdk_id\", this.myId);\r\n }\r\n\r\n // ---------------- STREAM ----------------\r\n async initLocal(video: HTMLVideoElement, name: string) {\r\n this.participantName = name;\r\n\r\n if (!this.localStream) {\r\n this.localStream = await navigator.mediaDevices.getUserMedia({\r\n video: true,\r\n audio: true,\r\n });\r\n }\r\n\r\n video.srcObject = this.localStream;\r\n\r\n // Fix: Supply mandatory fields to satisfy the Participant type constraint\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n stream: this.localStream,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n },\r\n });\r\n\r\n this.state.localStream = this.localStream;\r\n }\r\n\r\n // ---------------- CONNECT ----------------\r\n async connect(roomId: string, name: string) {\r\n this.roomId = roomId;\r\n\r\n this.reset();\r\n\r\n return new Promise<void>((resolve, reject) => {\r\n this.joinResolver = resolve;\r\n this.joinRejecter = reject;\r\n this.ws = new WebSocket(this.url);\r\n\r\n this.ws.onopen = () => {\r\n this.send({\r\n type: \"JOIN\",\r\n room_id: roomId,\r\n user_id: this.myId,\r\n sender_name: name,\r\n });\r\n };\r\n\r\n this.ws.onerror = (err) => {\r\n this.emitError(\"WS_ERROR\", \"WebSocket encountered an error\", err, true);\r\n };\r\n\r\n this.ws.onclose = (e) => {\r\n this.emitError(\r\n \"WS_CLOSED\",\r\n `Connection closed (${e.code}) ${e.reason || \"\"}`,\r\n e,\r\n true,\r\n );\r\n\r\n // If join never resolved, fail the promise\r\n this.joinRejecter?.({\r\n code: \"WS_CLOSED\",\r\n message: \"Connection closed before join completed\",\r\n raw: e,\r\n });\r\n\r\n this.joinRejecter = undefined;\r\n\r\n this.scheduleReconnect();\r\n };\r\n\r\n this.ws.onmessage = async (e) => {\r\n await this.handle(JSON.parse(e.data));\r\n };\r\n });\r\n }\r\n\r\n async joinMeeting(config: MeetingConfig) {\r\n const { roomId, name, audioMuted = false, videoMuted = false } = config;\r\n\r\n if (!roomId || !name) {\r\n throw new Error(\"roomId and name are required to join meeting\");\r\n }\r\n\r\n this.participantName = name;\r\n\r\n // Reuse existing stream if initLocal already configured it\r\n if (!this.localStream) {\r\n this.localStream = await navigator.mediaDevices.getUserMedia({\r\n video: true,\r\n audio: true,\r\n });\r\n }\r\n\r\n this.localStream.getAudioTracks().forEach((t) => {\r\n t.enabled = !audioMuted;\r\n });\r\n this.localStream.getVideoTracks().forEach((t) => {\r\n t.enabled = !videoMuted;\r\n });\r\n\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n stream: this.localStream,\r\n micEnabled: !audioMuted,\r\n camEnabled: !videoMuted,\r\n isScreenSharing: false,\r\n },\r\n });\r\n\r\n this.state.localStream = this.localStream;\r\n\r\n await this.connect(roomId, name);\r\n }\r\n\r\n /** Expose the roomId without making it fully public */\r\n getMeetingId(): string | null {\r\n return this.roomId;\r\n }\r\n\r\n toggleMic() {\r\n const mediaState = this.state.localParticipant?.media;\r\n if (!mediaState) return;\r\n\r\n // Flip the state tracked in localParticipant.media\r\n const nextEnabled = !mediaState.micEnabled;\r\n\r\n // Mirror the state change down to the actual hardware tracks\r\n this.localStream\r\n ?.getAudioTracks()\r\n .forEach((t) => (t.enabled = nextEnabled));\r\n\r\n // Update state layer\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n ...mediaState,\r\n micEnabled: nextEnabled,\r\n },\r\n });\r\n\r\n // Notify peers\r\n this.send({\r\n type: \"MEDIA_STATE\",\r\n kind: \"audio\",\r\n enabled: nextEnabled,\r\n });\r\n }\r\n\r\n toggleCam() {\r\n const mediaState = this.state.localParticipant?.media;\r\n if (!mediaState) return;\r\n\r\n // Flip the state tracked in localParticipant.media\r\n const nextEnabled = !mediaState.camEnabled;\r\n\r\n // Mirror the state change down to the actual hardware tracks\r\n this.localStream\r\n ?.getVideoTracks()\r\n .forEach((t) => (t.enabled = nextEnabled));\r\n\r\n // Update state layer\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n ...mediaState,\r\n camEnabled: nextEnabled,\r\n },\r\n });\r\n\r\n // Notify peers\r\n this.send({\r\n type: \"MEDIA_STATE\",\r\n kind: \"video\",\r\n enabled: nextEnabled,\r\n });\r\n }\r\n\r\n private scheduleReconnect() {\r\n if (!this.roomId) return;\r\n\r\n const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);\r\n\r\n clearTimeout(this.reconnectTimer);\r\n\r\n this.reconnectTimer = window.setTimeout(async () => {\r\n try {\r\n await this.connect(this.roomId!, this.participantName);\r\n\r\n this.reconnectAttempts = 0;\r\n } catch {\r\n this.reconnectAttempts++;\r\n this.scheduleReconnect();\r\n }\r\n }, delay);\r\n }\r\n\r\n private startHeartbeat() {\r\n this.stopHeartbeat();\r\n\r\n this.pingInterval = setInterval(() => {\r\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;\r\n\r\n this.send({\r\n type: \"PING\",\r\n client_ts: Date.now(),\r\n });\r\n }, 20000); // every 20s\r\n }\r\n private stopHeartbeat() {\r\n if (this.pingInterval) {\r\n clearInterval(this.pingInterval);\r\n this.pingInterval = null;\r\n }\r\n }\r\n\r\n // ---------------- RESET ----------------\r\n private reset() {\r\n Object.values(this.peers).forEach((pc) => pc.close());\r\n\r\n this.peers = {};\r\n this.initiators.clear();\r\n this.pendingIceCandidates = {};\r\n\r\n this.state.resetRemoteState();\r\n }\r\n\r\n // ---------------- HANDLE SIGNALS ----------------\r\n private async handle(msg: any) {\r\n if (msg.sender === this.myId) return;\r\n\r\n switch (msg.type) {\r\n case \"EXISTING_USERS\":\r\n if (msg.presenterId) {\r\n this.state.setPresenterId(msg.presenterId);\r\n\r\n // Trigger your event so the UI knows to render the stage\r\n this.events.onScreenShareStarted?.(msg.presenterId, null!);\r\n }\r\n\r\n for (const p of msg.participants || []) {\r\n if (!p?.id || p.id === this.myId) continue;\r\n this.state.addParticipant(p);\r\n this.events.onUserJoined?.(p);\r\n }\r\n break;\r\n\r\n case \"JOINED\": {\r\n this.startHeartbeat();\r\n this.joinResolver?.();\r\n this.joinResolver = undefined;\r\n this.joinRejecter = undefined;\r\n break;\r\n }\r\n case \"USER_JOINED\": {\r\n const p = msg.participant;\r\n\r\n if (!p?.id || p.id === this.myId) return;\r\n\r\n this.state.addParticipant(p);\r\n\r\n this.events.onUserJoined?.(p);\r\n // await this.createOffer(p.id);\r\n\r\n break;\r\n }\r\n\r\n case \"OFFER\":\r\n await this.handleOffer(msg.payload, msg.sender);\r\n break;\r\n\r\n case \"ANSWER\": {\r\n const pc = this.peers[msg.sender];\r\n\r\n if (!pc) return;\r\n\r\n if (pc.signalingState === \"stable\") {\r\n console.warn(\"Late answer received, restarting negotiation\");\r\n return;\r\n }\r\n\r\n await pc.setRemoteDescription({\r\n type: \"answer\",\r\n sdp: msg.payload,\r\n });\r\n\r\n await this.flushIce(msg.sender, pc);\r\n\r\n break;\r\n }\r\n\r\n case \"ICE\": {\r\n const candidate = JSON.parse(msg.payload);\r\n\r\n let pc = this.peers[msg.sender];\r\n\r\n if (!pc) {\r\n this.pendingIceCandidates[msg.sender] ??= [];\r\n this.pendingIceCandidates[msg.sender].push(candidate);\r\n break;\r\n }\r\n\r\n if (!pc.remoteDescription) {\r\n this.pendingIceCandidates[msg.sender] ??= [];\r\n this.pendingIceCandidates[msg.sender].push(candidate);\r\n break;\r\n }\r\n\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (err) {\r\n console.warn(\"ICE error:\", err);\r\n }\r\n\r\n break;\r\n }\r\n\r\n case \"USER_LEFT\":\r\n const peerId = msg.participant.id;\r\n this.closePeer(peerId);\r\n\r\n this.state.removeParticipant(peerId);\r\n\r\n this.events.onUserLeft?.(peerId);\r\n\r\n break;\r\n\r\n case \"MEDIA_STATE_CHANGE\": {\r\n const peerId = msg.peerId;\r\n const { kind, enabled } = msg;\r\n\r\n // 1. Sync the app state layer for UI rendering components\r\n if (kind === \"audio\") {\r\n this.state.updateParticipantMedia(peerId, { micEnabled: enabled });\r\n this.events.onMicToggled?.(peerId, enabled);\r\n } else if (kind === \"video\") {\r\n this.state.updateParticipantMedia(peerId, { camEnabled: enabled });\r\n this.events.onCamToggled?.(peerId, enabled);\r\n }\r\n\r\n break;\r\n }\r\n\r\n case \"CHAT_MESSAGE\": {\r\n const newMsg = msg.data;\r\n\r\n if (newMsg.sender_id === this.myId) break; // already added optimistically\r\n this.state.addChatMessage({\r\n id: newMsg.id,\r\n text: newMsg.message,\r\n sender_id: newMsg.sender_id,\r\n sender_name: newMsg.sender_name,\r\n timestamp: new Date(newMsg.timestamp).getTime(),\r\n target: newMsg.target,\r\n });\r\n\r\n this.events.onChatMessage?.(msg);\r\n break;\r\n }\r\n case \"SCREEN_SHARE_START\": {\r\n const peerId = msg.peerId;\r\n\r\n this.state.updateParticipantMedia(peerId, {\r\n isScreenSharing: true,\r\n remoteScreenStreamId: msg.stream_id,\r\n });\r\n\r\n if (!this.state.presenterId) {\r\n this.state.setPresenterId(peerId);\r\n }\r\n\r\n // Fix: Use screenStream instead of the regular camera stream\r\n const screenStream =\r\n this.state.getParticipant(peerId)?.media?.screenStream;\r\n\r\n this.events.onScreenShareStarted?.(peerId, screenStream || null!);\r\n break;\r\n }\r\n\r\n case \"SCREEN_SHARE_STOP\": {\r\n const peerId = msg.peerId;\r\n this.state.updateParticipantMedia(peerId, { isScreenSharing: false });\r\n if (this.state.presenterId === peerId) {\r\n this.state.setPresenterId(null);\r\n }\r\n this.events.onScreenShareStopped?.(peerId);\r\n break;\r\n }\r\n case \"ERROR\": {\r\n const fatal = msg?.fatal === true;\r\n\r\n this.emitError(\r\n \"WS_ERROR\",\r\n msg?.message || \"Unknown error\",\r\n msg,\r\n !fatal,\r\n );\r\n\r\n if (fatal) {\r\n this.disconnect();\r\n }\r\n\r\n return;\r\n }\r\n }\r\n }\r\n\r\n // ---------------- PEER ----------------\r\n private createPeer(id: string) {\r\n if (!this.localStream) throw new Error(\"No local stream\");\r\n console.log(\r\n \"Adding tracks\",\r\n this.localStream.getTracks().map((t) => ({\r\n kind: t.kind,\r\n enabled: t.enabled,\r\n state: t.readyState,\r\n })),\r\n );\r\n\r\n const pc = new RTCPeerConnection({\r\n iceServers: [\r\n {\r\n urls: [\r\n \"stun:stun.l.google.com:19302\",\r\n \"stun:stun1.l.google.com:19302\",\r\n ],\r\n },\r\n ],\r\n });\r\n\r\n pc.ontrack = (event) => {\r\n const incomingStream = event.streams[0];\r\n const participant = this.state.getParticipant(id);\r\n\r\n const isScreenStream =\r\n incomingStream.id === participant?.media?.remoteScreenStreamId;\r\n\r\n if (isScreenStream) {\r\n const videoTrack =\r\n event.track.kind === \"video\"\r\n ? event.track\r\n : incomingStream.getVideoTracks()[0] ||\r\n participant?.media?.screenTrack;\r\n\r\n this.state.updateParticipantMedia(id, {\r\n screenStream: incomingStream,\r\n screenTrack: videoTrack,\r\n\r\n isScreenSharing: true,\r\n });\r\n\r\n if (!this.state.presenterId) {\r\n this.state.setPresenterId(id);\r\n }\r\n\r\n this.events.onScreenShareStarted?.(id, incomingStream);\r\n } else {\r\n this.state.updateParticipantMedia(id, {\r\n stream: incomingStream,\r\n cameraTrack: incomingStream.getVideoTracks()[0],\r\n audioTrack: incomingStream.getAudioTracks()[0],\r\n });\r\n this.events.onTrack?.(incomingStream, id);\r\n }\r\n };\r\n\r\n pc.onicecandidate = (e) => {\r\n if (!e.candidate) return;\r\n this.send({\r\n type: \"ICE\",\r\n payload: JSON.stringify(e.candidate),\r\n sender: this.myId,\r\n target: id,\r\n });\r\n };\r\n\r\n pc.onconnectionstatechange = () => {\r\n if (pc.connectionState === \"failed\") {\r\n try {\r\n pc.restartIce();\r\n } catch {}\r\n }\r\n };\r\n\r\n this.localStream.getTracks().forEach((track) => {\r\n pc.addTrack(track, this.localStream!);\r\n });\r\n\r\n if (this.isScreenSharing && this.screenStream) {\r\n this.screenSenders[id] = [];\r\n this.screenStream.getTracks().forEach((track) => {\r\n const sender = pc.addTrack(track, this.screenStream!);\r\n this.screenSenders[id].push(sender);\r\n });\r\n }\r\n\r\n return pc;\r\n }\r\n\r\n // ---------------- OFFER ----------------\r\n private async createOffer(id: string, isRenegotiation = false) {\r\n if (!this.peers[id]) {\r\n this.peers[id] = this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n const offer = await pc.createOffer();\r\n await pc.setLocalDescription(offer);\r\n\r\n this.send({\r\n type: \"OFFER\",\r\n payload: offer.sdp,\r\n sender: this.myId,\r\n target: id,\r\n });\r\n }\r\n\r\n // ---------------- ANSWER ----------------\r\n private async handleOffer(sdp: string, id: string) {\r\n if (!this.peers[id]) {\r\n this.peers[id] = this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n await pc.setRemoteDescription({\r\n type: \"offer\",\r\n sdp,\r\n });\r\n\r\n const pending = this.pendingIceCandidates[id] || [];\r\n\r\n for (const candidate of pending) {\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (err) {\r\n console.warn(err);\r\n }\r\n }\r\n\r\n delete this.pendingIceCandidates[id];\r\n\r\n const answer = await pc.createAnswer();\r\n\r\n await pc.setLocalDescription(answer);\r\n await this.flushIce(id, pc);\r\n\r\n this.send({\r\n type: \"ANSWER\",\r\n payload: answer.sdp,\r\n sender: this.myId,\r\n target: id,\r\n });\r\n }\r\n\r\n // ---------------- CLEANUP ----------------\r\n private closePeer(id: string) {\r\n const pc = this.peers[id];\r\n\r\n if (!pc) return;\r\n\r\n pc.ontrack = null;\r\n pc.onicecandidate = null;\r\n pc.onconnectionstatechange = null;\r\n\r\n pc.close();\r\n\r\n delete this.peers[id];\r\n\r\n this.initiators.delete(id);\r\n\r\n this.state.removeParticipant(id);\r\n }\r\n\r\n async startScreenShare() {\r\n try {\r\n if (this.state.presenterId && this.state.presenterId !== this.myId) {\r\n throw new Error(\"Another user is already sharing their screen.\");\r\n }\r\n if (!navigator.mediaDevices?.getDisplayMedia) {\r\n throw new Error(\"Screen sharing not supported on this device\");\r\n }\r\n\r\n this.screenStream = await navigator.mediaDevices.getDisplayMedia({\r\n video: true,\r\n // audio: true,\r\n });\r\n\r\n this.isScreenSharing = true;\r\n\r\n this.state.updateLocalParticipant({\r\n media: {\r\n isScreenSharing: true,\r\n screenStream: this.screenStream,\r\n screenTrack: this.screenStream.getVideoTracks()[0],\r\n },\r\n });\r\n\r\n this.state.setPresenterId(this.myId);\r\n // Handle the user clicking browser's built-in \"Stop Sharing\" button\r\n this.screenStream.getVideoTracks()[0].onended = () => {\r\n this.stopScreenShare();\r\n };\r\n\r\n Object.entries(this.peers).forEach(([peerId, pc]) => {\r\n this.screenSenders[peerId] = [];\r\n this.screenStream!.getTracks().forEach((track) => {\r\n const sender = pc.addTrack(track, this.screenStream!);\r\n this.screenSenders[peerId].push(sender);\r\n });\r\n\r\n // Renegotiate peer connection descriptors to notify remote side of new track footprint\r\n this.createOffer(peerId, true);\r\n });\r\n\r\n this.send({\r\n type: \"SCREEN_SHARE_START\",\r\n sender: this.myId,\r\n room_id: this.roomId,\r\n stream_id: this.screenStream.id.replace(/[{}]/g, \"\"),\r\n });\r\n\r\n return this.screenStream;\r\n } catch (err: any) {\r\n this.emitError(\r\n \"SCREEN_SHARE_FAILED\",\r\n err?.message || \"Failed to start screen sharing\",\r\n err,\r\n true,\r\n );\r\n\r\n this.isScreenSharing = false;\r\n this.screenStream = null;\r\n throw err;\r\n }\r\n }\r\n\r\n stopScreenShare() {\r\n if (!this.screenStream) return;\r\n\r\n this.screenStream.getTracks().forEach((t) => t.stop());\r\n\r\n // Remove tracks cleanly from WebRTC channel pathways across your peers\r\n Object.entries(this.peers).forEach(([peerId, pc]) => {\r\n const senders = this.screenSenders[peerId] || [];\r\n senders.forEach((sender) => {\r\n try {\r\n pc.removeTrack(sender);\r\n } catch (err) {\r\n console.warn(err);\r\n }\r\n });\r\n delete this.screenSenders[peerId];\r\n\r\n // Renegotiate layout expectations to scale down stream bounds\r\n this.createOffer(peerId, true);\r\n });\r\n\r\n this.screenStream = null;\r\n this.isScreenSharing = false;\r\n\r\n this.state.updateLocalParticipant({\r\n media: {\r\n isScreenSharing: false,\r\n screenStream: null,\r\n screenTrack: undefined,\r\n },\r\n });\r\n\r\n if (this.state.presenterId === this.myId) {\r\n this.state.setPresenterId(null);\r\n }\r\n\r\n this.send({\r\n type: \"SCREEN_SHARE_STOP\",\r\n sender: this.myId,\r\n room_id: this.roomId,\r\n });\r\n }\r\n\r\n sendChatMessage(payload: ChatInput) {\r\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\r\n console.warn(\"WS not connected\");\r\n return;\r\n }\r\n\r\n if (!this.roomId) {\r\n console.warn(\"No roomId set\");\r\n return;\r\n }\r\n\r\n const isPrivate = !!payload?.target;\r\n\r\n const senderName = this.state.localParticipant?.name || \"Anonymous\";\r\n\r\n const msg: ChatMessage = {\r\n id: crypto.randomUUID(),\r\n sender_id: this.myId,\r\n sender_name: senderName,\r\n text: payload.message.trim(),\r\n timestamp: Date.now(),\r\n reply_to: payload.reply_to ?? null,\r\n target: payload.target ?? null,\r\n };\r\n\r\n // optimistic UI update\r\n this.state.addChatMessage(msg);\r\n\r\n // send protocol payload (clean + consistent)\r\n this.send({\r\n type: \"CHAT_MESSAGE\",\r\n message: payload.message.trim(),\r\n user_id: this.myId,\r\n sender_name: senderName,\r\n room_id: this.roomId,\r\n target: isPrivate ? (payload.target ?? null) : null,\r\n reply_to: payload.reply_to ?? null,\r\n\r\n client_ts: Date.now(),\r\n });\r\n }\r\n\r\n disconnect() {\r\n this.stopScreenShare();\r\n\r\n Object.values(this.peers).forEach((pc) => pc.close());\r\n this.peers = {};\r\n this.initiators.clear();\r\n\r\n this.stopHeartbeat();\r\n\r\n if (this.ws) {\r\n this.ws.close();\r\n this.ws = null;\r\n }\r\n\r\n if (this.localStream) {\r\n this.localStream.getTracks().forEach((track) => track.stop());\r\n this.localStream = null;\r\n }\r\n\r\n this.roomId = null;\r\n\r\n // Clear and notify\r\n this.state.localParticipant = null;\r\n this.state.notify(\"localParticipant\");\r\n\r\n this.state.participants.clear();\r\n this.state.notify(\"participants\");\r\n\r\n this.state.clearChat();\r\n this.state.setPresenterId(null);\r\n }\r\n\r\n private async flushIce(id: string, pc: RTCPeerConnection) {\r\n const pending = this.pendingIceCandidates[id];\r\n if (!pending?.length) return;\r\n\r\n for (const candidate of pending) {\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (e) {\r\n console.warn(\"ICE flush error\", e);\r\n }\r\n }\r\n\r\n delete this.pendingIceCandidates[id];\r\n }\r\n\r\n private send(msg: any) {\r\n this.ws?.send(JSON.stringify(msg));\r\n }\r\n}\r\n","import { useEffect, useState } from \"react\";\r\nimport { StateScope } from \"../types/meeting\";\r\nimport { MeetingState } from \"../core/MeetingState\";\r\n\r\nexport function useMeetingStore<T>(\r\n stateManager: MeetingState,\r\n scope: StateScope,\r\n selector: (state: MeetingState) => T,\r\n): T {\r\n const [state, setState] = useState(() => selector(stateManager));\r\n\r\n useEffect(() => {\r\n // Update local react state whenever the SDK notifies this scope\r\n const unsubscribe = stateManager.subscribe(scope, () => {\r\n setState(selector(stateManager));\r\n });\r\n\r\n return unsubscribe;\r\n }, [stateManager, scope, selector]);\r\n\r\n return state;\r\n}\r\n","import { useEffect } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\n\r\nexport const useMeeting = (handlers?: { onError?: (err: any) => void }) => {\r\n const ctx = useMeetingContext();\r\n\r\n useEffect(() => {\r\n if (!handlers?.onError) return;\r\n\r\n const unsubscribe = ctx.onError(handlers.onError);\r\n return unsubscribe;\r\n }, [handlers?.onError]);\r\n\r\n return ctx;\r\n};\r\n","import { useEffect, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useParticipants = () => {\r\n const { sdk } = useMeetingContext();\r\n\r\n const [participants, setParticipants] = useState<Participant[]>(() =>\r\n sdk.state.getParticipants(),\r\n );\r\n\r\n useEffect(() => {\r\n const update = () => {\r\n setParticipants(sdk.state.getParticipants());\r\n };\r\n\r\n update();\r\n\r\n const unsub = sdk.state.subscribe(\"participants\", update);\r\n\r\n return unsub;\r\n }, [sdk]);\r\n\r\n return participants;\r\n};\r\n","import { useCallback, useEffect, useRef, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useRemoteMedia = (participantId: string) => {\r\n const { sdk } = useMeetingContext();\r\n const [participant, setParticipant] = useState<Participant | null>(\r\n () => sdk.state.getParticipant(participantId) || null,\r\n );\r\n\r\n const buildMediaStream = (participant: Participant | null) => {\r\n if (!participant?.media) return null;\r\n\r\n const stream = new MediaStream();\r\n\r\n const videoTrack = participant.media.stream?.getVideoTracks?.()?.[0];\r\n const audioTrack = participant.media.stream?.getAudioTracks?.()?.[0];\r\n\r\n if (videoTrack) stream.addTrack(videoTrack);\r\n if (audioTrack) stream.addTrack(audioTrack);\r\n\r\n return stream;\r\n };\r\n\r\n useEffect(() => {\r\n const unsub = sdk.state.subscribe(`participant:${participantId}`, () => {\r\n const p = sdk.state.getParticipant(participantId);\r\n setParticipant(p ? { ...p } : null);\r\n });\r\n return unsub;\r\n }, [participantId, sdk]);\r\n\r\n // Video Callback Ref\r\n const videoRef = useCallback(\r\n (node: HTMLVideoElement | null) => {\r\n if (!node) return;\r\n\r\n const stream = participant?.media?.stream;\r\n if (!stream) return;\r\n\r\n node.pause();\r\n\r\n // IMPORTANT: always force rebind (no conditions)\r\n node.srcObject = stream;\r\n\r\n node.muted = true;\r\n node.playsInline = true;\r\n node.autoplay = true;\r\n\r\n node.play().catch(() => {\r\n // ignore autoplay restrictions\r\n });\r\n },\r\n [participant?.media?.stream],\r\n );\r\n\r\n // Audio Callback Ref\r\n const audioRef = useCallback(\r\n (node: HTMLAudioElement | null) => {\r\n if (!node) return;\r\n\r\n const stream = participant?.media?.stream;\r\n if (!stream) return;\r\n\r\n node.pause();\r\n node.srcObject = stream;\r\n\r\n node.muted = false;\r\n\r\n node.play().catch(() => {});\r\n },\r\n [participant?.media?.stream],\r\n );\r\n\r\n return {\r\n videoRef,\r\n audioRef,\r\n isCamActive: !!participant?.media?.camEnabled,\r\n isMicEnabled: !!participant?.media?.micEnabled,\r\n };\r\n};\r\n"],"mappings":"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","names":["import_react","import_react","peerId","import_react","import_react","import_react","participant"]}
|
package/dist/index.mjs
CHANGED
|
@@ -382,7 +382,6 @@ var VideoSDKCore = class {
|
|
|
382
382
|
if (!p?.id || p.id === this.myId) continue;
|
|
383
383
|
this.state.addParticipant(p);
|
|
384
384
|
this.events.onUserJoined?.(p);
|
|
385
|
-
await this.createOffer(p.id);
|
|
386
385
|
}
|
|
387
386
|
break;
|
|
388
387
|
case "JOINED": {
|
|
@@ -405,8 +404,8 @@ var VideoSDKCore = class {
|
|
|
405
404
|
case "ANSWER": {
|
|
406
405
|
const pc = this.peers[msg.sender];
|
|
407
406
|
if (!pc) return;
|
|
408
|
-
if (pc.signalingState
|
|
409
|
-
console.warn("
|
|
407
|
+
if (pc.signalingState === "stable") {
|
|
408
|
+
console.warn("Late answer received, restarting negotiation");
|
|
410
409
|
return;
|
|
411
410
|
}
|
|
412
411
|
await pc.setRemoteDescription({
|
|
@@ -508,6 +507,14 @@ var VideoSDKCore = class {
|
|
|
508
507
|
// ---------------- PEER ----------------
|
|
509
508
|
createPeer(id) {
|
|
510
509
|
if (!this.localStream) throw new Error("No local stream");
|
|
510
|
+
console.log(
|
|
511
|
+
"Adding tracks",
|
|
512
|
+
this.localStream.getTracks().map((t) => ({
|
|
513
|
+
kind: t.kind,
|
|
514
|
+
enabled: t.enabled,
|
|
515
|
+
state: t.readyState
|
|
516
|
+
}))
|
|
517
|
+
);
|
|
511
518
|
const pc = new RTCPeerConnection({
|
|
512
519
|
iceServers: [
|
|
513
520
|
{
|
|
@@ -536,7 +543,8 @@ var VideoSDKCore = class {
|
|
|
536
543
|
} else {
|
|
537
544
|
this.state.updateParticipantMedia(id, {
|
|
538
545
|
stream: incomingStream,
|
|
539
|
-
cameraTrack: incomingStream.getVideoTracks()[0]
|
|
546
|
+
cameraTrack: incomingStream.getVideoTracks()[0],
|
|
547
|
+
audioTrack: incomingStream.getAudioTracks()[0]
|
|
540
548
|
});
|
|
541
549
|
this.events.onTrack?.(incomingStream, id);
|
|
542
550
|
}
|
|
@@ -572,17 +580,10 @@ var VideoSDKCore = class {
|
|
|
572
580
|
}
|
|
573
581
|
// ---------------- OFFER ----------------
|
|
574
582
|
async createOffer(id, isRenegotiation = false) {
|
|
575
|
-
if (!isRenegotiation && this.initiators.has(id)) return;
|
|
576
|
-
if (!isRenegotiation) {
|
|
577
|
-
this.initiators.add(id);
|
|
578
|
-
}
|
|
579
583
|
if (!this.peers[id]) {
|
|
580
584
|
this.peers[id] = this.createPeer(id);
|
|
581
585
|
}
|
|
582
586
|
const pc = this.peers[id];
|
|
583
|
-
if (pc.signalingState !== "stable") {
|
|
584
|
-
return;
|
|
585
|
-
}
|
|
586
587
|
const offer = await pc.createOffer();
|
|
587
588
|
await pc.setLocalDescription(offer);
|
|
588
589
|
this.send({
|
|
@@ -634,39 +635,55 @@ var VideoSDKCore = class {
|
|
|
634
635
|
this.state.removeParticipant(id);
|
|
635
636
|
}
|
|
636
637
|
async startScreenShare() {
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
this.screenStream = await navigator.mediaDevices.getDisplayMedia({
|
|
641
|
-
video: true,
|
|
642
|
-
audio: true
|
|
643
|
-
});
|
|
644
|
-
this.isScreenSharing = true;
|
|
645
|
-
this.state.updateLocalParticipant({
|
|
646
|
-
media: {
|
|
647
|
-
isScreenSharing: true,
|
|
648
|
-
screenStream: this.screenStream
|
|
638
|
+
try {
|
|
639
|
+
if (this.state.presenterId && this.state.presenterId !== this.myId) {
|
|
640
|
+
throw new Error("Another user is already sharing their screen.");
|
|
649
641
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
this.
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
this.screenSenders[peerId] = [];
|
|
657
|
-
this.screenStream.getTracks().forEach((track) => {
|
|
658
|
-
const sender = pc.addTrack(track, this.screenStream);
|
|
659
|
-
this.screenSenders[peerId].push(sender);
|
|
642
|
+
if (!navigator.mediaDevices?.getDisplayMedia) {
|
|
643
|
+
throw new Error("Screen sharing not supported on this device");
|
|
644
|
+
}
|
|
645
|
+
this.screenStream = await navigator.mediaDevices.getDisplayMedia({
|
|
646
|
+
video: true
|
|
647
|
+
// audio: true,
|
|
660
648
|
});
|
|
661
|
-
this.
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
649
|
+
this.isScreenSharing = true;
|
|
650
|
+
this.state.updateLocalParticipant({
|
|
651
|
+
media: {
|
|
652
|
+
isScreenSharing: true,
|
|
653
|
+
screenStream: this.screenStream,
|
|
654
|
+
screenTrack: this.screenStream.getVideoTracks()[0]
|
|
655
|
+
}
|
|
656
|
+
});
|
|
657
|
+
this.state.setPresenterId(this.myId);
|
|
658
|
+
this.screenStream.getVideoTracks()[0].onended = () => {
|
|
659
|
+
this.stopScreenShare();
|
|
660
|
+
};
|
|
661
|
+
Object.entries(this.peers).forEach(([peerId, pc]) => {
|
|
662
|
+
this.screenSenders[peerId] = [];
|
|
663
|
+
this.screenStream.getTracks().forEach((track) => {
|
|
664
|
+
const sender = pc.addTrack(track, this.screenStream);
|
|
665
|
+
this.screenSenders[peerId].push(sender);
|
|
666
|
+
});
|
|
667
|
+
this.createOffer(peerId, true);
|
|
668
|
+
});
|
|
669
|
+
this.send({
|
|
670
|
+
type: "SCREEN_SHARE_START",
|
|
671
|
+
sender: this.myId,
|
|
672
|
+
room_id: this.roomId,
|
|
673
|
+
stream_id: this.screenStream.id.replace(/[{}]/g, "")
|
|
674
|
+
});
|
|
675
|
+
return this.screenStream;
|
|
676
|
+
} catch (err) {
|
|
677
|
+
this.emitError(
|
|
678
|
+
"SCREEN_SHARE_FAILED",
|
|
679
|
+
err?.message || "Failed to start screen sharing",
|
|
680
|
+
err,
|
|
681
|
+
true
|
|
682
|
+
);
|
|
683
|
+
this.isScreenSharing = false;
|
|
684
|
+
this.screenStream = null;
|
|
685
|
+
throw err;
|
|
686
|
+
}
|
|
670
687
|
}
|
|
671
688
|
stopScreenShare() {
|
|
672
689
|
if (!this.screenStream) return;
|
|
@@ -904,7 +921,6 @@ var useLocalParticipant = () => {
|
|
|
904
921
|
video.srcObject = stream;
|
|
905
922
|
video.autoplay = true;
|
|
906
923
|
video.playsInline = true;
|
|
907
|
-
video.muted = true;
|
|
908
924
|
video.play().catch((err) => {
|
|
909
925
|
console.warn(`Autoplay failed for local view:`, err);
|
|
910
926
|
});
|
|
@@ -948,34 +964,56 @@ var useParticipants = () => {
|
|
|
948
964
|
};
|
|
949
965
|
|
|
950
966
|
// src/react/useRemoteMedia.ts
|
|
951
|
-
import {
|
|
967
|
+
import { useCallback as useCallback2, useEffect as useEffect5, useState as useState4 } from "react";
|
|
952
968
|
var useRemoteMedia = (participantId) => {
|
|
953
969
|
const { sdk } = useMeetingContext();
|
|
954
|
-
const videoRef = useRef3(null);
|
|
955
|
-
const audioRef = useRef3(null);
|
|
956
970
|
const [participant, setParticipant] = useState4(
|
|
957
971
|
() => sdk.state.getParticipant(participantId) || null
|
|
958
972
|
);
|
|
973
|
+
const buildMediaStream = (participant2) => {
|
|
974
|
+
if (!participant2?.media) return null;
|
|
975
|
+
const stream = new MediaStream();
|
|
976
|
+
const videoTrack = participant2.media.stream?.getVideoTracks?.()?.[0];
|
|
977
|
+
const audioTrack = participant2.media.stream?.getAudioTracks?.()?.[0];
|
|
978
|
+
if (videoTrack) stream.addTrack(videoTrack);
|
|
979
|
+
if (audioTrack) stream.addTrack(audioTrack);
|
|
980
|
+
return stream;
|
|
981
|
+
};
|
|
959
982
|
useEffect5(() => {
|
|
960
983
|
const unsub = sdk.state.subscribe(`participant:${participantId}`, () => {
|
|
961
|
-
const
|
|
962
|
-
|
|
963
|
-
setParticipant({ ...updated });
|
|
964
|
-
}
|
|
984
|
+
const p = sdk.state.getParticipant(participantId);
|
|
985
|
+
setParticipant(p ? { ...p } : null);
|
|
965
986
|
});
|
|
966
987
|
return unsub;
|
|
967
988
|
}, [participantId, sdk]);
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
989
|
+
const videoRef = useCallback2(
|
|
990
|
+
(node) => {
|
|
991
|
+
if (!node) return;
|
|
992
|
+
const stream = participant?.media?.stream;
|
|
993
|
+
if (!stream) return;
|
|
994
|
+
node.pause();
|
|
995
|
+
node.srcObject = stream;
|
|
996
|
+
node.muted = true;
|
|
997
|
+
node.playsInline = true;
|
|
998
|
+
node.autoplay = true;
|
|
999
|
+
node.play().catch(() => {
|
|
1000
|
+
});
|
|
1001
|
+
},
|
|
1002
|
+
[participant?.media?.stream]
|
|
1003
|
+
);
|
|
1004
|
+
const audioRef = useCallback2(
|
|
1005
|
+
(node) => {
|
|
1006
|
+
if (!node) return;
|
|
1007
|
+
const stream = participant?.media?.stream;
|
|
1008
|
+
if (!stream) return;
|
|
1009
|
+
node.pause();
|
|
1010
|
+
node.srcObject = stream;
|
|
1011
|
+
node.muted = false;
|
|
1012
|
+
node.play().catch(() => {
|
|
1013
|
+
});
|
|
1014
|
+
},
|
|
1015
|
+
[participant?.media?.stream]
|
|
1016
|
+
);
|
|
979
1017
|
return {
|
|
980
1018
|
videoRef,
|
|
981
1019
|
audioRef,
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/react/useLocalParticipant.tsx","../src/react/MeetingProvider.tsx","../src/config/ws.ts","../src/core/MeetingState.ts","../src/core/VideoCore.ts","../src/react/useMeetingStore.ts","../src/react/useMeeting.ts","../src/react/useParticipants.ts","../src/react/useRemoteMedia.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useLocalParticipant = () => {\r\n const { sdk } = useMeetingContext();\r\n\r\n // Guard the initial state to ensure it matches Participant | null\r\n const [localParticipant, setLocalParticipant] = useState<Participant | null>(\r\n () => {\r\n const current = sdk.state.localParticipant;\r\n return current && current.id ? (current as Participant) : null;\r\n },\r\n );\r\n\r\n useEffect(() => {\r\n const unsubscribe = sdk.state.subscribe(\"localParticipant\", () => {\r\n const current = sdk.state.localParticipant;\r\n\r\n // Safe Type-Guard: Only update if the object has a finalized id\r\n if (current && current.id) {\r\n setLocalParticipant({ ...current } as Participant);\r\n } else {\r\n setLocalParticipant(null);\r\n }\r\n });\r\n\r\n return unsubscribe;\r\n }, [sdk]);\r\n\r\n const lastStreamRef = useRef<MediaStream | null>(null);\r\n\r\n const videoRef = useCallback(\r\n (video: HTMLVideoElement | null) => {\r\n if (!video) return;\r\n\r\n const stream = localParticipant?.media?.stream;\r\n if (!stream) return;\r\n\r\n if (lastStreamRef.current === stream) return;\r\n lastStreamRef.current = stream;\r\n\r\n video.srcObject = stream;\r\n video.autoplay = true;\r\n video.playsInline = true;\r\n video.muted = true;\r\n\r\n video.play().catch((err) => {\r\n console.warn(`Autoplay failed for local view:`, err);\r\n });\r\n },\r\n [localParticipant?.media?.stream],\r\n );\r\n\r\n return {\r\n participant: localParticipant,\r\n videoRef,\r\n };\r\n};\r\n","import React, { createContext, useContext, useMemo, useRef } from \"react\";\r\nimport { VideoSDKCore } from \"../core/VideoCore\";\r\nimport {\r\n ChatInput,\r\n ChatMessage,\r\n MeetingConfig,\r\n Participant,\r\n PubSubTopic,\r\n} from \"../types/meeting\";\r\nimport { useMeetingStore } from \"./useMeetingStore\";\r\n\r\ntype PubSubHandle = {\r\n messages: ChatMessage[];\r\n publish: (input: ChatInput) => void;\r\n};\r\n\r\ntype MeetingContextValue = {\r\n sdk: VideoSDKCore;\r\n\r\n join: (config: MeetingConfig) => Promise<void>;\r\n leave: () => void;\r\n\r\n toggleMic: () => void;\r\n toggleCam: () => void;\r\n\r\n startScreenShare: () => Promise<MediaStream>;\r\n stopScreenShare: () => void;\r\n\r\n sendMessage: (input: ChatInput) => void;\r\n\r\n meetingId: string | null;\r\n localParticipant: Participant | null;\r\n participants: Map<string, Participant>;\r\n messages: ChatMessage[];\r\n presenterId: string | null;\r\n usePubSub: (topic: PubSubTopic) => PubSubHandle;\r\n onError: (cb: (err: any) => void) => () => void;\r\n};\r\n\r\nconst MeetingContext = createContext<MeetingContextValue | null>(null);\r\n\r\nexport const MeetingProvider = ({\r\n config,\r\n children,\r\n}: {\r\n config: MeetingConfig;\r\n children: React.ReactNode;\r\n}) => {\r\n const sdkRef = useRef<VideoSDKCore | null>(null);\r\n const errorListeners = useRef(new Set<(err: any) => void>());\r\n if (!sdkRef.current) {\r\n sdkRef.current = new VideoSDKCore({\r\n onError: (err) => {\r\n errorListeners.current.forEach((fn) => fn(err));\r\n },\r\n });\r\n }\r\n\r\n const sdk = sdkRef.current;\r\n const presenterId = useMeetingStore(\r\n sdk.state,\r\n \"presenter\",\r\n (s) => s.presenterId,\r\n );\r\n const participants = useMeetingStore(\r\n sdk.state,\r\n \"participants\",\r\n (s) => s.participants,\r\n );\r\n const localParticipant = useMeetingStore(\r\n sdk.state,\r\n \"localParticipant\",\r\n (s) => s.localParticipant,\r\n );\r\n const messages = useMeetingStore(sdk.state, \"chat\", (s) =>\r\n s.getChatMessages(),\r\n );\r\n\r\n const value = useMemo<MeetingContextValue>(() => {\r\n if (!sdkRef.current) {\r\n sdkRef.current = new VideoSDKCore({\r\n onError: (err) => {\r\n errorListeners.current.forEach((fn) => fn(err));\r\n },\r\n });\r\n }\r\n\r\n return {\r\n sdk,\r\n\r\n join: (joinConfig: MeetingConfig) =>\r\n sdk.joinMeeting({\r\n ...config,\r\n ...joinConfig,\r\n }),\r\n leave: () => sdk.disconnect(),\r\n toggleMic: sdk.toggleMic.bind(sdk),\r\n toggleCam: sdk.toggleCam.bind(sdk),\r\n startScreenShare: sdk.startScreenShare.bind(sdk),\r\n stopScreenShare: sdk.stopScreenShare.bind(sdk),\r\n sendMessage: sdk.sendChatMessage.bind(sdk),\r\n\r\n meetingId: sdk.getMeetingId(),\r\n localParticipant,\r\n participants,\r\n messages,\r\n presenterId,\r\n usePubSub: (topic: PubSubTopic) => {\r\n if (topic !== \"SECURE_CHAT\") {\r\n throw new Error(`Unsupported PubSub argument: \"${topic}\"`);\r\n }\r\n return {\r\n messages: sdk.state.getChatMessages(),\r\n publish: sdk.sendChatMessage.bind(sdk),\r\n };\r\n },\r\n onError: (cb: (err: any) => void) => {\r\n errorListeners.current.add(cb);\r\n\r\n return () => {\r\n errorListeners.current.delete(cb);\r\n };\r\n },\r\n };\r\n }, [config, sdk, localParticipant, participants, messages, presenterId]);\r\n\r\n return (\r\n <MeetingContext.Provider value={value}>{children}</MeetingContext.Provider>\r\n );\r\n};\r\n\r\nexport const useMeetingContext = () => {\r\n const ctx = useContext(MeetingContext);\r\n if (!ctx)\r\n throw new Error(\"useMeetingContext must be used inside <MeetingProvider>\");\r\n return ctx;\r\n};\r\n","export const SDK_CONFIG = {\r\n wsUrl: \"wss://rust-video-server-sfyf.onrender.com/ws\",\r\n};\r\n","import {\r\n ChatMessage,\r\n Listener,\r\n Participant,\r\n ParticipantMedia,\r\n StateScope,\r\n} from \"../types/meeting\";\r\n\r\ntype LocalParticipantPatch = {\r\n id?: string;\r\n name?: string;\r\n media?: Partial<ParticipantMedia>;\r\n};\r\n\r\nexport class MeetingState {\r\n participants = new Map<string, Participant>();\r\n localParticipant: Participant | null = null;\r\n localStream: MediaStream | null = null;\r\n chatMessages = new Map<string, ChatMessage>();\r\n presenterId: string | null = null;\r\n\r\n private listeners = new Map<StateScope, Set<Listener>>();\r\n\r\n // ---- reactive system ----\r\n\r\n subscribe(scope: StateScope, fn: Listener): () => void {\r\n if (!this.listeners.has(scope)) {\r\n this.listeners.set(scope, new Set());\r\n }\r\n this.listeners.get(scope)!.add(fn);\r\n\r\n return () => {\r\n this.listeners.get(scope)?.delete(fn);\r\n };\r\n }\r\n\r\n notify(scope: StateScope) {\r\n this.listeners.get(scope)?.forEach((fn) => fn());\r\n }\r\n\r\n setPresenterId(id: string | null) {\r\n if (this.presenterId === id) return;\r\n this.presenterId = id;\r\n this.notify(\"presenter\");\r\n this.notify(\"participants\");\r\n }\r\n\r\n // ---- participants ----\r\n\r\n addParticipant(p: Participant) {\r\n if (this.participants.has(p.id)) return false;\r\n // Fix: Immutable Map update\r\n const next = new Map(this.participants);\r\n next.set(p.id, p);\r\n this.participants = next;\r\n\r\n this.notify(\"participants\");\r\n return true;\r\n }\r\n\r\n removeParticipant(id: string) {\r\n // Fix: Immutable Map update\r\n const next = new Map(this.participants);\r\n next.delete(id);\r\n this.participants = next;\r\n\r\n this.notify(\"participants\");\r\n }\r\n\r\n updateParticipantMedia(\r\n id: string,\r\n patch: Partial<NonNullable<Participant[\"media\"]>>,\r\n ) {\r\n const p = this.participants.get(id);\r\n if (!p) return;\r\n\r\n const updated: Participant = {\r\n ...p,\r\n media: {\r\n stream: null,\r\n screenStream: undefined,\r\n cameraTrack: undefined,\r\n screenTrack: undefined,\r\n audioTrack: undefined,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n ...p.media, // preserve existing media items if they happen to exist\r\n ...patch, // apply the incoming stream updates\r\n },\r\n };\r\n\r\n // Keep your clean immutable map update\r\n const next = new Map(this.participants);\r\n next.set(id, updated);\r\n this.participants = next;\r\n\r\n this.notify(`participant:${id}`);\r\n this.notify(\"participants\");\r\n }\r\n\r\n updateLocalParticipant(patch: LocalParticipantPatch) {\r\n const prev = this.localParticipant;\r\n\r\n if (!prev) {\r\n this.localParticipant = {\r\n id: patch.id ?? \"\",\r\n name: patch.name ?? \"\",\r\n media: {\r\n stream: patch.media?.stream ?? null, // ◄ FIX: Capture the stream from the patch here\r\n screenStream: patch.media?.screenStream,\r\n cameraTrack: patch.media?.cameraTrack,\r\n screenTrack: patch.media?.screenTrack,\r\n audioTrack: patch.media?.audioTrack,\r\n micEnabled: patch.media?.micEnabled ?? true,\r\n camEnabled: patch.media?.camEnabled ?? true,\r\n isScreenSharing: patch.media?.isScreenSharing ?? false,\r\n },\r\n };\r\n\r\n this.notify(\"localParticipant\");\r\n return;\r\n }\r\n\r\n const prevMedia = prev.media ?? {\r\n stream: null,\r\n screenStream: undefined,\r\n cameraTrack: undefined,\r\n screenTrack: undefined,\r\n audioTrack: undefined,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n };\r\n\r\n const nextMedia: ParticipantMedia = {\r\n stream: patch.media?.stream ?? prevMedia.stream,\r\n screenStream: patch.media?.screenStream ?? prevMedia.screenStream,\r\n cameraTrack: patch.media?.cameraTrack ?? prevMedia.cameraTrack,\r\n screenTrack: patch.media?.screenTrack ?? prevMedia.screenTrack,\r\n audioTrack: patch.media?.audioTrack ?? prevMedia.audioTrack,\r\n micEnabled: patch.media?.micEnabled ?? prevMedia.micEnabled,\r\n camEnabled: patch.media?.camEnabled ?? prevMedia.camEnabled,\r\n isScreenSharing:\r\n patch.media?.isScreenSharing ?? prevMedia.isScreenSharing,\r\n };\r\n\r\n this.localParticipant = {\r\n ...prev,\r\n id: patch.id ?? prev.id,\r\n name: patch.name ?? prev.name,\r\n media: nextMedia,\r\n };\r\n\r\n this.notify(\"localParticipant\");\r\n }\r\n\r\n // ---- chat ----\r\n\r\n addChatMessage(msg: ChatMessage) {\r\n this.chatMessages.set(msg.id, msg);\r\n this.notify(\"chat\");\r\n }\r\n\r\n getChatMessages() {\r\n return Array.from(this.chatMessages.values()).sort(\r\n (a, b) => a.timestamp - b.timestamp,\r\n );\r\n }\r\n\r\n clearChat() {\r\n this.chatMessages.clear();\r\n this.notify(\"chat\");\r\n }\r\n\r\n // ---- helpers ----\r\n\r\n getParticipants() {\r\n return Array.from(this.participants.values());\r\n }\r\n\r\n getParticipant(id: string) {\r\n return this.participants.get(id) ?? null;\r\n }\r\n\r\n resetRemoteState() {\r\n this.participants.clear();\r\n this.chatMessages.clear();\r\n this.presenterId = null;\r\n this.notify(\"participants\");\r\n this.notify(\"chat\");\r\n this.notify(\"presenter\");\r\n }\r\n}\r\n","import { SDK_CONFIG } from \"../config/ws\";\r\nimport {\r\n ChatInput,\r\n ChatMessage,\r\n Events,\r\n MeetingConfig,\r\n SDKError,\r\n} from \"../types/meeting\";\r\nimport { MeetingState } from \"./MeetingState\";\r\n\r\nexport class VideoSDKCore {\r\n private ws: WebSocket | null = null;\r\n private peers: Record<string, RTCPeerConnection> = {};\r\n private initiators = new Set<string>();\r\n\r\n private myId: string;\r\n private roomId: string | null = null;\r\n private localStream: MediaStream | null = null;\r\n private screenStream: MediaStream | null = null;\r\n private isScreenSharing = false;\r\n private screenSenders: Record<string, RTCRtpSender[]> = {};\r\n\r\n private pingInterval: any = null;\r\n private pendingIceCandidates: Record<string, RTCIceCandidateInit[]> = {};\r\n private reconnectAttempts = 0;\r\n private reconnectTimer?: number;\r\n private participantName = \"\";\r\n public readonly state: MeetingState;\r\n private joinResolver?: () => void;\r\n private joinRejecter?: (e: any) => void;\r\n private emitError(\r\n code: string,\r\n message: string,\r\n raw?: any,\r\n recoverable = true,\r\n ) {\r\n const err: SDKError = {\r\n code,\r\n message,\r\n raw,\r\n roomId: this.roomId,\r\n userId: this.myId,\r\n recoverable,\r\n };\r\n\r\n this.events.onError?.(err);\r\n\r\n this.joinRejecter?.(err);\r\n this.joinRejecter = undefined;\r\n\r\n console.error(\"[MeetingSDK Error]\", err);\r\n }\r\n\r\n constructor(\r\n private events: Events = {},\r\n private url: string = SDK_CONFIG.wsUrl,\r\n ) {\r\n this.state = new MeetingState();\r\n this.events = events;\r\n this.url = url;\r\n\r\n this.myId = localStorage.getItem(\"vsdk_id\") || crypto.randomUUID();\r\n\r\n localStorage.setItem(\"vsdk_id\", this.myId);\r\n }\r\n\r\n // ---------------- STREAM ----------------\r\n async initLocal(video: HTMLVideoElement, name: string) {\r\n this.participantName = name;\r\n\r\n if (!this.localStream) {\r\n this.localStream = await navigator.mediaDevices.getUserMedia({\r\n video: true,\r\n audio: true,\r\n });\r\n }\r\n\r\n video.srcObject = this.localStream;\r\n\r\n // Fix: Supply mandatory fields to satisfy the Participant type constraint\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n stream: this.localStream,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n },\r\n });\r\n\r\n this.state.localStream = this.localStream;\r\n }\r\n\r\n // ---------------- CONNECT ----------------\r\n async connect(roomId: string, name: string) {\r\n this.roomId = roomId;\r\n\r\n this.reset();\r\n\r\n return new Promise<void>((resolve, reject) => {\r\n this.joinResolver = resolve;\r\n this.joinRejecter = reject;\r\n this.ws = new WebSocket(this.url);\r\n\r\n this.ws.onopen = () => {\r\n this.send({\r\n type: \"JOIN\",\r\n room_id: roomId,\r\n user_id: this.myId,\r\n sender_name: name,\r\n });\r\n };\r\n\r\n this.ws.onerror = (err) => {\r\n this.emitError(\"WS_ERROR\", \"WebSocket encountered an error\", err, true);\r\n };\r\n\r\n this.ws.onclose = (e) => {\r\n this.emitError(\r\n \"WS_CLOSED\",\r\n `Connection closed (${e.code}) ${e.reason || \"\"}`,\r\n e,\r\n true,\r\n );\r\n\r\n // If join never resolved, fail the promise\r\n this.joinRejecter?.({\r\n code: \"WS_CLOSED\",\r\n message: \"Connection closed before join completed\",\r\n raw: e,\r\n });\r\n\r\n this.joinRejecter = undefined;\r\n\r\n this.scheduleReconnect();\r\n };\r\n\r\n this.ws.onmessage = async (e) => {\r\n await this.handle(JSON.parse(e.data));\r\n };\r\n });\r\n }\r\n\r\n async joinMeeting(config: MeetingConfig) {\r\n const { roomId, name, audioMuted = false, videoMuted = false } = config;\r\n\r\n if (!roomId || !name) {\r\n throw new Error(\"roomId and name are required to join meeting\");\r\n }\r\n\r\n this.participantName = name;\r\n\r\n // Reuse existing stream if initLocal already configured it\r\n if (!this.localStream) {\r\n this.localStream = await navigator.mediaDevices.getUserMedia({\r\n video: true,\r\n audio: true,\r\n });\r\n }\r\n\r\n this.localStream.getAudioTracks().forEach((t) => {\r\n t.enabled = !audioMuted;\r\n });\r\n this.localStream.getVideoTracks().forEach((t) => {\r\n t.enabled = !videoMuted;\r\n });\r\n\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n stream: this.localStream,\r\n micEnabled: !audioMuted,\r\n camEnabled: !videoMuted,\r\n isScreenSharing: false,\r\n },\r\n });\r\n\r\n this.state.localStream = this.localStream;\r\n\r\n await this.connect(roomId, name);\r\n }\r\n\r\n /** Expose the roomId without making it fully public */\r\n getMeetingId(): string | null {\r\n return this.roomId;\r\n }\r\n\r\n toggleMic() {\r\n const mediaState = this.state.localParticipant?.media;\r\n if (!mediaState) return;\r\n\r\n // Flip the state tracked in localParticipant.media\r\n const nextEnabled = !mediaState.micEnabled;\r\n\r\n // Mirror the state change down to the actual hardware tracks\r\n this.localStream\r\n ?.getAudioTracks()\r\n .forEach((t) => (t.enabled = nextEnabled));\r\n\r\n // Update state layer\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n ...mediaState,\r\n micEnabled: nextEnabled,\r\n },\r\n });\r\n\r\n // Notify peers\r\n this.send({\r\n type: \"MEDIA_STATE\",\r\n kind: \"audio\",\r\n enabled: nextEnabled,\r\n });\r\n }\r\n\r\n toggleCam() {\r\n const mediaState = this.state.localParticipant?.media;\r\n if (!mediaState) return;\r\n\r\n // Flip the state tracked in localParticipant.media\r\n const nextEnabled = !mediaState.camEnabled;\r\n\r\n // Mirror the state change down to the actual hardware tracks\r\n this.localStream\r\n ?.getVideoTracks()\r\n .forEach((t) => (t.enabled = nextEnabled));\r\n\r\n // Update state layer\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n ...mediaState,\r\n camEnabled: nextEnabled,\r\n },\r\n });\r\n\r\n // Notify peers\r\n this.send({\r\n type: \"MEDIA_STATE\",\r\n kind: \"video\",\r\n enabled: nextEnabled,\r\n });\r\n }\r\n\r\n private scheduleReconnect() {\r\n if (!this.roomId) return;\r\n\r\n const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);\r\n\r\n clearTimeout(this.reconnectTimer);\r\n\r\n this.reconnectTimer = window.setTimeout(async () => {\r\n try {\r\n await this.connect(this.roomId!, this.participantName);\r\n\r\n this.reconnectAttempts = 0;\r\n } catch {\r\n this.reconnectAttempts++;\r\n this.scheduleReconnect();\r\n }\r\n }, delay);\r\n }\r\n\r\n private startHeartbeat() {\r\n this.stopHeartbeat();\r\n\r\n this.pingInterval = setInterval(() => {\r\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;\r\n\r\n this.send({\r\n type: \"PING\",\r\n client_ts: Date.now(),\r\n });\r\n }, 20000); // every 20s\r\n }\r\n private stopHeartbeat() {\r\n if (this.pingInterval) {\r\n clearInterval(this.pingInterval);\r\n this.pingInterval = null;\r\n }\r\n }\r\n\r\n // ---------------- RESET ----------------\r\n private reset() {\r\n Object.values(this.peers).forEach((pc) => pc.close());\r\n\r\n this.peers = {};\r\n this.initiators.clear();\r\n this.pendingIceCandidates = {};\r\n\r\n this.state.resetRemoteState();\r\n }\r\n\r\n // ---------------- HANDLE SIGNALS ----------------\r\n private async handle(msg: any) {\r\n if (msg.sender === this.myId) return;\r\n\r\n switch (msg.type) {\r\n case \"EXISTING_USERS\":\r\n if (msg.presenterId) {\r\n this.state.setPresenterId(msg.presenterId);\r\n\r\n // Trigger your event so the UI knows to render the stage\r\n this.events.onScreenShareStarted?.(msg.presenterId, null!);\r\n }\r\n\r\n for (const p of msg.participants || []) {\r\n if (!p?.id || p.id === this.myId) continue;\r\n this.state.addParticipant(p);\r\n this.events.onUserJoined?.(p);\r\n await this.createOffer(p.id);\r\n }\r\n break;\r\n\r\n case \"JOINED\": {\r\n this.startHeartbeat();\r\n this.joinResolver?.();\r\n this.joinResolver = undefined;\r\n this.joinRejecter = undefined;\r\n break;\r\n }\r\n case \"USER_JOINED\": {\r\n const p = msg.participant;\r\n\r\n if (!p?.id || p.id === this.myId) return;\r\n\r\n this.state.addParticipant(p);\r\n\r\n this.events.onUserJoined?.(p);\r\n\r\n break;\r\n }\r\n\r\n case \"OFFER\":\r\n await this.handleOffer(msg.payload, msg.sender);\r\n break;\r\n\r\n case \"ANSWER\": {\r\n const pc = this.peers[msg.sender];\r\n if (!pc) return;\r\n\r\n if (pc.signalingState !== \"have-local-offer\") {\r\n console.warn(\"Ignoring invalid answer:\", pc.signalingState);\r\n return;\r\n }\r\n\r\n await pc.setRemoteDescription({\r\n type: \"answer\",\r\n sdp: msg.payload,\r\n });\r\n\r\n // CRITICAL WEBRTC FIX: Flush cached ICE paths now that remoteDescription is set!\r\n await this.flushIce(msg.sender, pc);\r\n\r\n break;\r\n }\r\n\r\n case \"ICE\": {\r\n const candidate = JSON.parse(msg.payload);\r\n\r\n let pc = this.peers[msg.sender];\r\n\r\n if (!pc) {\r\n this.pendingIceCandidates[msg.sender] ??= [];\r\n this.pendingIceCandidates[msg.sender].push(candidate);\r\n break;\r\n }\r\n\r\n if (!pc.remoteDescription) {\r\n this.pendingIceCandidates[msg.sender] ??= [];\r\n this.pendingIceCandidates[msg.sender].push(candidate);\r\n break;\r\n }\r\n\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (err) {\r\n console.warn(\"ICE error:\", err);\r\n }\r\n\r\n break;\r\n }\r\n\r\n case \"USER_LEFT\":\r\n const peerId = msg.participant.id;\r\n this.closePeer(peerId);\r\n\r\n this.state.removeParticipant(peerId);\r\n\r\n this.events.onUserLeft?.(peerId);\r\n\r\n break;\r\n\r\n case \"MEDIA_STATE_CHANGE\": {\r\n const peerId = msg.peerId;\r\n const { kind, enabled } = msg;\r\n\r\n // 1. Sync the app state layer for UI rendering components\r\n if (kind === \"audio\") {\r\n this.state.updateParticipantMedia(peerId, { micEnabled: enabled });\r\n this.events.onMicToggled?.(peerId, enabled);\r\n } else if (kind === \"video\") {\r\n this.state.updateParticipantMedia(peerId, { camEnabled: enabled });\r\n this.events.onCamToggled?.(peerId, enabled);\r\n }\r\n\r\n break;\r\n }\r\n\r\n case \"CHAT_MESSAGE\": {\r\n const newMsg = msg.data;\r\n\r\n if (newMsg.sender_id === this.myId) break; // already added optimistically\r\n this.state.addChatMessage({\r\n id: newMsg.id,\r\n text: newMsg.message,\r\n sender_id: newMsg.sender_id,\r\n sender_name: newMsg.sender_name,\r\n timestamp: new Date(newMsg.timestamp).getTime(),\r\n target: newMsg.target,\r\n });\r\n\r\n this.events.onChatMessage?.(msg);\r\n break;\r\n }\r\n case \"SCREEN_SHARE_START\": {\r\n const peerId = msg.peerId;\r\n\r\n this.state.updateParticipantMedia(peerId, {\r\n isScreenSharing: true,\r\n remoteScreenStreamId: msg.stream_id,\r\n });\r\n\r\n if (!this.state.presenterId) {\r\n this.state.setPresenterId(peerId);\r\n }\r\n\r\n // Fix: Use screenStream instead of the regular camera stream\r\n const screenStream =\r\n this.state.getParticipant(peerId)?.media?.screenStream;\r\n\r\n this.events.onScreenShareStarted?.(peerId, screenStream || null!);\r\n break;\r\n }\r\n\r\n case \"SCREEN_SHARE_STOP\": {\r\n const peerId = msg.peerId;\r\n this.state.updateParticipantMedia(peerId, { isScreenSharing: false });\r\n if (this.state.presenterId === peerId) {\r\n this.state.setPresenterId(null);\r\n }\r\n this.events.onScreenShareStopped?.(peerId);\r\n break;\r\n }\r\n case \"ERROR\": {\r\n const fatal = msg?.fatal === true;\r\n\r\n this.emitError(\r\n \"WS_ERROR\",\r\n msg?.message || \"Unknown error\",\r\n msg,\r\n !fatal,\r\n );\r\n\r\n if (fatal) {\r\n this.disconnect();\r\n }\r\n\r\n return;\r\n }\r\n }\r\n }\r\n\r\n // ---------------- PEER ----------------\r\n private createPeer(id: string) {\r\n if (!this.localStream) throw new Error(\"No local stream\");\r\n\r\n const pc = new RTCPeerConnection({\r\n iceServers: [\r\n {\r\n urls: [\r\n \"stun:stun.l.google.com:19302\",\r\n \"stun:stun1.l.google.com:19302\",\r\n ],\r\n },\r\n ],\r\n });\r\n\r\n pc.ontrack = (event) => {\r\n const incomingStream = event.streams[0];\r\n const participant = this.state.getParticipant(id);\r\n\r\n const isScreenStream =\r\n incomingStream.id === participant?.media?.remoteScreenStreamId;\r\n\r\n if (isScreenStream) {\r\n const videoTrack =\r\n event.track.kind === \"video\"\r\n ? event.track\r\n : incomingStream.getVideoTracks()[0] ||\r\n participant?.media?.screenTrack;\r\n\r\n this.state.updateParticipantMedia(id, {\r\n screenStream: incomingStream,\r\n screenTrack: videoTrack,\r\n\r\n isScreenSharing: true,\r\n });\r\n\r\n // CRITICAL FIX: Late joiners never get SCREEN_SHARE_START.\r\n // We must set the presenter ID here if it isn't set, otherwise the grid won't render the stage.\r\n if (!this.state.presenterId) {\r\n this.state.setPresenterId(id);\r\n }\r\n\r\n this.events.onScreenShareStarted?.(id, incomingStream);\r\n } else {\r\n this.state.updateParticipantMedia(id, {\r\n stream: incomingStream,\r\n cameraTrack: incomingStream.getVideoTracks()[0],\r\n });\r\n this.events.onTrack?.(incomingStream, id);\r\n }\r\n };\r\n\r\n pc.onicecandidate = (e) => {\r\n if (!e.candidate) return;\r\n this.send({\r\n type: \"ICE\",\r\n payload: JSON.stringify(e.candidate),\r\n sender: this.myId,\r\n target: id,\r\n });\r\n };\r\n\r\n pc.onconnectionstatechange = () => {\r\n if (pc.connectionState === \"failed\") {\r\n try {\r\n pc.restartIce();\r\n } catch {}\r\n }\r\n };\r\n\r\n this.localStream.getTracks().forEach((track) => {\r\n pc.addTrack(track, this.localStream!);\r\n });\r\n\r\n if (this.isScreenSharing && this.screenStream) {\r\n this.screenSenders[id] = [];\r\n this.screenStream.getTracks().forEach((track) => {\r\n const sender = pc.addTrack(track, this.screenStream!);\r\n this.screenSenders[id].push(sender);\r\n });\r\n }\r\n\r\n return pc;\r\n }\r\n\r\n // ---------------- OFFER ----------------\r\n private async createOffer(id: string, isRenegotiation = false) {\r\n // Only apply the initial glare gate if this is not a track renegotiation\r\n if (!isRenegotiation && this.initiators.has(id)) return;\r\n\r\n if (!isRenegotiation) {\r\n this.initiators.add(id);\r\n }\r\n\r\n if (!this.peers[id]) {\r\n this.peers[id] = this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n if (pc.signalingState !== \"stable\") {\r\n return;\r\n }\r\n\r\n const offer = await pc.createOffer();\r\n await pc.setLocalDescription(offer);\r\n\r\n this.send({\r\n type: \"OFFER\",\r\n payload: offer.sdp,\r\n sender: this.myId,\r\n target: id,\r\n });\r\n }\r\n\r\n // ---------------- ANSWER ----------------\r\n private async handleOffer(sdp: string, id: string) {\r\n if (!this.peers[id]) {\r\n this.peers[id] = this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n await pc.setRemoteDescription({\r\n type: \"offer\",\r\n sdp,\r\n });\r\n\r\n const pending = this.pendingIceCandidates[id] || [];\r\n\r\n for (const candidate of pending) {\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (err) {\r\n console.warn(err);\r\n }\r\n }\r\n\r\n delete this.pendingIceCandidates[id];\r\n\r\n const answer = await pc.createAnswer();\r\n\r\n await pc.setLocalDescription(answer);\r\n await this.flushIce(id, pc);\r\n\r\n this.send({\r\n type: \"ANSWER\",\r\n payload: answer.sdp,\r\n sender: this.myId,\r\n target: id,\r\n });\r\n }\r\n\r\n // ---------------- CLEANUP ----------------\r\n private closePeer(id: string) {\r\n const pc = this.peers[id];\r\n\r\n if (!pc) return;\r\n\r\n pc.ontrack = null;\r\n pc.onicecandidate = null;\r\n pc.onconnectionstatechange = null;\r\n\r\n pc.close();\r\n\r\n delete this.peers[id];\r\n\r\n this.initiators.delete(id);\r\n\r\n this.state.removeParticipant(id);\r\n }\r\n\r\n async startScreenShare() {\r\n if (this.state.presenterId && this.state.presenterId !== this.myId) {\r\n throw new Error(\"Another user is already sharing their screen.\");\r\n }\r\n\r\n this.screenStream = await navigator.mediaDevices.getDisplayMedia({\r\n video: true,\r\n audio: true,\r\n });\r\n\r\n this.isScreenSharing = true;\r\n\r\n this.state.updateLocalParticipant({\r\n media: {\r\n isScreenSharing: true,\r\n screenStream: this.screenStream,\r\n },\r\n });\r\n\r\n this.state.setPresenterId(this.myId);\r\n // Handle the user clicking browser's built-in \"Stop Sharing\" button\r\n this.screenStream.getVideoTracks()[0].onended = () => {\r\n this.stopScreenShare();\r\n };\r\n\r\n Object.entries(this.peers).forEach(([peerId, pc]) => {\r\n this.screenSenders[peerId] = [];\r\n this.screenStream!.getTracks().forEach((track) => {\r\n const sender = pc.addTrack(track, this.screenStream!);\r\n this.screenSenders[peerId].push(sender);\r\n });\r\n\r\n // Renegotiate peer connection descriptors to notify remote side of new track footprint\r\n this.createOffer(peerId, true);\r\n });\r\n\r\n this.send({\r\n type: \"SCREEN_SHARE_START\",\r\n sender: this.myId,\r\n room_id: this.roomId,\r\n stream_id: this.screenStream.id.replace(/[{}]/g, \"\"),\r\n });\r\n\r\n return this.screenStream;\r\n }\r\n\r\n stopScreenShare() {\r\n if (!this.screenStream) return;\r\n\r\n this.screenStream.getTracks().forEach((t) => t.stop());\r\n\r\n // Remove tracks cleanly from WebRTC channel pathways across your peers\r\n Object.entries(this.peers).forEach(([peerId, pc]) => {\r\n const senders = this.screenSenders[peerId] || [];\r\n senders.forEach((sender) => {\r\n try {\r\n pc.removeTrack(sender);\r\n } catch (err) {\r\n console.warn(err);\r\n }\r\n });\r\n delete this.screenSenders[peerId];\r\n\r\n // Renegotiate layout expectations to scale down stream bounds\r\n this.createOffer(peerId, true);\r\n });\r\n\r\n this.screenStream = null;\r\n this.isScreenSharing = false;\r\n\r\n this.state.updateLocalParticipant({\r\n media: {\r\n isScreenSharing: false,\r\n screenStream: null,\r\n screenTrack: undefined,\r\n },\r\n });\r\n\r\n if (this.state.presenterId === this.myId) {\r\n this.state.setPresenterId(null);\r\n }\r\n\r\n this.send({\r\n type: \"SCREEN_SHARE_STOP\",\r\n sender: this.myId,\r\n room_id: this.roomId,\r\n });\r\n }\r\n\r\n sendChatMessage(payload: ChatInput) {\r\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\r\n console.warn(\"WS not connected\");\r\n return;\r\n }\r\n\r\n if (!this.roomId) {\r\n console.warn(\"No roomId set\");\r\n return;\r\n }\r\n\r\n const isPrivate = !!payload?.target;\r\n\r\n const senderName = this.state.localParticipant?.name || \"Anonymous\";\r\n\r\n const msg: ChatMessage = {\r\n id: crypto.randomUUID(),\r\n sender_id: this.myId,\r\n sender_name: senderName,\r\n text: payload.message.trim(),\r\n timestamp: Date.now(),\r\n reply_to: payload.reply_to ?? null,\r\n target: payload.target ?? null,\r\n };\r\n\r\n // optimistic UI update\r\n this.state.addChatMessage(msg);\r\n\r\n // send protocol payload (clean + consistent)\r\n this.send({\r\n type: \"CHAT_MESSAGE\",\r\n message: payload.message.trim(),\r\n user_id: this.myId,\r\n sender_name: senderName,\r\n room_id: this.roomId,\r\n target: isPrivate ? (payload.target ?? null) : null,\r\n reply_to: payload.reply_to ?? null,\r\n\r\n client_ts: Date.now(),\r\n });\r\n }\r\n\r\n disconnect() {\r\n this.stopScreenShare();\r\n\r\n Object.values(this.peers).forEach((pc) => pc.close());\r\n this.peers = {};\r\n this.initiators.clear();\r\n\r\n this.stopHeartbeat();\r\n\r\n if (this.ws) {\r\n this.ws.close();\r\n this.ws = null;\r\n }\r\n\r\n if (this.localStream) {\r\n this.localStream.getTracks().forEach((track) => track.stop());\r\n this.localStream = null;\r\n }\r\n\r\n this.roomId = null;\r\n\r\n // Clear and notify\r\n this.state.localParticipant = null;\r\n this.state.notify(\"localParticipant\");\r\n\r\n this.state.participants.clear();\r\n this.state.notify(\"participants\");\r\n\r\n this.state.clearChat();\r\n this.state.setPresenterId(null);\r\n }\r\n\r\n private async flushIce(id: string, pc: RTCPeerConnection) {\r\n const pending = this.pendingIceCandidates[id];\r\n if (!pending?.length) return;\r\n\r\n for (const candidate of pending) {\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (e) {\r\n console.warn(\"ICE flush error\", e);\r\n }\r\n }\r\n\r\n delete this.pendingIceCandidates[id];\r\n }\r\n\r\n private send(msg: any) {\r\n this.ws?.send(JSON.stringify(msg));\r\n }\r\n}\r\n","import { useEffect, useState } from \"react\";\r\nimport { StateScope } from \"../types/meeting\";\r\nimport { MeetingState } from \"../core/MeetingState\";\r\n\r\nexport function useMeetingStore<T>(\r\n stateManager: MeetingState,\r\n scope: StateScope,\r\n selector: (state: MeetingState) => T,\r\n): T {\r\n const [state, setState] = useState(() => selector(stateManager));\r\n\r\n useEffect(() => {\r\n // Update local react state whenever the SDK notifies this scope\r\n const unsubscribe = stateManager.subscribe(scope, () => {\r\n setState(selector(stateManager));\r\n });\r\n\r\n return unsubscribe;\r\n }, [stateManager, scope, selector]);\r\n\r\n return state;\r\n}\r\n","import { useEffect } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\n\r\nexport const useMeeting = (handlers?: { onError?: (err: any) => void }) => {\r\n const ctx = useMeetingContext();\r\n\r\n useEffect(() => {\r\n if (!handlers?.onError) return;\r\n\r\n const unsubscribe = ctx.onError(handlers.onError);\r\n return unsubscribe;\r\n }, [handlers?.onError]);\r\n\r\n return ctx;\r\n};\r\n","import { useEffect, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useParticipants = () => {\r\n const { sdk } = useMeetingContext();\r\n\r\n const [participants, setParticipants] = useState<Participant[]>(() =>\r\n sdk.state.getParticipants(),\r\n );\r\n\r\n useEffect(() => {\r\n const update = () => {\r\n setParticipants(sdk.state.getParticipants());\r\n };\r\n\r\n update();\r\n\r\n const unsub = sdk.state.subscribe(\"participants\", update);\r\n\r\n return unsub;\r\n }, [sdk]);\r\n\r\n return participants;\r\n};\r\n","import { useEffect, useRef, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useRemoteMedia = (participantId: string) => {\r\n const { sdk } = useMeetingContext();\r\n\r\n // Create refs to access the DOM elements directly\r\n const videoRef = useRef<HTMLVideoElement>(null);\r\n const audioRef = useRef<HTMLAudioElement>(null);\r\n\r\n const [participant, setParticipant] = useState<Participant | null>(\r\n () => sdk.state.getParticipant(participantId) || null,\r\n );\r\n\r\n // Sync state from SDK to React\r\n useEffect(() => {\r\n const unsub = sdk.state.subscribe(`participant:${participantId}`, () => {\r\n const updated = sdk.state.getParticipant(participantId);\r\n if (updated) {\r\n setParticipant({ ...updated });\r\n }\r\n });\r\n return unsub;\r\n }, [participantId, sdk]);\r\n\r\n // Handle stream attachment whenever the stream changes\r\n useEffect(() => {\r\n const stream = participant?.media?.stream;\r\n\r\n if (stream) {\r\n if (videoRef.current && videoRef.current.srcObject !== stream) {\r\n videoRef.current.srcObject = stream;\r\n }\r\n if (audioRef.current && audioRef.current.srcObject !== stream) {\r\n audioRef.current.srcObject = stream;\r\n }\r\n }\r\n }, [participant?.media?.stream]);\r\n\r\n return {\r\n videoRef,\r\n audioRef,\r\n isCamActive: !!participant?.media?.camEnabled,\r\n isMicEnabled: !!participant?.media?.micEnabled,\r\n };\r\n};\r\n"],"mappings":"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","names":["useEffect","useRef","useState","peerId","useState","useEffect","useRef","useEffect","useEffect","useEffect","useState","useState","useEffect","useEffect","useRef","useState","useRef","useState","useEffect"]}
|
|
1
|
+
{"version":3,"sources":["../src/react/useLocalParticipant.tsx","../src/react/MeetingProvider.tsx","../src/config/ws.ts","../src/core/MeetingState.ts","../src/core/VideoCore.ts","../src/react/useMeetingStore.ts","../src/react/useMeeting.ts","../src/react/useParticipants.ts","../src/react/useRemoteMedia.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useLocalParticipant = () => {\r\n const { sdk } = useMeetingContext();\r\n\r\n // Guard the initial state to ensure it matches Participant | null\r\n const [localParticipant, setLocalParticipant] = useState<Participant | null>(\r\n () => {\r\n const current = sdk.state.localParticipant;\r\n return current && current.id ? (current as Participant) : null;\r\n },\r\n );\r\n\r\n useEffect(() => {\r\n const unsubscribe = sdk.state.subscribe(\"localParticipant\", () => {\r\n const current = sdk.state.localParticipant;\r\n\r\n // Safe Type-Guard: Only update if the object has a finalized id\r\n if (current && current.id) {\r\n setLocalParticipant({ ...current } as Participant);\r\n } else {\r\n setLocalParticipant(null);\r\n }\r\n });\r\n\r\n return unsubscribe;\r\n }, [sdk]);\r\n\r\n const lastStreamRef = useRef<MediaStream | null>(null);\r\n\r\n const videoRef = useCallback(\r\n (video: HTMLVideoElement | null) => {\r\n if (!video) return;\r\n\r\n const stream = localParticipant?.media?.stream;\r\n if (!stream) return;\r\n\r\n if (lastStreamRef.current === stream) return;\r\n lastStreamRef.current = stream;\r\n\r\n video.srcObject = stream;\r\n video.autoplay = true;\r\n video.playsInline = true;\r\n\r\n video.play().catch((err) => {\r\n console.warn(`Autoplay failed for local view:`, err);\r\n });\r\n },\r\n [localParticipant?.media?.stream],\r\n );\r\n\r\n return {\r\n participant: localParticipant,\r\n videoRef,\r\n };\r\n};\r\n","import React, { createContext, useContext, useMemo, useRef } from \"react\";\r\nimport { VideoSDKCore } from \"../core/VideoCore\";\r\nimport {\r\n ChatInput,\r\n ChatMessage,\r\n MeetingConfig,\r\n Participant,\r\n PubSubTopic,\r\n} from \"../types/meeting\";\r\nimport { useMeetingStore } from \"./useMeetingStore\";\r\n\r\ntype PubSubHandle = {\r\n messages: ChatMessage[];\r\n publish: (input: ChatInput) => void;\r\n};\r\n\r\ntype MeetingContextValue = {\r\n sdk: VideoSDKCore;\r\n\r\n join: (config: MeetingConfig) => Promise<void>;\r\n leave: () => void;\r\n\r\n toggleMic: () => void;\r\n toggleCam: () => void;\r\n\r\n startScreenShare: () => Promise<MediaStream>;\r\n stopScreenShare: () => void;\r\n\r\n sendMessage: (input: ChatInput) => void;\r\n\r\n meetingId: string | null;\r\n localParticipant: Participant | null;\r\n participants: Map<string, Participant>;\r\n messages: ChatMessage[];\r\n presenterId: string | null;\r\n usePubSub: (topic: PubSubTopic) => PubSubHandle;\r\n onError: (cb: (err: any) => void) => () => void;\r\n};\r\n\r\nconst MeetingContext = createContext<MeetingContextValue | null>(null);\r\n\r\nexport const MeetingProvider = ({\r\n config,\r\n children,\r\n}: {\r\n config: MeetingConfig;\r\n children: React.ReactNode;\r\n}) => {\r\n const sdkRef = useRef<VideoSDKCore | null>(null);\r\n const errorListeners = useRef(new Set<(err: any) => void>());\r\n if (!sdkRef.current) {\r\n sdkRef.current = new VideoSDKCore({\r\n onError: (err) => {\r\n errorListeners.current.forEach((fn) => fn(err));\r\n },\r\n });\r\n }\r\n\r\n const sdk = sdkRef.current;\r\n const presenterId = useMeetingStore(\r\n sdk.state,\r\n \"presenter\",\r\n (s) => s.presenterId,\r\n );\r\n const participants = useMeetingStore(\r\n sdk.state,\r\n \"participants\",\r\n (s) => s.participants,\r\n );\r\n const localParticipant = useMeetingStore(\r\n sdk.state,\r\n \"localParticipant\",\r\n (s) => s.localParticipant,\r\n );\r\n const messages = useMeetingStore(sdk.state, \"chat\", (s) =>\r\n s.getChatMessages(),\r\n );\r\n\r\n const value = useMemo<MeetingContextValue>(() => {\r\n if (!sdkRef.current) {\r\n sdkRef.current = new VideoSDKCore({\r\n onError: (err) => {\r\n errorListeners.current.forEach((fn) => fn(err));\r\n },\r\n });\r\n }\r\n\r\n return {\r\n sdk,\r\n\r\n join: (joinConfig: MeetingConfig) =>\r\n sdk.joinMeeting({\r\n ...config,\r\n ...joinConfig,\r\n }),\r\n leave: () => sdk.disconnect(),\r\n toggleMic: sdk.toggleMic.bind(sdk),\r\n toggleCam: sdk.toggleCam.bind(sdk),\r\n startScreenShare: sdk.startScreenShare.bind(sdk),\r\n stopScreenShare: sdk.stopScreenShare.bind(sdk),\r\n sendMessage: sdk.sendChatMessage.bind(sdk),\r\n\r\n meetingId: sdk.getMeetingId(),\r\n localParticipant,\r\n participants,\r\n messages,\r\n presenterId,\r\n usePubSub: (topic: PubSubTopic) => {\r\n if (topic !== \"SECURE_CHAT\") {\r\n throw new Error(`Unsupported PubSub argument: \"${topic}\"`);\r\n }\r\n return {\r\n messages: sdk.state.getChatMessages(),\r\n publish: sdk.sendChatMessage.bind(sdk),\r\n };\r\n },\r\n onError: (cb: (err: any) => void) => {\r\n errorListeners.current.add(cb);\r\n\r\n return () => {\r\n errorListeners.current.delete(cb);\r\n };\r\n },\r\n };\r\n }, [config, sdk, localParticipant, participants, messages, presenterId]);\r\n\r\n return (\r\n <MeetingContext.Provider value={value}>{children}</MeetingContext.Provider>\r\n );\r\n};\r\n\r\nexport const useMeetingContext = () => {\r\n const ctx = useContext(MeetingContext);\r\n if (!ctx)\r\n throw new Error(\"useMeetingContext must be used inside <MeetingProvider>\");\r\n return ctx;\r\n};\r\n","export const SDK_CONFIG = {\r\n wsUrl: \"wss://rust-video-server-sfyf.onrender.com/ws\",\r\n};\r\n","import {\r\n ChatMessage,\r\n Listener,\r\n Participant,\r\n ParticipantMedia,\r\n StateScope,\r\n} from \"../types/meeting\";\r\n\r\ntype LocalParticipantPatch = {\r\n id?: string;\r\n name?: string;\r\n media?: Partial<ParticipantMedia>;\r\n};\r\n\r\nexport class MeetingState {\r\n participants = new Map<string, Participant>();\r\n localParticipant: Participant | null = null;\r\n localStream: MediaStream | null = null;\r\n chatMessages = new Map<string, ChatMessage>();\r\n presenterId: string | null = null;\r\n\r\n private listeners = new Map<StateScope, Set<Listener>>();\r\n\r\n // ---- reactive system ----\r\n\r\n subscribe(scope: StateScope, fn: Listener): () => void {\r\n if (!this.listeners.has(scope)) {\r\n this.listeners.set(scope, new Set());\r\n }\r\n this.listeners.get(scope)!.add(fn);\r\n\r\n return () => {\r\n this.listeners.get(scope)?.delete(fn);\r\n };\r\n }\r\n\r\n notify(scope: StateScope) {\r\n this.listeners.get(scope)?.forEach((fn) => fn());\r\n }\r\n\r\n setPresenterId(id: string | null) {\r\n if (this.presenterId === id) return;\r\n this.presenterId = id;\r\n this.notify(\"presenter\");\r\n this.notify(\"participants\");\r\n }\r\n\r\n // ---- participants ----\r\n\r\n addParticipant(p: Participant) {\r\n if (this.participants.has(p.id)) return false;\r\n // Fix: Immutable Map update\r\n const next = new Map(this.participants);\r\n next.set(p.id, p);\r\n this.participants = next;\r\n\r\n this.notify(\"participants\");\r\n return true;\r\n }\r\n\r\n removeParticipant(id: string) {\r\n // Fix: Immutable Map update\r\n const next = new Map(this.participants);\r\n next.delete(id);\r\n this.participants = next;\r\n\r\n this.notify(\"participants\");\r\n }\r\n\r\n updateParticipantMedia(\r\n id: string,\r\n patch: Partial<NonNullable<Participant[\"media\"]>>,\r\n ) {\r\n const p = this.participants.get(id);\r\n if (!p) return;\r\n\r\n const updated: Participant = {\r\n ...p,\r\n media: {\r\n stream: null,\r\n screenStream: undefined,\r\n cameraTrack: undefined,\r\n screenTrack: undefined,\r\n audioTrack: undefined,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n ...p.media, // preserve existing media items if they happen to exist\r\n ...patch, // apply the incoming stream updates\r\n },\r\n };\r\n\r\n // Keep your clean immutable map update\r\n const next = new Map(this.participants);\r\n next.set(id, updated);\r\n this.participants = next;\r\n\r\n this.notify(`participant:${id}`);\r\n this.notify(\"participants\");\r\n }\r\n\r\n updateLocalParticipant(patch: LocalParticipantPatch) {\r\n const prev = this.localParticipant;\r\n\r\n if (!prev) {\r\n this.localParticipant = {\r\n id: patch.id ?? \"\",\r\n name: patch.name ?? \"\",\r\n media: {\r\n stream: patch.media?.stream ?? null, // ◄ FIX: Capture the stream from the patch here\r\n screenStream: patch.media?.screenStream,\r\n cameraTrack: patch.media?.cameraTrack,\r\n screenTrack: patch.media?.screenTrack,\r\n audioTrack: patch.media?.audioTrack,\r\n micEnabled: patch.media?.micEnabled ?? true,\r\n camEnabled: patch.media?.camEnabled ?? true,\r\n isScreenSharing: patch.media?.isScreenSharing ?? false,\r\n },\r\n };\r\n\r\n this.notify(\"localParticipant\");\r\n return;\r\n }\r\n\r\n const prevMedia = prev.media ?? {\r\n stream: null,\r\n screenStream: undefined,\r\n cameraTrack: undefined,\r\n screenTrack: undefined,\r\n audioTrack: undefined,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n };\r\n\r\n const nextMedia: ParticipantMedia = {\r\n stream: patch.media?.stream ?? prevMedia.stream,\r\n screenStream: patch.media?.screenStream ?? prevMedia.screenStream,\r\n cameraTrack: patch.media?.cameraTrack ?? prevMedia.cameraTrack,\r\n screenTrack: patch.media?.screenTrack ?? prevMedia.screenTrack,\r\n audioTrack: patch.media?.audioTrack ?? prevMedia.audioTrack,\r\n micEnabled: patch.media?.micEnabled ?? prevMedia.micEnabled,\r\n camEnabled: patch.media?.camEnabled ?? prevMedia.camEnabled,\r\n isScreenSharing:\r\n patch.media?.isScreenSharing ?? prevMedia.isScreenSharing,\r\n };\r\n\r\n this.localParticipant = {\r\n ...prev,\r\n id: patch.id ?? prev.id,\r\n name: patch.name ?? prev.name,\r\n media: nextMedia,\r\n };\r\n\r\n this.notify(\"localParticipant\");\r\n }\r\n\r\n // ---- chat ----\r\n\r\n addChatMessage(msg: ChatMessage) {\r\n this.chatMessages.set(msg.id, msg);\r\n this.notify(\"chat\");\r\n }\r\n\r\n getChatMessages() {\r\n return Array.from(this.chatMessages.values()).sort(\r\n (a, b) => a.timestamp - b.timestamp,\r\n );\r\n }\r\n\r\n clearChat() {\r\n this.chatMessages.clear();\r\n this.notify(\"chat\");\r\n }\r\n\r\n // ---- helpers ----\r\n\r\n getParticipants() {\r\n return Array.from(this.participants.values());\r\n }\r\n\r\n getParticipant(id: string) {\r\n return this.participants.get(id) ?? null;\r\n }\r\n\r\n resetRemoteState() {\r\n this.participants.clear();\r\n this.chatMessages.clear();\r\n this.presenterId = null;\r\n this.notify(\"participants\");\r\n this.notify(\"chat\");\r\n this.notify(\"presenter\");\r\n }\r\n}\r\n","import { SDK_CONFIG } from \"../config/ws\";\r\nimport {\r\n ChatInput,\r\n ChatMessage,\r\n Events,\r\n MeetingConfig,\r\n SDKError,\r\n} from \"../types/meeting\";\r\nimport { MeetingState } from \"./MeetingState\";\r\n\r\nexport class VideoSDKCore {\r\n private ws: WebSocket | null = null;\r\n private peers: Record<string, RTCPeerConnection> = {};\r\n private initiators = new Set<string>();\r\n\r\n private myId: string;\r\n private roomId: string | null = null;\r\n private localStream: MediaStream | null = null;\r\n private screenStream: MediaStream | null = null;\r\n private isScreenSharing = false;\r\n private screenSenders: Record<string, RTCRtpSender[]> = {};\r\n\r\n private pingInterval: any = null;\r\n private pendingIceCandidates: Record<string, RTCIceCandidateInit[]> = {};\r\n private reconnectAttempts = 0;\r\n private reconnectTimer?: number;\r\n private participantName = \"\";\r\n public readonly state: MeetingState;\r\n private joinResolver?: () => void;\r\n private joinRejecter?: (e: any) => void;\r\n private emitError(\r\n code: string,\r\n message: string,\r\n raw?: any,\r\n recoverable = true,\r\n ) {\r\n const err: SDKError = {\r\n code,\r\n message,\r\n raw,\r\n roomId: this.roomId,\r\n userId: this.myId,\r\n recoverable,\r\n };\r\n\r\n this.events.onError?.(err);\r\n\r\n this.joinRejecter?.(err);\r\n this.joinRejecter = undefined;\r\n\r\n console.error(\"[MeetingSDK Error]\", err);\r\n }\r\n\r\n constructor(\r\n private events: Events = {},\r\n private url: string = SDK_CONFIG.wsUrl,\r\n ) {\r\n this.state = new MeetingState();\r\n this.events = events;\r\n this.url = url;\r\n\r\n this.myId = localStorage.getItem(\"vsdk_id\") || crypto.randomUUID();\r\n\r\n localStorage.setItem(\"vsdk_id\", this.myId);\r\n }\r\n\r\n // ---------------- STREAM ----------------\r\n async initLocal(video: HTMLVideoElement, name: string) {\r\n this.participantName = name;\r\n\r\n if (!this.localStream) {\r\n this.localStream = await navigator.mediaDevices.getUserMedia({\r\n video: true,\r\n audio: true,\r\n });\r\n }\r\n\r\n video.srcObject = this.localStream;\r\n\r\n // Fix: Supply mandatory fields to satisfy the Participant type constraint\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n stream: this.localStream,\r\n micEnabled: true,\r\n camEnabled: true,\r\n isScreenSharing: false,\r\n },\r\n });\r\n\r\n this.state.localStream = this.localStream;\r\n }\r\n\r\n // ---------------- CONNECT ----------------\r\n async connect(roomId: string, name: string) {\r\n this.roomId = roomId;\r\n\r\n this.reset();\r\n\r\n return new Promise<void>((resolve, reject) => {\r\n this.joinResolver = resolve;\r\n this.joinRejecter = reject;\r\n this.ws = new WebSocket(this.url);\r\n\r\n this.ws.onopen = () => {\r\n this.send({\r\n type: \"JOIN\",\r\n room_id: roomId,\r\n user_id: this.myId,\r\n sender_name: name,\r\n });\r\n };\r\n\r\n this.ws.onerror = (err) => {\r\n this.emitError(\"WS_ERROR\", \"WebSocket encountered an error\", err, true);\r\n };\r\n\r\n this.ws.onclose = (e) => {\r\n this.emitError(\r\n \"WS_CLOSED\",\r\n `Connection closed (${e.code}) ${e.reason || \"\"}`,\r\n e,\r\n true,\r\n );\r\n\r\n // If join never resolved, fail the promise\r\n this.joinRejecter?.({\r\n code: \"WS_CLOSED\",\r\n message: \"Connection closed before join completed\",\r\n raw: e,\r\n });\r\n\r\n this.joinRejecter = undefined;\r\n\r\n this.scheduleReconnect();\r\n };\r\n\r\n this.ws.onmessage = async (e) => {\r\n await this.handle(JSON.parse(e.data));\r\n };\r\n });\r\n }\r\n\r\n async joinMeeting(config: MeetingConfig) {\r\n const { roomId, name, audioMuted = false, videoMuted = false } = config;\r\n\r\n if (!roomId || !name) {\r\n throw new Error(\"roomId and name are required to join meeting\");\r\n }\r\n\r\n this.participantName = name;\r\n\r\n // Reuse existing stream if initLocal already configured it\r\n if (!this.localStream) {\r\n this.localStream = await navigator.mediaDevices.getUserMedia({\r\n video: true,\r\n audio: true,\r\n });\r\n }\r\n\r\n this.localStream.getAudioTracks().forEach((t) => {\r\n t.enabled = !audioMuted;\r\n });\r\n this.localStream.getVideoTracks().forEach((t) => {\r\n t.enabled = !videoMuted;\r\n });\r\n\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n stream: this.localStream,\r\n micEnabled: !audioMuted,\r\n camEnabled: !videoMuted,\r\n isScreenSharing: false,\r\n },\r\n });\r\n\r\n this.state.localStream = this.localStream;\r\n\r\n await this.connect(roomId, name);\r\n }\r\n\r\n /** Expose the roomId without making it fully public */\r\n getMeetingId(): string | null {\r\n return this.roomId;\r\n }\r\n\r\n toggleMic() {\r\n const mediaState = this.state.localParticipant?.media;\r\n if (!mediaState) return;\r\n\r\n // Flip the state tracked in localParticipant.media\r\n const nextEnabled = !mediaState.micEnabled;\r\n\r\n // Mirror the state change down to the actual hardware tracks\r\n this.localStream\r\n ?.getAudioTracks()\r\n .forEach((t) => (t.enabled = nextEnabled));\r\n\r\n // Update state layer\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n ...mediaState,\r\n micEnabled: nextEnabled,\r\n },\r\n });\r\n\r\n // Notify peers\r\n this.send({\r\n type: \"MEDIA_STATE\",\r\n kind: \"audio\",\r\n enabled: nextEnabled,\r\n });\r\n }\r\n\r\n toggleCam() {\r\n const mediaState = this.state.localParticipant?.media;\r\n if (!mediaState) return;\r\n\r\n // Flip the state tracked in localParticipant.media\r\n const nextEnabled = !mediaState.camEnabled;\r\n\r\n // Mirror the state change down to the actual hardware tracks\r\n this.localStream\r\n ?.getVideoTracks()\r\n .forEach((t) => (t.enabled = nextEnabled));\r\n\r\n // Update state layer\r\n this.state.updateLocalParticipant({\r\n id: this.myId,\r\n name: this.participantName,\r\n media: {\r\n ...mediaState,\r\n camEnabled: nextEnabled,\r\n },\r\n });\r\n\r\n // Notify peers\r\n this.send({\r\n type: \"MEDIA_STATE\",\r\n kind: \"video\",\r\n enabled: nextEnabled,\r\n });\r\n }\r\n\r\n private scheduleReconnect() {\r\n if (!this.roomId) return;\r\n\r\n const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);\r\n\r\n clearTimeout(this.reconnectTimer);\r\n\r\n this.reconnectTimer = window.setTimeout(async () => {\r\n try {\r\n await this.connect(this.roomId!, this.participantName);\r\n\r\n this.reconnectAttempts = 0;\r\n } catch {\r\n this.reconnectAttempts++;\r\n this.scheduleReconnect();\r\n }\r\n }, delay);\r\n }\r\n\r\n private startHeartbeat() {\r\n this.stopHeartbeat();\r\n\r\n this.pingInterval = setInterval(() => {\r\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;\r\n\r\n this.send({\r\n type: \"PING\",\r\n client_ts: Date.now(),\r\n });\r\n }, 20000); // every 20s\r\n }\r\n private stopHeartbeat() {\r\n if (this.pingInterval) {\r\n clearInterval(this.pingInterval);\r\n this.pingInterval = null;\r\n }\r\n }\r\n\r\n // ---------------- RESET ----------------\r\n private reset() {\r\n Object.values(this.peers).forEach((pc) => pc.close());\r\n\r\n this.peers = {};\r\n this.initiators.clear();\r\n this.pendingIceCandidates = {};\r\n\r\n this.state.resetRemoteState();\r\n }\r\n\r\n // ---------------- HANDLE SIGNALS ----------------\r\n private async handle(msg: any) {\r\n if (msg.sender === this.myId) return;\r\n\r\n switch (msg.type) {\r\n case \"EXISTING_USERS\":\r\n if (msg.presenterId) {\r\n this.state.setPresenterId(msg.presenterId);\r\n\r\n // Trigger your event so the UI knows to render the stage\r\n this.events.onScreenShareStarted?.(msg.presenterId, null!);\r\n }\r\n\r\n for (const p of msg.participants || []) {\r\n if (!p?.id || p.id === this.myId) continue;\r\n this.state.addParticipant(p);\r\n this.events.onUserJoined?.(p);\r\n }\r\n break;\r\n\r\n case \"JOINED\": {\r\n this.startHeartbeat();\r\n this.joinResolver?.();\r\n this.joinResolver = undefined;\r\n this.joinRejecter = undefined;\r\n break;\r\n }\r\n case \"USER_JOINED\": {\r\n const p = msg.participant;\r\n\r\n if (!p?.id || p.id === this.myId) return;\r\n\r\n this.state.addParticipant(p);\r\n\r\n this.events.onUserJoined?.(p);\r\n // await this.createOffer(p.id);\r\n\r\n break;\r\n }\r\n\r\n case \"OFFER\":\r\n await this.handleOffer(msg.payload, msg.sender);\r\n break;\r\n\r\n case \"ANSWER\": {\r\n const pc = this.peers[msg.sender];\r\n\r\n if (!pc) return;\r\n\r\n if (pc.signalingState === \"stable\") {\r\n console.warn(\"Late answer received, restarting negotiation\");\r\n return;\r\n }\r\n\r\n await pc.setRemoteDescription({\r\n type: \"answer\",\r\n sdp: msg.payload,\r\n });\r\n\r\n await this.flushIce(msg.sender, pc);\r\n\r\n break;\r\n }\r\n\r\n case \"ICE\": {\r\n const candidate = JSON.parse(msg.payload);\r\n\r\n let pc = this.peers[msg.sender];\r\n\r\n if (!pc) {\r\n this.pendingIceCandidates[msg.sender] ??= [];\r\n this.pendingIceCandidates[msg.sender].push(candidate);\r\n break;\r\n }\r\n\r\n if (!pc.remoteDescription) {\r\n this.pendingIceCandidates[msg.sender] ??= [];\r\n this.pendingIceCandidates[msg.sender].push(candidate);\r\n break;\r\n }\r\n\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (err) {\r\n console.warn(\"ICE error:\", err);\r\n }\r\n\r\n break;\r\n }\r\n\r\n case \"USER_LEFT\":\r\n const peerId = msg.participant.id;\r\n this.closePeer(peerId);\r\n\r\n this.state.removeParticipant(peerId);\r\n\r\n this.events.onUserLeft?.(peerId);\r\n\r\n break;\r\n\r\n case \"MEDIA_STATE_CHANGE\": {\r\n const peerId = msg.peerId;\r\n const { kind, enabled } = msg;\r\n\r\n // 1. Sync the app state layer for UI rendering components\r\n if (kind === \"audio\") {\r\n this.state.updateParticipantMedia(peerId, { micEnabled: enabled });\r\n this.events.onMicToggled?.(peerId, enabled);\r\n } else if (kind === \"video\") {\r\n this.state.updateParticipantMedia(peerId, { camEnabled: enabled });\r\n this.events.onCamToggled?.(peerId, enabled);\r\n }\r\n\r\n break;\r\n }\r\n\r\n case \"CHAT_MESSAGE\": {\r\n const newMsg = msg.data;\r\n\r\n if (newMsg.sender_id === this.myId) break; // already added optimistically\r\n this.state.addChatMessage({\r\n id: newMsg.id,\r\n text: newMsg.message,\r\n sender_id: newMsg.sender_id,\r\n sender_name: newMsg.sender_name,\r\n timestamp: new Date(newMsg.timestamp).getTime(),\r\n target: newMsg.target,\r\n });\r\n\r\n this.events.onChatMessage?.(msg);\r\n break;\r\n }\r\n case \"SCREEN_SHARE_START\": {\r\n const peerId = msg.peerId;\r\n\r\n this.state.updateParticipantMedia(peerId, {\r\n isScreenSharing: true,\r\n remoteScreenStreamId: msg.stream_id,\r\n });\r\n\r\n if (!this.state.presenterId) {\r\n this.state.setPresenterId(peerId);\r\n }\r\n\r\n // Fix: Use screenStream instead of the regular camera stream\r\n const screenStream =\r\n this.state.getParticipant(peerId)?.media?.screenStream;\r\n\r\n this.events.onScreenShareStarted?.(peerId, screenStream || null!);\r\n break;\r\n }\r\n\r\n case \"SCREEN_SHARE_STOP\": {\r\n const peerId = msg.peerId;\r\n this.state.updateParticipantMedia(peerId, { isScreenSharing: false });\r\n if (this.state.presenterId === peerId) {\r\n this.state.setPresenterId(null);\r\n }\r\n this.events.onScreenShareStopped?.(peerId);\r\n break;\r\n }\r\n case \"ERROR\": {\r\n const fatal = msg?.fatal === true;\r\n\r\n this.emitError(\r\n \"WS_ERROR\",\r\n msg?.message || \"Unknown error\",\r\n msg,\r\n !fatal,\r\n );\r\n\r\n if (fatal) {\r\n this.disconnect();\r\n }\r\n\r\n return;\r\n }\r\n }\r\n }\r\n\r\n // ---------------- PEER ----------------\r\n private createPeer(id: string) {\r\n if (!this.localStream) throw new Error(\"No local stream\");\r\n console.log(\r\n \"Adding tracks\",\r\n this.localStream.getTracks().map((t) => ({\r\n kind: t.kind,\r\n enabled: t.enabled,\r\n state: t.readyState,\r\n })),\r\n );\r\n\r\n const pc = new RTCPeerConnection({\r\n iceServers: [\r\n {\r\n urls: [\r\n \"stun:stun.l.google.com:19302\",\r\n \"stun:stun1.l.google.com:19302\",\r\n ],\r\n },\r\n ],\r\n });\r\n\r\n pc.ontrack = (event) => {\r\n const incomingStream = event.streams[0];\r\n const participant = this.state.getParticipant(id);\r\n\r\n const isScreenStream =\r\n incomingStream.id === participant?.media?.remoteScreenStreamId;\r\n\r\n if (isScreenStream) {\r\n const videoTrack =\r\n event.track.kind === \"video\"\r\n ? event.track\r\n : incomingStream.getVideoTracks()[0] ||\r\n participant?.media?.screenTrack;\r\n\r\n this.state.updateParticipantMedia(id, {\r\n screenStream: incomingStream,\r\n screenTrack: videoTrack,\r\n\r\n isScreenSharing: true,\r\n });\r\n\r\n if (!this.state.presenterId) {\r\n this.state.setPresenterId(id);\r\n }\r\n\r\n this.events.onScreenShareStarted?.(id, incomingStream);\r\n } else {\r\n this.state.updateParticipantMedia(id, {\r\n stream: incomingStream,\r\n cameraTrack: incomingStream.getVideoTracks()[0],\r\n audioTrack: incomingStream.getAudioTracks()[0],\r\n });\r\n this.events.onTrack?.(incomingStream, id);\r\n }\r\n };\r\n\r\n pc.onicecandidate = (e) => {\r\n if (!e.candidate) return;\r\n this.send({\r\n type: \"ICE\",\r\n payload: JSON.stringify(e.candidate),\r\n sender: this.myId,\r\n target: id,\r\n });\r\n };\r\n\r\n pc.onconnectionstatechange = () => {\r\n if (pc.connectionState === \"failed\") {\r\n try {\r\n pc.restartIce();\r\n } catch {}\r\n }\r\n };\r\n\r\n this.localStream.getTracks().forEach((track) => {\r\n pc.addTrack(track, this.localStream!);\r\n });\r\n\r\n if (this.isScreenSharing && this.screenStream) {\r\n this.screenSenders[id] = [];\r\n this.screenStream.getTracks().forEach((track) => {\r\n const sender = pc.addTrack(track, this.screenStream!);\r\n this.screenSenders[id].push(sender);\r\n });\r\n }\r\n\r\n return pc;\r\n }\r\n\r\n // ---------------- OFFER ----------------\r\n private async createOffer(id: string, isRenegotiation = false) {\r\n if (!this.peers[id]) {\r\n this.peers[id] = this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n const offer = await pc.createOffer();\r\n await pc.setLocalDescription(offer);\r\n\r\n this.send({\r\n type: \"OFFER\",\r\n payload: offer.sdp,\r\n sender: this.myId,\r\n target: id,\r\n });\r\n }\r\n\r\n // ---------------- ANSWER ----------------\r\n private async handleOffer(sdp: string, id: string) {\r\n if (!this.peers[id]) {\r\n this.peers[id] = this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n await pc.setRemoteDescription({\r\n type: \"offer\",\r\n sdp,\r\n });\r\n\r\n const pending = this.pendingIceCandidates[id] || [];\r\n\r\n for (const candidate of pending) {\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (err) {\r\n console.warn(err);\r\n }\r\n }\r\n\r\n delete this.pendingIceCandidates[id];\r\n\r\n const answer = await pc.createAnswer();\r\n\r\n await pc.setLocalDescription(answer);\r\n await this.flushIce(id, pc);\r\n\r\n this.send({\r\n type: \"ANSWER\",\r\n payload: answer.sdp,\r\n sender: this.myId,\r\n target: id,\r\n });\r\n }\r\n\r\n // ---------------- CLEANUP ----------------\r\n private closePeer(id: string) {\r\n const pc = this.peers[id];\r\n\r\n if (!pc) return;\r\n\r\n pc.ontrack = null;\r\n pc.onicecandidate = null;\r\n pc.onconnectionstatechange = null;\r\n\r\n pc.close();\r\n\r\n delete this.peers[id];\r\n\r\n this.initiators.delete(id);\r\n\r\n this.state.removeParticipant(id);\r\n }\r\n\r\n async startScreenShare() {\r\n try {\r\n if (this.state.presenterId && this.state.presenterId !== this.myId) {\r\n throw new Error(\"Another user is already sharing their screen.\");\r\n }\r\n if (!navigator.mediaDevices?.getDisplayMedia) {\r\n throw new Error(\"Screen sharing not supported on this device\");\r\n }\r\n\r\n this.screenStream = await navigator.mediaDevices.getDisplayMedia({\r\n video: true,\r\n // audio: true,\r\n });\r\n\r\n this.isScreenSharing = true;\r\n\r\n this.state.updateLocalParticipant({\r\n media: {\r\n isScreenSharing: true,\r\n screenStream: this.screenStream,\r\n screenTrack: this.screenStream.getVideoTracks()[0],\r\n },\r\n });\r\n\r\n this.state.setPresenterId(this.myId);\r\n // Handle the user clicking browser's built-in \"Stop Sharing\" button\r\n this.screenStream.getVideoTracks()[0].onended = () => {\r\n this.stopScreenShare();\r\n };\r\n\r\n Object.entries(this.peers).forEach(([peerId, pc]) => {\r\n this.screenSenders[peerId] = [];\r\n this.screenStream!.getTracks().forEach((track) => {\r\n const sender = pc.addTrack(track, this.screenStream!);\r\n this.screenSenders[peerId].push(sender);\r\n });\r\n\r\n // Renegotiate peer connection descriptors to notify remote side of new track footprint\r\n this.createOffer(peerId, true);\r\n });\r\n\r\n this.send({\r\n type: \"SCREEN_SHARE_START\",\r\n sender: this.myId,\r\n room_id: this.roomId,\r\n stream_id: this.screenStream.id.replace(/[{}]/g, \"\"),\r\n });\r\n\r\n return this.screenStream;\r\n } catch (err: any) {\r\n this.emitError(\r\n \"SCREEN_SHARE_FAILED\",\r\n err?.message || \"Failed to start screen sharing\",\r\n err,\r\n true,\r\n );\r\n\r\n this.isScreenSharing = false;\r\n this.screenStream = null;\r\n throw err;\r\n }\r\n }\r\n\r\n stopScreenShare() {\r\n if (!this.screenStream) return;\r\n\r\n this.screenStream.getTracks().forEach((t) => t.stop());\r\n\r\n // Remove tracks cleanly from WebRTC channel pathways across your peers\r\n Object.entries(this.peers).forEach(([peerId, pc]) => {\r\n const senders = this.screenSenders[peerId] || [];\r\n senders.forEach((sender) => {\r\n try {\r\n pc.removeTrack(sender);\r\n } catch (err) {\r\n console.warn(err);\r\n }\r\n });\r\n delete this.screenSenders[peerId];\r\n\r\n // Renegotiate layout expectations to scale down stream bounds\r\n this.createOffer(peerId, true);\r\n });\r\n\r\n this.screenStream = null;\r\n this.isScreenSharing = false;\r\n\r\n this.state.updateLocalParticipant({\r\n media: {\r\n isScreenSharing: false,\r\n screenStream: null,\r\n screenTrack: undefined,\r\n },\r\n });\r\n\r\n if (this.state.presenterId === this.myId) {\r\n this.state.setPresenterId(null);\r\n }\r\n\r\n this.send({\r\n type: \"SCREEN_SHARE_STOP\",\r\n sender: this.myId,\r\n room_id: this.roomId,\r\n });\r\n }\r\n\r\n sendChatMessage(payload: ChatInput) {\r\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\r\n console.warn(\"WS not connected\");\r\n return;\r\n }\r\n\r\n if (!this.roomId) {\r\n console.warn(\"No roomId set\");\r\n return;\r\n }\r\n\r\n const isPrivate = !!payload?.target;\r\n\r\n const senderName = this.state.localParticipant?.name || \"Anonymous\";\r\n\r\n const msg: ChatMessage = {\r\n id: crypto.randomUUID(),\r\n sender_id: this.myId,\r\n sender_name: senderName,\r\n text: payload.message.trim(),\r\n timestamp: Date.now(),\r\n reply_to: payload.reply_to ?? null,\r\n target: payload.target ?? null,\r\n };\r\n\r\n // optimistic UI update\r\n this.state.addChatMessage(msg);\r\n\r\n // send protocol payload (clean + consistent)\r\n this.send({\r\n type: \"CHAT_MESSAGE\",\r\n message: payload.message.trim(),\r\n user_id: this.myId,\r\n sender_name: senderName,\r\n room_id: this.roomId,\r\n target: isPrivate ? (payload.target ?? null) : null,\r\n reply_to: payload.reply_to ?? null,\r\n\r\n client_ts: Date.now(),\r\n });\r\n }\r\n\r\n disconnect() {\r\n this.stopScreenShare();\r\n\r\n Object.values(this.peers).forEach((pc) => pc.close());\r\n this.peers = {};\r\n this.initiators.clear();\r\n\r\n this.stopHeartbeat();\r\n\r\n if (this.ws) {\r\n this.ws.close();\r\n this.ws = null;\r\n }\r\n\r\n if (this.localStream) {\r\n this.localStream.getTracks().forEach((track) => track.stop());\r\n this.localStream = null;\r\n }\r\n\r\n this.roomId = null;\r\n\r\n // Clear and notify\r\n this.state.localParticipant = null;\r\n this.state.notify(\"localParticipant\");\r\n\r\n this.state.participants.clear();\r\n this.state.notify(\"participants\");\r\n\r\n this.state.clearChat();\r\n this.state.setPresenterId(null);\r\n }\r\n\r\n private async flushIce(id: string, pc: RTCPeerConnection) {\r\n const pending = this.pendingIceCandidates[id];\r\n if (!pending?.length) return;\r\n\r\n for (const candidate of pending) {\r\n try {\r\n await pc.addIceCandidate(candidate);\r\n } catch (e) {\r\n console.warn(\"ICE flush error\", e);\r\n }\r\n }\r\n\r\n delete this.pendingIceCandidates[id];\r\n }\r\n\r\n private send(msg: any) {\r\n this.ws?.send(JSON.stringify(msg));\r\n }\r\n}\r\n","import { useEffect, useState } from \"react\";\r\nimport { StateScope } from \"../types/meeting\";\r\nimport { MeetingState } from \"../core/MeetingState\";\r\n\r\nexport function useMeetingStore<T>(\r\n stateManager: MeetingState,\r\n scope: StateScope,\r\n selector: (state: MeetingState) => T,\r\n): T {\r\n const [state, setState] = useState(() => selector(stateManager));\r\n\r\n useEffect(() => {\r\n // Update local react state whenever the SDK notifies this scope\r\n const unsubscribe = stateManager.subscribe(scope, () => {\r\n setState(selector(stateManager));\r\n });\r\n\r\n return unsubscribe;\r\n }, [stateManager, scope, selector]);\r\n\r\n return state;\r\n}\r\n","import { useEffect } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\n\r\nexport const useMeeting = (handlers?: { onError?: (err: any) => void }) => {\r\n const ctx = useMeetingContext();\r\n\r\n useEffect(() => {\r\n if (!handlers?.onError) return;\r\n\r\n const unsubscribe = ctx.onError(handlers.onError);\r\n return unsubscribe;\r\n }, [handlers?.onError]);\r\n\r\n return ctx;\r\n};\r\n","import { useEffect, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useParticipants = () => {\r\n const { sdk } = useMeetingContext();\r\n\r\n const [participants, setParticipants] = useState<Participant[]>(() =>\r\n sdk.state.getParticipants(),\r\n );\r\n\r\n useEffect(() => {\r\n const update = () => {\r\n setParticipants(sdk.state.getParticipants());\r\n };\r\n\r\n update();\r\n\r\n const unsub = sdk.state.subscribe(\"participants\", update);\r\n\r\n return unsub;\r\n }, [sdk]);\r\n\r\n return participants;\r\n};\r\n","import { useCallback, useEffect, useRef, useState } from \"react\";\r\nimport { useMeetingContext } from \"./MeetingProvider\";\r\nimport { Participant } from \"../types/meeting\";\r\n\r\nexport const useRemoteMedia = (participantId: string) => {\r\n const { sdk } = useMeetingContext();\r\n const [participant, setParticipant] = useState<Participant | null>(\r\n () => sdk.state.getParticipant(participantId) || null,\r\n );\r\n\r\n const buildMediaStream = (participant: Participant | null) => {\r\n if (!participant?.media) return null;\r\n\r\n const stream = new MediaStream();\r\n\r\n const videoTrack = participant.media.stream?.getVideoTracks?.()?.[0];\r\n const audioTrack = participant.media.stream?.getAudioTracks?.()?.[0];\r\n\r\n if (videoTrack) stream.addTrack(videoTrack);\r\n if (audioTrack) stream.addTrack(audioTrack);\r\n\r\n return stream;\r\n };\r\n\r\n useEffect(() => {\r\n const unsub = sdk.state.subscribe(`participant:${participantId}`, () => {\r\n const p = sdk.state.getParticipant(participantId);\r\n setParticipant(p ? { ...p } : null);\r\n });\r\n return unsub;\r\n }, [participantId, sdk]);\r\n\r\n // Video Callback Ref\r\n const videoRef = useCallback(\r\n (node: HTMLVideoElement | null) => {\r\n if (!node) return;\r\n\r\n const stream = participant?.media?.stream;\r\n if (!stream) return;\r\n\r\n node.pause();\r\n\r\n // IMPORTANT: always force rebind (no conditions)\r\n node.srcObject = stream;\r\n\r\n node.muted = true;\r\n node.playsInline = true;\r\n node.autoplay = true;\r\n\r\n node.play().catch(() => {\r\n // ignore autoplay restrictions\r\n });\r\n },\r\n [participant?.media?.stream],\r\n );\r\n\r\n // Audio Callback Ref\r\n const audioRef = useCallback(\r\n (node: HTMLAudioElement | null) => {\r\n if (!node) return;\r\n\r\n const stream = participant?.media?.stream;\r\n if (!stream) return;\r\n\r\n node.pause();\r\n node.srcObject = stream;\r\n\r\n node.muted = false;\r\n\r\n node.play().catch(() => {});\r\n },\r\n [participant?.media?.stream],\r\n );\r\n\r\n return {\r\n videoRef,\r\n audioRef,\r\n isCamActive: !!participant?.media?.camEnabled,\r\n isMicEnabled: !!participant?.media?.micEnabled,\r\n };\r\n};\r\n"],"mappings":"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","names":["useEffect","useRef","useState","peerId","useState","useEffect","useRef","useEffect","useEffect","useEffect","useState","useState","useEffect","useCallback","useEffect","useState","useState","participant","useEffect","useCallback"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@afosecure/meetingsdk",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "A modern, lightweight React SDK for building peer-to-peer video communication applications. Built on WebRTC with a clean, composable rust API.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|