@afosecure/meetingsdk 1.3.1 → 1.3.2

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/dist/index.d.mts CHANGED
@@ -159,15 +159,6 @@ declare class VideoSDKCore {
159
159
  private reset;
160
160
  private handleJoinApproved;
161
161
  private handle;
162
- /**
163
- * Create a peer connection with pre-established transceiver layout:
164
- * - Audio transceiver (sendrecv)
165
- * - Camera video transceiver (sendrecv)
166
- * - Screen video transceiver (initially recvonly, becomes sendrecv when sharing)
167
- *
168
- * This fixed layout ensures late joiners get the screen transceiver m-line
169
- * negotiated from the very first offer, even if no one is sharing yet.
170
- */
171
162
  private createPeer;
172
163
  /**
173
164
  * Capture the screen transceiver's MID after SDP negotiation completes.
@@ -184,9 +175,12 @@ declare class VideoSDKCore {
184
175
  * Just swap the track and update direction if needed.
185
176
  */
186
177
  startScreenShare(): Promise<MediaStream>;
178
+ /**
179
+ * Stop screen sharing: clear the screen transceiver track and flip direction back to recvonly.
180
+ */
187
181
  stopScreenShare(): Promise<void>;
188
182
  sendChatMessage(payload: ChatInput): void;
189
- disconnect(): void;
183
+ disconnect(): Promise<void>;
190
184
  private flushIce;
191
185
  private send;
192
186
  approveJoinRequest(requestId: string): void;
package/dist/index.d.ts CHANGED
@@ -159,15 +159,6 @@ declare class VideoSDKCore {
159
159
  private reset;
160
160
  private handleJoinApproved;
161
161
  private handle;
162
- /**
163
- * Create a peer connection with pre-established transceiver layout:
164
- * - Audio transceiver (sendrecv)
165
- * - Camera video transceiver (sendrecv)
166
- * - Screen video transceiver (initially recvonly, becomes sendrecv when sharing)
167
- *
168
- * This fixed layout ensures late joiners get the screen transceiver m-line
169
- * negotiated from the very first offer, even if no one is sharing yet.
170
- */
171
162
  private createPeer;
172
163
  /**
173
164
  * Capture the screen transceiver's MID after SDP negotiation completes.
@@ -184,9 +175,12 @@ declare class VideoSDKCore {
184
175
  * Just swap the track and update direction if needed.
185
176
  */
186
177
  startScreenShare(): Promise<MediaStream>;
178
+ /**
179
+ * Stop screen sharing: clear the screen transceiver track and flip direction back to recvonly.
180
+ */
187
181
  stopScreenShare(): Promise<void>;
188
182
  sendChatMessage(payload: ChatInput): void;
189
- disconnect(): void;
183
+ disconnect(): Promise<void>;
190
184
  private flushIce;
191
185
  private send;
192
186
  approveJoinRequest(requestId: string): void;
package/dist/index.js CHANGED
@@ -684,16 +684,6 @@ var VideoSDKCore = class {
684
684
  }
685
685
  }
686
686
  }
687
- // PEER
688
- /**
689
- * Create a peer connection with pre-established transceiver layout:
690
- * - Audio transceiver (sendrecv)
691
- * - Camera video transceiver (sendrecv)
692
- * - Screen video transceiver (initially recvonly, becomes sendrecv when sharing)
693
- *
694
- * This fixed layout ensures late joiners get the screen transceiver m-line
695
- * negotiated from the very first offer, even if no one is sharing yet.
696
- */
697
687
  async createPeer(id) {
698
688
  if (!this.localStream) throw new Error("No local stream");
699
689
  if (!this.iceServers || this.iceServers.length === 0) {
@@ -745,7 +735,7 @@ var VideoSDKCore = class {
745
735
  };
746
736
  pc.ontrack = (event) => {
747
737
  const transceiver = event.transceiver;
748
- const isScreenTrack = transceiver.mid === this.peerTransceivers[id]?.screenMid;
738
+ const isScreenTrack = transceiver === this.peerTransceivers[id]?.screenTransceiver;
749
739
  console.log(
750
740
  `[ontrack] ${id}: kind=${event.track.kind}, mid=${transceiver.mid}, isScreen=${isScreenTrack}`
751
741
  );
@@ -1025,6 +1015,9 @@ var VideoSDKCore = class {
1025
1015
  throw err;
1026
1016
  }
1027
1017
  }
1018
+ /**
1019
+ * Stop screen sharing: clear the screen transceiver track and flip direction back to recvonly.
1020
+ */
1028
1021
  async stopScreenShare() {
1029
1022
  if (!this.screenStream) return;
1030
1023
  console.log("[Screen Share] Stopping...");
@@ -1101,9 +1094,9 @@ var VideoSDKCore = class {
1101
1094
  });
1102
1095
  }
1103
1096
  // DISCONNECT
1104
- disconnect() {
1097
+ async disconnect() {
1105
1098
  this.intentionalDisconnect = true;
1106
- this.stopScreenShare();
1099
+ await this.stopScreenShare();
1107
1100
  Object.values(this.peers).forEach((pc) => pc.close());
1108
1101
  this.peers = {};
1109
1102
  this.peerTransceivers = {};
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\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 room: { id: string | null; name: 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 approveJoinRequest: (requestId: string) => void;\r\n rejectJoinRequest: (requestId: string) => void;\r\n onError: (cb: (err: any) => void) => () => void;\r\n onEntryRequested: (cb: (req: any) => void) => () => void;\r\n onEntryResponded: (cb: (payload: any, decision?: any) => void) => () => void;\r\n onMeetingLeft: (cb: () => 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 const entryRequestListeners = useRef(new Set<(req: any) => void>());\r\n const entryResponseListeners = useRef(\r\n new Set<(payload: any, decision?: any) => void>(),\r\n );\r\n const meetingLeftListeners = useRef(new Set<() => void>());\r\n\r\n if (!sdkRef.current) {\r\n sdkRef.current = new VideoSDKCore({\r\n onError: (err) => errorListeners.current.forEach((fn) => fn(err)),\r\n onEntryRequested: (req) =>\r\n entryRequestListeners.current.forEach((fn) => fn(req)),\r\n onEntryResponded: (p, d) =>\r\n entryResponseListeners.current.forEach((fn) => fn(p, d)),\r\n onMeetingLeft: () => meetingLeftListeners.current.forEach((fn) => fn()),\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 room: sdk.getMeeting(),\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 approveJoinRequest: sdk.approveJoinRequest.bind(sdk),\r\n rejectJoinRequest: sdk.rejectJoinRequest.bind(sdk),\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 onEntryRequested: (cb: (err: any) => void) => {\r\n entryRequestListeners.current.add(cb);\r\n\r\n return () => {\r\n entryRequestListeners.current.delete(cb);\r\n };\r\n },\r\n onEntryResponded: (cb: (err: any) => void) => {\r\n entryResponseListeners.current.add(cb);\r\n\r\n return () => {\r\n entryResponseListeners.current.delete(cb);\r\n };\r\n },\r\n onMeetingLeft: (cb: () => void) => {\r\n meetingLeftListeners.current.add(cb);\r\n return () => {\r\n meetingLeftListeners.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 private lastPong = Date.now();\r\n private iceServers: RTCIceServer[] = [];\r\n private intentionalDisconnect = false;\r\n\r\n private myId: string;\r\n private room: { id: string | null; name: string | null } = {\r\n id: null,\r\n name: null,\r\n };\r\n private localStream: MediaStream | null = null;\r\n private screenStream: MediaStream | null = null;\r\n private isScreenSharing = false;\r\n\r\n // Transceiver-based tracking: maps peerId -> { cameraTransceiver, screenTransceiver, screenMid }\r\n private peerTransceivers: Record<\r\n string,\r\n {\r\n cameraTransceiver: RTCRtpTransceiver;\r\n screenTransceiver: RTCRtpTransceiver;\r\n screenMid: string | null; // set after negotiation completes\r\n }\r\n > = {};\r\n\r\n private pingInterval: any = null;\r\n private pendingIceCandidates: Record<string, RTCIceCandidateInit[]> = {};\r\n private pendingOffers: Record<string, string> = {};\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\r\n // Track if we're in the waiting room (pending approval)\r\n private isWaitingForApproval = false;\r\n private pendingRequestId: string | null = null;\r\n\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.room.id,\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 try {\r\n this.localStream = await navigator.mediaDevices.getUserMedia({\r\n video: {\r\n width: { ideal: 1280 },\r\n height: { ideal: 720 },\r\n },\r\n audio: {\r\n echoCancellation: true,\r\n noiseSuppression: true,\r\n autoGainControl: true,\r\n },\r\n });\r\n\r\n // Verify tracks are actually live BEFORE connecting\r\n const hasVideo = this.localStream\r\n .getVideoTracks()\r\n .some((t) => t.readyState === \"live\");\r\n const hasAudio = this.localStream\r\n .getAudioTracks()\r\n .some((t) => t.readyState === \"live\");\r\n\r\n if (!hasVideo || !hasAudio) {\r\n throw new Error(`Missing tracks: video=${hasVideo}, audio=${hasAudio}`);\r\n }\r\n\r\n video.srcObject = this.localStream;\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 } catch (err: any) {\r\n this.emitError(\"GET_USER_MEDIA_FAILED\", err?.message, err, false);\r\n throw err;\r\n }\r\n }\r\n\r\n // CONNECT\r\n async connect(roomId: string, name: string) {\r\n this.room.id = 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 console.log(\"WebSocket connected, sending JOIN...\");\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.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 if (this.intentionalDisconnect) {\r\n return; // do NOT reconnect\r\n }\r\n\r\n if (e.code === 1000 || e.code === 1001) {\r\n return;\r\n }\r\n\r\n // Don't auto-reconnect if waiting for approval (user must reconnect after approval)\r\n if (this.isWaitingForApproval) {\r\n console.log(\"Waiting for approval, not auto-reconnecting\");\r\n return;\r\n }\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 getMeeting(): { id: string | null; name: string | null } {\r\n return this.room;\r\n }\r\n\r\n toggleMic() {\r\n const mediaState = this.state.localParticipant?.media;\r\n if (!mediaState) return;\r\n\r\n const nextEnabled = !mediaState.micEnabled;\r\n\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 const nextEnabled = !mediaState.camEnabled;\r\n\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.room.id) 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.room.id!, 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\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.peerTransceivers = {};\r\n this.initiators.clear();\r\n this.pendingIceCandidates = {};\r\n\r\n this.state.resetRemoteState();\r\n }\r\n\r\n private async handleJoinApproved(msg: any) {\r\n console.log(\"JOIN_APPROVED received, sending new JOIN...\");\r\n\r\n this.events.onEntryResponded?.({\r\n participantId: msg.user_id,\r\n decision: \"approved\",\r\n });\r\n\r\n this.isWaitingForApproval = false;\r\n this.pendingRequestId = null;\r\n\r\n // Send JOIN again on SAME socket\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.send({\r\n type: \"JOIN\",\r\n room_id: this.room.id,\r\n user_id: this.myId,\r\n sender_name: this.participantName,\r\n });\r\n console.log(\"Sent new JOIN after approval\");\r\n }\r\n }\r\n\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 \"PONG\":\r\n this.lastPong = Date.now();\r\n break;\r\n\r\n case \"OFFER\":\r\n console.log(\"[Offer] Received from\", msg.sender, {\r\n sdp: msg.payload.substring(0, 200),\r\n });\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 console.log(\"[Answer] Received from\", msg.sender, {\r\n signalingState: pc?.signalingState,\r\n iceConnectionState: pc?.iceConnectionState,\r\n connectionState: pc?.connectionState,\r\n });\r\n\r\n if (!pc) return;\r\n\r\n if (pc.signalingState !== \"have-local-offer\") {\r\n console.warn(\r\n `[Signaling] Unexpected ANSWER in state \"${pc.signalingState}\", ignoring`,\r\n );\r\n return;\r\n }\r\n\r\n try {\r\n await pc.setRemoteDescription({\r\n type: \"answer\",\r\n sdp: msg.payload,\r\n });\r\n\r\n // Capture screenMid after remote description is set\r\n this.captureScreenMid(msg.sender);\r\n\r\n await this.flushIce(msg.sender, pc);\r\n } catch (err) {\r\n console.error(\"[Signaling] Failed to apply answer:\", err);\r\n this.emitError(\r\n \"ANSWER_FAILED\",\r\n `Failed to apply answer from ${msg.sender}`,\r\n err,\r\n true,\r\n );\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 \"EXISTING_USERS\":\r\n if (msg.presenterId) {\r\n this.state.setPresenterId(msg.presenterId);\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 if (p.isScreenSharing && p.remoteScreenStreamId) {\r\n console.log(\r\n `[Existing Users] ${p.name} is sharing screen (stream: ${p.remoteScreenStreamId})`,\r\n );\r\n\r\n this.state.setPresenterId(p.id);\r\n this.state.updateParticipantMedia(p.id, {\r\n isScreenSharing: true,\r\n remoteScreenStreamId: p.remoteScreenStreamId,\r\n });\r\n console.log(\r\n `[EXISTING_USERS] Pre-seeded screen state for ${p.id}, waiting for ontrack`,\r\n );\r\n }\r\n\r\n if (this.shouldInitiate(p.id)) {\r\n await this.createOffer(p.id);\r\n }\r\n }\r\n break;\r\n\r\n case \"JOINED\": {\r\n if (msg.iceServers) {\r\n this.iceServers = msg.iceServers;\r\n }\r\n this.room.name = msg.room_name;\r\n\r\n this.isWaitingForApproval = false;\r\n this.pendingRequestId = null;\r\n this.intentionalDisconnect = false;\r\n this.reconnectAttempts = 0;\r\n\r\n // Process any OFFERs that arrived before JOINED\r\n if (Object.keys(this.pendingOffers).length > 0) {\r\n console.log(\r\n \"Processing\",\r\n Object.keys(this.pendingOffers).length,\r\n \"pending offers\",\r\n );\r\n for (const [peerId, sdp] of Object.entries(this.pendingOffers)) {\r\n console.log(\"Handling queued offer from\", peerId);\r\n await this.handleOffer(sdp, peerId);\r\n }\r\n this.pendingOffers = {};\r\n }\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\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 this.events.onUserJoined?.(p);\r\n\r\n if (this.shouldInitiate(p.id)) {\r\n await this.createOffer(p.id);\r\n }\r\n\r\n break;\r\n }\r\n\r\n case \"JOIN_PENDING\": {\r\n const req = msg.request;\r\n\r\n console.log(\"JOIN_PENDING - waiting for host approval\");\r\n this.isWaitingForApproval = true;\r\n this.pendingRequestId = req.request_id;\r\n\r\n this.events.onEntryRequested?.({\r\n requestId: req.request_id,\r\n userId: req.user_id,\r\n name: req.name,\r\n });\r\n\r\n break;\r\n }\r\n\r\n case \"JOIN_REQUEST\": {\r\n const req = msg.request;\r\n\r\n console.log(\"JOIN_REQUEST - show to host for approval\");\r\n\r\n this.events.onEntryRequested?.({\r\n requestId: req.id,\r\n userId: req.user_id,\r\n name: req.name,\r\n });\r\n\r\n break;\r\n }\r\n\r\n case \"JOIN_APPROVED\": {\r\n await this.handleJoinApproved(msg);\r\n break;\r\n }\r\n\r\n case \"JOIN_REJECTED\": {\r\n const decision = \"rejected\";\r\n\r\n console.log(\"JOIN_REJECTED - user not allowed to join\");\r\n this.isWaitingForApproval = false;\r\n this.pendingRequestId = null;\r\n\r\n this.events.onEntryResponded?.({\r\n participantId: msg.user_id,\r\n decision,\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 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;\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\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 // Screen stream will arrive via ontrack on the screen transceiver\r\n this.events.onScreenShareStarted?.(peerId, 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\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 /**\r\n * Create a peer connection with pre-established transceiver layout:\r\n * - Audio transceiver (sendrecv)\r\n * - Camera video transceiver (sendrecv)\r\n * - Screen video transceiver (initially recvonly, becomes sendrecv when sharing)\r\n *\r\n * This fixed layout ensures late joiners get the screen transceiver m-line\r\n * negotiated from the very first offer, even if no one is sharing yet.\r\n */\r\n private async createPeer(id: string) {\r\n if (!this.localStream) throw new Error(\"No local stream\");\r\n if (!this.iceServers || this.iceServers.length === 0) {\r\n throw new Error(\r\n \"ICE Servers not configured. Backend must provide iceServers on JOIN.\",\r\n );\r\n }\r\n\r\n console.log(\r\n \"Creating peer connection for\",\r\n id,\r\n \"with 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: this.iceServers,\r\n });\r\n\r\n // ===== AUDIO TRANSCEIVER =====\r\n const audioTransceiver = pc.addTransceiver(\"audio\", {\r\n direction: \"sendrecv\",\r\n });\r\n const audioTrack = this.localStream.getAudioTracks()[0];\r\n if (audioTrack) {\r\n await audioTransceiver.sender.replaceTrack(audioTrack);\r\n }\r\n\r\n // ===== CAMERA VIDEO TRANSCEIVER =====\r\n const cameraTransceiver = pc.addTransceiver(\"video\", {\r\n direction: \"sendrecv\",\r\n });\r\n const videoTrack = this.localStream.getVideoTracks()[0];\r\n if (videoTrack) {\r\n await cameraTransceiver.sender.replaceTrack(videoTrack);\r\n }\r\n\r\n // ===== SCREEN VIDEO TRANSCEIVER (initially recvonly) =====\r\n // This will be present in the SDP even if we're not currently sharing.\r\n // When we start sharing, we flip direction to \"sendrecv\" and replaceTrack.\r\n const screenTransceiver = pc.addTransceiver(\"video\", {\r\n direction: this.isScreenSharing ? \"sendrecv\" : \"recvonly\",\r\n });\r\n\r\n if (this.isScreenSharing && this.screenStream) {\r\n const screenTrack = this.screenStream.getVideoTracks()[0];\r\n if (screenTrack) {\r\n await screenTransceiver.sender.replaceTrack(screenTrack);\r\n }\r\n }\r\n\r\n // Store transceiver info for later use\r\n this.peerTransceivers[id] = {\r\n cameraTransceiver,\r\n screenTransceiver,\r\n screenMid: null, // will be populated after negotiation\r\n };\r\n\r\n // ===== TRACK HANDLER =====\r\n pc.ontrack = (event) => {\r\n const transceiver = event.transceiver;\r\n const isScreenTrack =\r\n transceiver.mid === this.peerTransceivers[id]?.screenMid;\r\n\r\n console.log(\r\n `[ontrack] ${id}: kind=${event.track.kind}, mid=${transceiver.mid}, isScreen=${isScreenTrack}`,\r\n );\r\n\r\n const incomingStream =\r\n event.streams?.[0] || new MediaStream([event.track]);\r\n\r\n // Handle track unmuting\r\n if (event.track.muted) {\r\n event.track.onunmute = () => {\r\n console.log(`[ontrack] ${event.track.kind} track unmuted for ${id}`);\r\n };\r\n }\r\n\r\n if (isScreenTrack) {\r\n // Screen share stream\r\n const videoTrack =\r\n event.track.kind === \"video\"\r\n ? event.track\r\n : incomingStream.getVideoTracks()[0];\r\n\r\n this.state.updateParticipantMedia(id, {\r\n screenStream: incomingStream,\r\n screenTrack: videoTrack,\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 // Camera/normal stream\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.oniceconnectionstatechange = () => {\r\n console.log(`[ICE Connection] ${id}: ${pc.iceConnectionState}`);\r\n };\r\n\r\n pc.onconnectionstatechange = () => {\r\n console.log(`[Connection] ${id}: ${pc.connectionState}`);\r\n if (pc.connectionState === \"failed\") {\r\n try {\r\n pc.restartIce();\r\n } catch (e) {\r\n console.warn(\"Failed to restart ICE:\", e);\r\n }\r\n }\r\n };\r\n\r\n return pc;\r\n }\r\n\r\n /**\r\n * Capture the screen transceiver's MID after SDP negotiation completes.\r\n * The MID is assigned during negotiation and is stable for the life of the connection.\r\n */\r\n private captureScreenMid(peerId: string) {\r\n const pc = this.peers[peerId];\r\n if (!pc) return;\r\n\r\n const transceivers = pc.getTransceivers();\r\n const screenTransceiver = this.peerTransceivers[peerId]?.screenTransceiver;\r\n\r\n if (!screenTransceiver) return;\r\n\r\n // Find the actual mid from the negotiated transceiver\r\n const negotiatedTransceiver = transceivers.find(\r\n (t) => t === screenTransceiver,\r\n );\r\n\r\n if (negotiatedTransceiver?.mid) {\r\n this.peerTransceivers[peerId].screenMid = negotiatedTransceiver.mid;\r\n console.log(\r\n `[Negotiation] Captured screenMid for ${peerId}: ${negotiatedTransceiver.mid}`,\r\n );\r\n }\r\n }\r\n\r\n // OFFER\r\n private async createOffer(id: string, isRenegotiation = false) {\r\n if (!isRenegotiation && !this.shouldInitiate(id)) {\r\n console.debug(\r\n `[Offer] ${id} should initiate (${id} > ${this.myId}), skipping`,\r\n );\r\n return;\r\n }\r\n\r\n if (!isRenegotiation && this.initiators.has(id)) {\r\n console.debug(\r\n `[Offer] Already initiating with ${id}, skipping duplicate`,\r\n );\r\n return;\r\n }\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] = await this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n try {\r\n const offer = await pc.createOffer();\r\n await pc.setLocalDescription(offer);\r\n\r\n // Capture screenMid after local description is set\r\n this.captureScreenMid(id);\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 console.debug(`[Offer] Sent to ${id}`);\r\n } catch (err) {\r\n console.error(`[Offer] Failed for ${id}:`, err);\r\n this.emitError(\r\n \"OFFER_CREATION_FAILED\",\r\n `Failed to create offer for ${id}`,\r\n err,\r\n true,\r\n );\r\n }\r\n }\r\n\r\n private shouldInitiate(peerId: string): boolean {\r\n // Lexicographic comparison: lower ID initiates\r\n return this.myId < peerId;\r\n }\r\n\r\n // ANSWER\r\n private async handleOffer(sdp: string, id: string) {\r\n if (!this.iceServers || this.iceServers.length === 0) {\r\n console.warn(\"[Offer] Waiting for iceServers, queuing offer from\", id);\r\n this.pendingOffers[id] = sdp;\r\n return;\r\n }\r\n\r\n if (!this.peers[id]) {\r\n this.peers[id] = await this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n try {\r\n // GLARE RECOVERY: Both peers sent OFFERs simultaneously\r\n if (pc.signalingState === \"have-local-offer\") {\r\n if (this.shouldInitiate(id)) {\r\n // WE WIN: Keep our offer, reject theirs\r\n console.warn(\r\n `[Glare] Both sent OFFERs, we win (${this.myId} < ${id}), keeping our OFFER`,\r\n );\r\n return; // Ignore their offer, wait for their ANSWER\r\n } else {\r\n // THEY WIN: Roll back and accept their offer\r\n console.warn(\r\n `[Glare] Both sent OFFERs, they win (${id} < ${this.myId}), rolling back`,\r\n );\r\n pc.close();\r\n delete this.peers[id];\r\n delete this.peerTransceivers[id];\r\n this.initiators.delete(id);\r\n\r\n // Create fresh peer connection to answer their offer\r\n this.peers[id] = await this.createPeer(id);\r\n }\r\n }\r\n\r\n // Only set remote description if we're in a valid state\r\n if (\r\n this.peers[id].signalingState !== \"stable\" &&\r\n this.peers[id].signalingState !== \"have-local-offer\"\r\n ) {\r\n console.warn(\r\n `[Signaling] Cannot accept OFFER in state \"${this.peers[id].signalingState}\"`,\r\n );\r\n return;\r\n }\r\n\r\n await this.peers[id].setRemoteDescription({\r\n type: \"offer\",\r\n sdp,\r\n });\r\n\r\n // Capture screenMid after remote description is set\r\n this.captureScreenMid(id);\r\n\r\n const pending = this.pendingIceCandidates[id] || [];\r\n\r\n for (const candidate of pending) {\r\n try {\r\n await this.peers[id].addIceCandidate(candidate);\r\n } catch (err) {\r\n console.warn(\"[ICE] Failed to add candidate:\", err);\r\n }\r\n }\r\n\r\n delete this.pendingIceCandidates[id];\r\n\r\n const answer = await this.peers[id].createAnswer();\r\n\r\n await this.peers[id].setLocalDescription(answer);\r\n await this.flushIce(id, this.peers[id]);\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 console.debug(`[Answer] Sent to ${id}`);\r\n } catch (err) {\r\n console.error(`[Signaling] Failed to handle OFFER from ${id}:`, err);\r\n this.emitError(\r\n \"OFFER_HANDLING_FAILED\",\r\n `Failed to handle offer from ${id}`,\r\n err,\r\n true,\r\n );\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 pc.oniceconnectionstatechange = null;\r\n\r\n pc.close();\r\n\r\n delete this.peers[id];\r\n delete this.peerTransceivers[id];\r\n\r\n this.initiators.delete(id);\r\n\r\n this.state.removeParticipant(id);\r\n }\r\n\r\n // SCREEN SHARE (TRANSCEIVER-BASED)\r\n /**\r\n * Start screen sharing using replaceTrack on the pre-established screen transceiver.\r\n * No need to add/remove tracks, no renegotiation needed (transceiver already in SDP).\r\n * Just swap the track and update direction if needed.\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 });\r\n\r\n const screenTrack = this.screenStream.getVideoTracks()[0];\r\n if (!screenTrack) {\r\n throw new Error(\"No video track in screen stream\");\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: screenTrack,\r\n },\r\n });\r\n\r\n this.state.setPresenterId(this.myId);\r\n\r\n // Handle user clicking browser's \"Stop Sharing\" button\r\n screenTrack.onended = () => {\r\n console.log(\"[Screen Share] User stopped via browser button\");\r\n this.stopScreenShare();\r\n };\r\n\r\n // Update all existing peer connections: use replaceTrack on screen transceiver\r\n for (const [peerId, pc] of Object.entries(this.peers)) {\r\n const txInfo = this.peerTransceivers[peerId];\r\n if (!txInfo) {\r\n console.warn(\r\n `[Screen Share] No transceiver info for ${peerId}, skipping`,\r\n );\r\n continue;\r\n }\r\n\r\n try {\r\n // Replace the track on the screen transceiver sender\r\n await txInfo.screenTransceiver.sender.replaceTrack(screenTrack);\r\n\r\n // Flip direction from recvonly to sendrecv if needed\r\n if (txInfo.screenTransceiver.currentDirection === \"recvonly\") {\r\n txInfo.screenTransceiver.direction = \"sendrecv\";\r\n console.log(\r\n `[Screen Share] Flipped ${peerId} screen transceiver to sendrecv`,\r\n );\r\n\r\n // Trigger renegotiation for the direction change\r\n await this.createOffer(peerId, true);\r\n }\r\n } catch (err) {\r\n console.error(\r\n `[Screen Share] Failed to update transceiver for ${peerId}:`,\r\n err,\r\n );\r\n }\r\n }\r\n\r\n this.send({\r\n type: \"SCREEN_SHARE_START\",\r\n sender: this.myId,\r\n room_id: this.room.id,\r\n stream_id: this.screenStream.id.replace(/[{}]/g, \"\"),\r\n });\r\n\r\n console.log(\"[Screen Share] Started successfully\");\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 async stopScreenShare() {\r\n if (!this.screenStream) return;\r\n\r\n console.log(\"[Screen Share] Stopping...\");\r\n\r\n // Stop all tracks in the screen stream\r\n this.screenStream.getTracks().forEach((t) => t.stop());\r\n\r\n // Update all peer connections: clear track and flip direction back to recvonly\r\n for (const [peerId, pc] of Object.entries(this.peers)) {\r\n const txInfo = this.peerTransceivers[peerId];\r\n if (!txInfo) continue;\r\n\r\n try {\r\n // Clear the screen transceiver track\r\n await txInfo.screenTransceiver.sender.replaceTrack(null);\r\n\r\n // Flip direction back to recvonly\r\n if (txInfo.screenTransceiver.currentDirection === \"sendrecv\") {\r\n txInfo.screenTransceiver.direction = \"recvonly\";\r\n console.log(\r\n `[Screen Share] Flipped ${peerId} screen transceiver to recvonly`,\r\n );\r\n\r\n // Trigger renegotiation for the direction change\r\n await this.createOffer(peerId, true);\r\n }\r\n } catch (err) {\r\n console.error(\r\n `[Screen Share] Failed to clear transceiver for ${peerId}:`,\r\n err,\r\n );\r\n }\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.room.id,\r\n });\r\n\r\n console.log(\"[Screen Share] Stopped\");\r\n }\r\n\r\n // CHAT\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.room.id) {\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.room.id,\r\n target: isPrivate ? (payload.target ?? null) : null,\r\n reply_to: payload.reply_to ?? null,\r\n client_ts: Date.now(),\r\n });\r\n }\r\n\r\n // DISCONNECT\r\n disconnect() {\r\n this.intentionalDisconnect = true;\r\n\r\n this.stopScreenShare();\r\n\r\n Object.values(this.peers).forEach((pc) => pc.close());\r\n this.peers = {};\r\n this.peerTransceivers = {};\r\n this.initiators.clear();\r\n\r\n this.stopHeartbeat();\r\n\r\n if (this.ws?.readyState === WebSocket.OPEN) {\r\n this.send({\r\n type: \"LEAVE\",\r\n room_id: this.room.id,\r\n user_id: this.myId,\r\n sender_name: this.state.localParticipant?.name,\r\n });\r\n\r\n // Allow the LEAVE frame to be flushed.\r\n setTimeout(() => {\r\n this.ws?.close(1000, \"Leaving meeting\");\r\n this.ws = null;\r\n }, 50);\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.room.id = null;\r\n\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.events.onMeetingLeft?.();\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 approveJoinRequest(requestId: string) {\r\n this.send({\r\n type: \"JOIN_APPROVE\",\r\n request_id: requestId,\r\n });\r\n }\r\n\r\n rejectJoinRequest(requestId: string) {\r\n this.send({\r\n type: \"JOIN_REJECT\",\r\n request_id: requestId,\r\n });\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?: {\r\n onError?: (err: any) => void;\r\n onEntryRequested?: (req: any) => void;\r\n onEntryResponded?: (payload: any, decision?: any) => void;\r\n onMeetingLeft?: () => void;\r\n}) => {\r\n const ctx = useMeetingContext();\r\n\r\n // Error Handler\r\n useEffect(() => {\r\n if (!handlers?.onError) return;\r\n return ctx.onError(handlers.onError);\r\n }, [handlers?.onError]);\r\n\r\n // Entry Request Handler\r\n useEffect(() => {\r\n if (!handlers?.onEntryRequested) return;\r\n return ctx.onEntryRequested(handlers.onEntryRequested);\r\n }, [handlers?.onEntryRequested]);\r\n\r\n // Entry Response Handler\r\n useEffect(() => {\r\n if (!handlers?.onEntryResponded) return;\r\n return ctx.onEntryResponded(handlers.onEntryResponded);\r\n }, [handlers?.onEntryResponded]);\r\n\r\n // Meeting Left Handler\r\n useEffect(() => {\r\n if (!handlers?.onMeetingLeft) return;\r\n return ctx.onMeetingLeft(handlers.onMeetingLeft);\r\n }, [handlers?.onMeetingLeft]);\r\n\r\n const { sdk: _, ...publicApi } = ctx;\r\n return publicApi;\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 const videoRef = useRef<HTMLVideoElement | null>(null);\r\n const audioRef = useRef<HTMLAudioElement | null>(null);\r\n\r\n const [participant, setParticipant] = useState<Participant | null>(\r\n () => sdk.state.getParticipant(participantId) || null,\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\r\n return unsub;\r\n }, [participantId, sdk]);\r\n\r\n useEffect(() => {\r\n const stream = participant?.media?.stream;\r\n if (!stream) return;\r\n\r\n // VIDEO\r\n if (videoRef.current) {\r\n videoRef.current.srcObject = stream;\r\n videoRef.current.muted = true;\r\n videoRef.current.playsInline = true;\r\n videoRef.current.play().catch(() => {});\r\n }\r\n\r\n // AUDIO\r\n if (audioRef.current) {\r\n audioRef.current.srcObject = stream;\r\n audioRef.current.play().catch(() => {});\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","videoTrack","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 room: { id: string | null; name: 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 approveJoinRequest: (requestId: string) => void;\r\n rejectJoinRequest: (requestId: string) => void;\r\n onError: (cb: (err: any) => void) => () => void;\r\n onEntryRequested: (cb: (req: any) => void) => () => void;\r\n onEntryResponded: (cb: (payload: any, decision?: any) => void) => () => void;\r\n onMeetingLeft: (cb: () => 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 const entryRequestListeners = useRef(new Set<(req: any) => void>());\r\n const entryResponseListeners = useRef(\r\n new Set<(payload: any, decision?: any) => void>(),\r\n );\r\n const meetingLeftListeners = useRef(new Set<() => void>());\r\n\r\n if (!sdkRef.current) {\r\n sdkRef.current = new VideoSDKCore({\r\n onError: (err) => errorListeners.current.forEach((fn) => fn(err)),\r\n onEntryRequested: (req) =>\r\n entryRequestListeners.current.forEach((fn) => fn(req)),\r\n onEntryResponded: (p, d) =>\r\n entryResponseListeners.current.forEach((fn) => fn(p, d)),\r\n onMeetingLeft: () => meetingLeftListeners.current.forEach((fn) => fn()),\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 room: sdk.getMeeting(),\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 approveJoinRequest: sdk.approveJoinRequest.bind(sdk),\r\n rejectJoinRequest: sdk.rejectJoinRequest.bind(sdk),\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 onEntryRequested: (cb: (err: any) => void) => {\r\n entryRequestListeners.current.add(cb);\r\n\r\n return () => {\r\n entryRequestListeners.current.delete(cb);\r\n };\r\n },\r\n onEntryResponded: (cb: (err: any) => void) => {\r\n entryResponseListeners.current.add(cb);\r\n\r\n return () => {\r\n entryResponseListeners.current.delete(cb);\r\n };\r\n },\r\n onMeetingLeft: (cb: () => void) => {\r\n meetingLeftListeners.current.add(cb);\r\n return () => {\r\n meetingLeftListeners.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 private lastPong = Date.now();\r\n private iceServers: RTCIceServer[] = [];\r\n private intentionalDisconnect = false;\r\n\r\n private myId: string;\r\n private room: { id: string | null; name: string | null } = {\r\n id: null,\r\n name: null,\r\n };\r\n private localStream: MediaStream | null = null;\r\n private screenStream: MediaStream | null = null;\r\n private isScreenSharing = false;\r\n\r\n // Transceiver-based tracking: maps peerId -> { cameraTransceiver, screenTransceiver, screenMid }\r\n private peerTransceivers: Record<\r\n string,\r\n {\r\n cameraTransceiver: RTCRtpTransceiver;\r\n screenTransceiver: RTCRtpTransceiver;\r\n screenMid: string | null; // set after negotiation completes\r\n }\r\n > = {};\r\n\r\n private pingInterval: any = null;\r\n private pendingIceCandidates: Record<string, RTCIceCandidateInit[]> = {};\r\n private pendingOffers: Record<string, string> = {};\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\r\n // Track if we're in the waiting room (pending approval)\r\n private isWaitingForApproval = false;\r\n private pendingRequestId: string | null = null;\r\n\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.room.id,\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 try {\r\n this.localStream = await navigator.mediaDevices.getUserMedia({\r\n video: {\r\n width: { ideal: 1280 },\r\n height: { ideal: 720 },\r\n },\r\n audio: {\r\n echoCancellation: true,\r\n noiseSuppression: true,\r\n autoGainControl: true,\r\n },\r\n });\r\n\r\n // Verify tracks are actually live BEFORE connecting\r\n const hasVideo = this.localStream\r\n .getVideoTracks()\r\n .some((t) => t.readyState === \"live\");\r\n const hasAudio = this.localStream\r\n .getAudioTracks()\r\n .some((t) => t.readyState === \"live\");\r\n\r\n if (!hasVideo || !hasAudio) {\r\n throw new Error(`Missing tracks: video=${hasVideo}, audio=${hasAudio}`);\r\n }\r\n\r\n video.srcObject = this.localStream;\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 } catch (err: any) {\r\n this.emitError(\"GET_USER_MEDIA_FAILED\", err?.message, err, false);\r\n throw err;\r\n }\r\n }\r\n\r\n // CONNECT\r\n async connect(roomId: string, name: string) {\r\n this.room.id = 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 console.log(\"WebSocket connected, sending JOIN...\");\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.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 if (this.intentionalDisconnect) {\r\n return; // do NOT reconnect\r\n }\r\n\r\n if (e.code === 1000 || e.code === 1001) {\r\n return;\r\n }\r\n\r\n // Don't auto-reconnect if waiting for approval (user must reconnect after approval)\r\n if (this.isWaitingForApproval) {\r\n console.log(\"Waiting for approval, not auto-reconnecting\");\r\n return;\r\n }\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 getMeeting(): { id: string | null; name: string | null } {\r\n return this.room;\r\n }\r\n\r\n toggleMic() {\r\n const mediaState = this.state.localParticipant?.media;\r\n if (!mediaState) return;\r\n\r\n const nextEnabled = !mediaState.micEnabled;\r\n\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 const nextEnabled = !mediaState.camEnabled;\r\n\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.room.id) 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.room.id!, 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\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.peerTransceivers = {};\r\n this.initiators.clear();\r\n this.pendingIceCandidates = {};\r\n\r\n this.state.resetRemoteState();\r\n }\r\n\r\n private async handleJoinApproved(msg: any) {\r\n console.log(\"JOIN_APPROVED received, sending new JOIN...\");\r\n\r\n this.events.onEntryResponded?.({\r\n participantId: msg.user_id,\r\n decision: \"approved\",\r\n });\r\n\r\n this.isWaitingForApproval = false;\r\n this.pendingRequestId = null;\r\n\r\n // Send JOIN again on SAME socket\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.send({\r\n type: \"JOIN\",\r\n room_id: this.room.id,\r\n user_id: this.myId,\r\n sender_name: this.participantName,\r\n });\r\n console.log(\"Sent new JOIN after approval\");\r\n }\r\n }\r\n\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 \"PONG\":\r\n this.lastPong = Date.now();\r\n break;\r\n\r\n case \"OFFER\":\r\n console.log(\"[Offer] Received from\", msg.sender, {\r\n sdp: msg.payload.substring(0, 200),\r\n });\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 console.log(\"[Answer] Received from\", msg.sender, {\r\n signalingState: pc?.signalingState,\r\n iceConnectionState: pc?.iceConnectionState,\r\n connectionState: pc?.connectionState,\r\n });\r\n\r\n if (!pc) return;\r\n\r\n if (pc.signalingState !== \"have-local-offer\") {\r\n console.warn(\r\n `[Signaling] Unexpected ANSWER in state \"${pc.signalingState}\", ignoring`,\r\n );\r\n return;\r\n }\r\n\r\n try {\r\n await pc.setRemoteDescription({\r\n type: \"answer\",\r\n sdp: msg.payload,\r\n });\r\n\r\n // Capture screenMid after remote description is set\r\n this.captureScreenMid(msg.sender);\r\n\r\n await this.flushIce(msg.sender, pc);\r\n } catch (err) {\r\n console.error(\"[Signaling] Failed to apply answer:\", err);\r\n this.emitError(\r\n \"ANSWER_FAILED\",\r\n `Failed to apply answer from ${msg.sender}`,\r\n err,\r\n true,\r\n );\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 \"EXISTING_USERS\":\r\n if (msg.presenterId) {\r\n this.state.setPresenterId(msg.presenterId);\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 if (p.isScreenSharing && p.remoteScreenStreamId) {\r\n console.log(\r\n `[Existing Users] ${p.name} is sharing screen (stream: ${p.remoteScreenStreamId})`,\r\n );\r\n\r\n this.state.setPresenterId(p.id);\r\n this.state.updateParticipantMedia(p.id, {\r\n isScreenSharing: true,\r\n remoteScreenStreamId: p.remoteScreenStreamId,\r\n });\r\n console.log(\r\n `[EXISTING_USERS] Pre-seeded screen state for ${p.id}, waiting for ontrack`,\r\n );\r\n }\r\n\r\n if (this.shouldInitiate(p.id)) {\r\n await this.createOffer(p.id);\r\n }\r\n }\r\n break;\r\n\r\n case \"JOINED\": {\r\n if (msg.iceServers) {\r\n this.iceServers = msg.iceServers;\r\n }\r\n this.room.name = msg.room_name;\r\n\r\n this.isWaitingForApproval = false;\r\n this.pendingRequestId = null;\r\n this.intentionalDisconnect = false;\r\n this.reconnectAttempts = 0;\r\n\r\n // Process any OFFERs that arrived before JOINED\r\n if (Object.keys(this.pendingOffers).length > 0) {\r\n console.log(\r\n \"Processing\",\r\n Object.keys(this.pendingOffers).length,\r\n \"pending offers\",\r\n );\r\n for (const [peerId, sdp] of Object.entries(this.pendingOffers)) {\r\n console.log(\"Handling queued offer from\", peerId);\r\n await this.handleOffer(sdp, peerId);\r\n }\r\n this.pendingOffers = {};\r\n }\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\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 this.events.onUserJoined?.(p);\r\n\r\n if (this.shouldInitiate(p.id)) {\r\n await this.createOffer(p.id);\r\n }\r\n\r\n break;\r\n }\r\n\r\n case \"JOIN_PENDING\": {\r\n const req = msg.request;\r\n\r\n console.log(\"JOIN_PENDING - waiting for host approval\");\r\n this.isWaitingForApproval = true;\r\n this.pendingRequestId = req.request_id;\r\n\r\n this.events.onEntryRequested?.({\r\n requestId: req.request_id,\r\n userId: req.user_id,\r\n name: req.name,\r\n });\r\n\r\n break;\r\n }\r\n\r\n case \"JOIN_REQUEST\": {\r\n const req = msg.request;\r\n\r\n console.log(\"JOIN_REQUEST - show to host for approval\");\r\n\r\n this.events.onEntryRequested?.({\r\n requestId: req.id,\r\n userId: req.user_id,\r\n name: req.name,\r\n });\r\n\r\n break;\r\n }\r\n\r\n case \"JOIN_APPROVED\": {\r\n await this.handleJoinApproved(msg);\r\n break;\r\n }\r\n\r\n case \"JOIN_REJECTED\": {\r\n const decision = \"rejected\";\r\n\r\n console.log(\"JOIN_REJECTED - user not allowed to join\");\r\n this.isWaitingForApproval = false;\r\n this.pendingRequestId = null;\r\n\r\n this.events.onEntryResponded?.({\r\n participantId: msg.user_id,\r\n decision,\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 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;\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\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 // Screen stream will arrive via ontrack on the screen transceiver\r\n this.events.onScreenShareStarted?.(peerId, 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\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 private async createPeer(id: string) {\r\n if (!this.localStream) throw new Error(\"No local stream\");\r\n if (!this.iceServers || this.iceServers.length === 0) {\r\n throw new Error(\r\n \"ICE Servers not configured. Backend must provide iceServers on JOIN.\",\r\n );\r\n }\r\n\r\n console.log(\r\n \"Creating peer connection for\",\r\n id,\r\n \"with 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: this.iceServers,\r\n });\r\n\r\n // AUDIO TRANSCEIVER\r\n const audioTransceiver = pc.addTransceiver(\"audio\", {\r\n direction: \"sendrecv\",\r\n });\r\n const audioTrack = this.localStream.getAudioTracks()[0];\r\n if (audioTrack) {\r\n await audioTransceiver.sender.replaceTrack(audioTrack);\r\n }\r\n\r\n // CAMERA VIDEO TRANSCEIVER\r\n const cameraTransceiver = pc.addTransceiver(\"video\", {\r\n direction: \"sendrecv\",\r\n });\r\n const videoTrack = this.localStream.getVideoTracks()[0];\r\n if (videoTrack) {\r\n await cameraTransceiver.sender.replaceTrack(videoTrack);\r\n }\r\n\r\n const screenTransceiver = pc.addTransceiver(\"video\", {\r\n direction: this.isScreenSharing ? \"sendrecv\" : \"recvonly\",\r\n });\r\n\r\n if (this.isScreenSharing && this.screenStream) {\r\n const screenTrack = this.screenStream.getVideoTracks()[0];\r\n if (screenTrack) {\r\n await screenTransceiver.sender.replaceTrack(screenTrack);\r\n }\r\n }\r\n\r\n // Store transceiver info for later use\r\n this.peerTransceivers[id] = {\r\n cameraTransceiver,\r\n screenTransceiver,\r\n screenMid: null, // will be populated after negotiation\r\n };\r\n\r\n // TRACK HANDLER\r\n pc.ontrack = (event) => {\r\n const transceiver = event.transceiver;\r\n // Compare transceiver object reference, not mid (mid may not be assigned yet)\r\n const isScreenTrack =\r\n transceiver === this.peerTransceivers[id]?.screenTransceiver;\r\n\r\n console.log(\r\n `[ontrack] ${id}: kind=${event.track.kind}, mid=${transceiver.mid}, isScreen=${isScreenTrack}`,\r\n );\r\n\r\n const incomingStream =\r\n event.streams?.[0] || new MediaStream([event.track]);\r\n\r\n // Handle track unmuting\r\n if (event.track.muted) {\r\n event.track.onunmute = () => {\r\n console.log(`[ontrack] ${event.track.kind} track unmuted for ${id}`);\r\n };\r\n }\r\n\r\n if (isScreenTrack) {\r\n // Screen share stream\r\n const videoTrack =\r\n event.track.kind === \"video\"\r\n ? event.track\r\n : incomingStream.getVideoTracks()[0];\r\n\r\n this.state.updateParticipantMedia(id, {\r\n screenStream: incomingStream,\r\n screenTrack: videoTrack,\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 // Camera/normal stream\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.oniceconnectionstatechange = () => {\r\n console.log(`[ICE Connection] ${id}: ${pc.iceConnectionState}`);\r\n };\r\n\r\n pc.onconnectionstatechange = () => {\r\n console.log(`[Connection] ${id}: ${pc.connectionState}`);\r\n if (pc.connectionState === \"failed\") {\r\n try {\r\n pc.restartIce();\r\n } catch (e) {\r\n console.warn(\"Failed to restart ICE:\", e);\r\n }\r\n }\r\n };\r\n\r\n return pc;\r\n }\r\n\r\n /**\r\n * Capture the screen transceiver's MID after SDP negotiation completes.\r\n * The MID is assigned during negotiation and is stable for the life of the connection.\r\n */\r\n private captureScreenMid(peerId: string) {\r\n const pc = this.peers[peerId];\r\n if (!pc) return;\r\n\r\n const transceivers = pc.getTransceivers();\r\n const screenTransceiver = this.peerTransceivers[peerId]?.screenTransceiver;\r\n\r\n if (!screenTransceiver) return;\r\n\r\n // Find the actual mid from the negotiated transceiver\r\n const negotiatedTransceiver = transceivers.find(\r\n (t) => t === screenTransceiver,\r\n );\r\n\r\n if (negotiatedTransceiver?.mid) {\r\n this.peerTransceivers[peerId].screenMid = negotiatedTransceiver.mid;\r\n console.log(\r\n `[Negotiation] Captured screenMid for ${peerId}: ${negotiatedTransceiver.mid}`,\r\n );\r\n }\r\n }\r\n\r\n // OFFER\r\n private async createOffer(id: string, isRenegotiation = false) {\r\n if (!isRenegotiation && !this.shouldInitiate(id)) {\r\n console.debug(\r\n `[Offer] ${id} should initiate (${id} > ${this.myId}), skipping`,\r\n );\r\n return;\r\n }\r\n\r\n if (!isRenegotiation && this.initiators.has(id)) {\r\n console.debug(\r\n `[Offer] Already initiating with ${id}, skipping duplicate`,\r\n );\r\n return;\r\n }\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] = await this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n try {\r\n const offer = await pc.createOffer();\r\n await pc.setLocalDescription(offer);\r\n\r\n // Capture screenMid after local description is set\r\n this.captureScreenMid(id);\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 console.debug(`[Offer] Sent to ${id}`);\r\n } catch (err) {\r\n console.error(`[Offer] Failed for ${id}:`, err);\r\n this.emitError(\r\n \"OFFER_CREATION_FAILED\",\r\n `Failed to create offer for ${id}`,\r\n err,\r\n true,\r\n );\r\n }\r\n }\r\n\r\n private shouldInitiate(peerId: string): boolean {\r\n // Lexicographic comparison: lower ID initiates\r\n return this.myId < peerId;\r\n }\r\n\r\n // ANSWER\r\n private async handleOffer(sdp: string, id: string) {\r\n if (!this.iceServers || this.iceServers.length === 0) {\r\n console.warn(\"[Offer] Waiting for iceServers, queuing offer from\", id);\r\n this.pendingOffers[id] = sdp;\r\n return;\r\n }\r\n\r\n if (!this.peers[id]) {\r\n this.peers[id] = await this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n try {\r\n // GLARE RECOVERY: Both peers sent OFFERs simultaneously\r\n if (pc.signalingState === \"have-local-offer\") {\r\n if (this.shouldInitiate(id)) {\r\n // WE WIN: Keep our offer, reject theirs\r\n console.warn(\r\n `[Glare] Both sent OFFERs, we win (${this.myId} < ${id}), keeping our OFFER`,\r\n );\r\n return; // Ignore their offer, wait for their ANSWER\r\n } else {\r\n // THEY WIN: Roll back and accept their offer\r\n console.warn(\r\n `[Glare] Both sent OFFERs, they win (${id} < ${this.myId}), rolling back`,\r\n );\r\n pc.close();\r\n delete this.peers[id];\r\n delete this.peerTransceivers[id];\r\n this.initiators.delete(id);\r\n\r\n // Create fresh peer connection to answer their offer\r\n this.peers[id] = await this.createPeer(id);\r\n }\r\n }\r\n\r\n // Only set remote description if we're in a valid state\r\n if (\r\n this.peers[id].signalingState !== \"stable\" &&\r\n this.peers[id].signalingState !== \"have-local-offer\"\r\n ) {\r\n console.warn(\r\n `[Signaling] Cannot accept OFFER in state \"${this.peers[id].signalingState}\"`,\r\n );\r\n return;\r\n }\r\n\r\n await this.peers[id].setRemoteDescription({\r\n type: \"offer\",\r\n sdp,\r\n });\r\n\r\n // Capture screenMid after remote description is set\r\n this.captureScreenMid(id);\r\n\r\n const pending = this.pendingIceCandidates[id] || [];\r\n\r\n for (const candidate of pending) {\r\n try {\r\n await this.peers[id].addIceCandidate(candidate);\r\n } catch (err) {\r\n console.warn(\"[ICE] Failed to add candidate:\", err);\r\n }\r\n }\r\n\r\n delete this.pendingIceCandidates[id];\r\n\r\n const answer = await this.peers[id].createAnswer();\r\n\r\n await this.peers[id].setLocalDescription(answer);\r\n await this.flushIce(id, this.peers[id]);\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 console.debug(`[Answer] Sent to ${id}`);\r\n } catch (err) {\r\n console.error(`[Signaling] Failed to handle OFFER from ${id}:`, err);\r\n this.emitError(\r\n \"OFFER_HANDLING_FAILED\",\r\n `Failed to handle offer from ${id}`,\r\n err,\r\n true,\r\n );\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 pc.oniceconnectionstatechange = null;\r\n\r\n pc.close();\r\n\r\n delete this.peers[id];\r\n delete this.peerTransceivers[id];\r\n\r\n this.initiators.delete(id);\r\n\r\n this.state.removeParticipant(id);\r\n }\r\n\r\n // SCREEN SHARE (TRANSCEIVER-BASED)\r\n /**\r\n * Start screen sharing using replaceTrack on the pre-established screen transceiver.\r\n * No need to add/remove tracks, no renegotiation needed (transceiver already in SDP).\r\n * Just swap the track and update direction if needed.\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 });\r\n\r\n const screenTrack = this.screenStream.getVideoTracks()[0];\r\n if (!screenTrack) {\r\n throw new Error(\"No video track in screen stream\");\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: screenTrack,\r\n },\r\n });\r\n\r\n this.state.setPresenterId(this.myId);\r\n\r\n // Handle user clicking browser's \"Stop Sharing\" button\r\n screenTrack.onended = () => {\r\n console.log(\"[Screen Share] User stopped via browser button\");\r\n this.stopScreenShare();\r\n };\r\n\r\n // Update all existing peer connections: use replaceTrack on screen transceiver\r\n for (const [peerId, pc] of Object.entries(this.peers)) {\r\n const txInfo = this.peerTransceivers[peerId];\r\n if (!txInfo) {\r\n console.warn(\r\n `[Screen Share] No transceiver info for ${peerId}, skipping`,\r\n );\r\n continue;\r\n }\r\n\r\n try {\r\n // Replace the track on the screen transceiver sender\r\n await txInfo.screenTransceiver.sender.replaceTrack(screenTrack);\r\n\r\n // Flip direction from recvonly to sendrecv if needed\r\n if (txInfo.screenTransceiver.currentDirection === \"recvonly\") {\r\n txInfo.screenTransceiver.direction = \"sendrecv\";\r\n console.log(\r\n `[Screen Share] Flipped ${peerId} screen transceiver to sendrecv`,\r\n );\r\n\r\n // Trigger renegotiation for the direction change\r\n await this.createOffer(peerId, true);\r\n }\r\n } catch (err) {\r\n console.error(\r\n `[Screen Share] Failed to update transceiver for ${peerId}:`,\r\n err,\r\n );\r\n }\r\n }\r\n\r\n this.send({\r\n type: \"SCREEN_SHARE_START\",\r\n sender: this.myId,\r\n room_id: this.room.id,\r\n stream_id: this.screenStream.id.replace(/[{}]/g, \"\"),\r\n });\r\n\r\n console.log(\"[Screen Share] Started successfully\");\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 /**\r\n * Stop screen sharing: clear the screen transceiver track and flip direction back to recvonly.\r\n */\r\n async stopScreenShare() {\r\n if (!this.screenStream) return;\r\n\r\n console.log(\"[Screen Share] Stopping...\");\r\n\r\n // Stop all tracks in the screen stream\r\n this.screenStream.getTracks().forEach((t) => t.stop());\r\n\r\n // Update all peer connections: clear track and flip direction back to recvonly\r\n for (const [peerId, pc] of Object.entries(this.peers)) {\r\n const txInfo = this.peerTransceivers[peerId];\r\n if (!txInfo) continue;\r\n\r\n try {\r\n // Clear the screen transceiver track\r\n await txInfo.screenTransceiver.sender.replaceTrack(null);\r\n\r\n // Flip direction back to recvonly\r\n if (txInfo.screenTransceiver.currentDirection === \"sendrecv\") {\r\n txInfo.screenTransceiver.direction = \"recvonly\";\r\n console.log(\r\n `[Screen Share] Flipped ${peerId} screen transceiver to recvonly`,\r\n );\r\n\r\n // Trigger renegotiation for the direction change\r\n await this.createOffer(peerId, true);\r\n }\r\n } catch (err) {\r\n console.error(\r\n `[Screen Share] Failed to clear transceiver for ${peerId}:`,\r\n err,\r\n );\r\n }\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.room.id,\r\n });\r\n\r\n console.log(\"[Screen Share] Stopped\");\r\n }\r\n\r\n // CHAT\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.room.id) {\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.room.id,\r\n target: isPrivate ? (payload.target ?? null) : null,\r\n reply_to: payload.reply_to ?? null,\r\n client_ts: Date.now(),\r\n });\r\n }\r\n\r\n // DISCONNECT\r\n async disconnect() {\r\n this.intentionalDisconnect = true;\r\n\r\n await this.stopScreenShare();\r\n\r\n Object.values(this.peers).forEach((pc) => pc.close());\r\n this.peers = {};\r\n this.peerTransceivers = {};\r\n this.initiators.clear();\r\n\r\n this.stopHeartbeat();\r\n\r\n if (this.ws?.readyState === WebSocket.OPEN) {\r\n this.send({\r\n type: \"LEAVE\",\r\n room_id: this.room.id,\r\n user_id: this.myId,\r\n sender_name: this.state.localParticipant?.name,\r\n });\r\n\r\n // Allow the LEAVE frame to be flushed.\r\n setTimeout(() => {\r\n this.ws?.close(1000, \"Leaving meeting\");\r\n this.ws = null;\r\n }, 50);\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.room.id = null;\r\n\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.events.onMeetingLeft?.();\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 approveJoinRequest(requestId: string) {\r\n this.send({\r\n type: \"JOIN_APPROVE\",\r\n request_id: requestId,\r\n });\r\n }\r\n\r\n rejectJoinRequest(requestId: string) {\r\n this.send({\r\n type: \"JOIN_REJECT\",\r\n request_id: requestId,\r\n });\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?: {\r\n onError?: (err: any) => void;\r\n onEntryRequested?: (req: any) => void;\r\n onEntryResponded?: (payload: any, decision?: any) => void;\r\n onMeetingLeft?: () => void;\r\n}) => {\r\n const ctx = useMeetingContext();\r\n\r\n // Error Handler\r\n useEffect(() => {\r\n if (!handlers?.onError) return;\r\n return ctx.onError(handlers.onError);\r\n }, [handlers?.onError]);\r\n\r\n // Entry Request Handler\r\n useEffect(() => {\r\n if (!handlers?.onEntryRequested) return;\r\n return ctx.onEntryRequested(handlers.onEntryRequested);\r\n }, [handlers?.onEntryRequested]);\r\n\r\n // Entry Response Handler\r\n useEffect(() => {\r\n if (!handlers?.onEntryResponded) return;\r\n return ctx.onEntryResponded(handlers.onEntryResponded);\r\n }, [handlers?.onEntryResponded]);\r\n\r\n // Meeting Left Handler\r\n useEffect(() => {\r\n if (!handlers?.onMeetingLeft) return;\r\n return ctx.onMeetingLeft(handlers.onMeetingLeft);\r\n }, [handlers?.onMeetingLeft]);\r\n\r\n const { sdk: _, ...publicApi } = ctx;\r\n return publicApi;\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 const videoRef = useRef<HTMLVideoElement | null>(null);\r\n const audioRef = useRef<HTMLAudioElement | null>(null);\r\n\r\n const [participant, setParticipant] = useState<Participant | null>(\r\n () => sdk.state.getParticipant(participantId) || null,\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\r\n return unsub;\r\n }, [participantId, sdk]);\r\n\r\n useEffect(() => {\r\n const stream = participant?.media?.stream;\r\n if (!stream) return;\r\n\r\n // VIDEO\r\n if (videoRef.current) {\r\n videoRef.current.srcObject = stream;\r\n videoRef.current.muted = true;\r\n videoRef.current.playsInline = true;\r\n videoRef.current.play().catch(() => {});\r\n }\r\n\r\n // AUDIO\r\n if (audioRef.current) {\r\n audioRef.current.srcObject = stream;\r\n audioRef.current.play().catch(() => {});\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","videoTrack","import_react","import_react","import_react"]}
package/dist/index.mjs CHANGED
@@ -651,16 +651,6 @@ var VideoSDKCore = class {
651
651
  }
652
652
  }
653
653
  }
654
- // PEER
655
- /**
656
- * Create a peer connection with pre-established transceiver layout:
657
- * - Audio transceiver (sendrecv)
658
- * - Camera video transceiver (sendrecv)
659
- * - Screen video transceiver (initially recvonly, becomes sendrecv when sharing)
660
- *
661
- * This fixed layout ensures late joiners get the screen transceiver m-line
662
- * negotiated from the very first offer, even if no one is sharing yet.
663
- */
664
654
  async createPeer(id) {
665
655
  if (!this.localStream) throw new Error("No local stream");
666
656
  if (!this.iceServers || this.iceServers.length === 0) {
@@ -712,7 +702,7 @@ var VideoSDKCore = class {
712
702
  };
713
703
  pc.ontrack = (event) => {
714
704
  const transceiver = event.transceiver;
715
- const isScreenTrack = transceiver.mid === this.peerTransceivers[id]?.screenMid;
705
+ const isScreenTrack = transceiver === this.peerTransceivers[id]?.screenTransceiver;
716
706
  console.log(
717
707
  `[ontrack] ${id}: kind=${event.track.kind}, mid=${transceiver.mid}, isScreen=${isScreenTrack}`
718
708
  );
@@ -992,6 +982,9 @@ var VideoSDKCore = class {
992
982
  throw err;
993
983
  }
994
984
  }
985
+ /**
986
+ * Stop screen sharing: clear the screen transceiver track and flip direction back to recvonly.
987
+ */
995
988
  async stopScreenShare() {
996
989
  if (!this.screenStream) return;
997
990
  console.log("[Screen Share] Stopping...");
@@ -1068,9 +1061,9 @@ var VideoSDKCore = class {
1068
1061
  });
1069
1062
  }
1070
1063
  // DISCONNECT
1071
- disconnect() {
1064
+ async disconnect() {
1072
1065
  this.intentionalDisconnect = true;
1073
- this.stopScreenShare();
1066
+ await this.stopScreenShare();
1074
1067
  Object.values(this.peers).forEach((pc) => pc.close());
1075
1068
  this.peers = {};
1076
1069
  this.peerTransceivers = {};
@@ -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\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 room: { id: string | null; name: 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 approveJoinRequest: (requestId: string) => void;\r\n rejectJoinRequest: (requestId: string) => void;\r\n onError: (cb: (err: any) => void) => () => void;\r\n onEntryRequested: (cb: (req: any) => void) => () => void;\r\n onEntryResponded: (cb: (payload: any, decision?: any) => void) => () => void;\r\n onMeetingLeft: (cb: () => 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 const entryRequestListeners = useRef(new Set<(req: any) => void>());\r\n const entryResponseListeners = useRef(\r\n new Set<(payload: any, decision?: any) => void>(),\r\n );\r\n const meetingLeftListeners = useRef(new Set<() => void>());\r\n\r\n if (!sdkRef.current) {\r\n sdkRef.current = new VideoSDKCore({\r\n onError: (err) => errorListeners.current.forEach((fn) => fn(err)),\r\n onEntryRequested: (req) =>\r\n entryRequestListeners.current.forEach((fn) => fn(req)),\r\n onEntryResponded: (p, d) =>\r\n entryResponseListeners.current.forEach((fn) => fn(p, d)),\r\n onMeetingLeft: () => meetingLeftListeners.current.forEach((fn) => fn()),\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 room: sdk.getMeeting(),\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 approveJoinRequest: sdk.approveJoinRequest.bind(sdk),\r\n rejectJoinRequest: sdk.rejectJoinRequest.bind(sdk),\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 onEntryRequested: (cb: (err: any) => void) => {\r\n entryRequestListeners.current.add(cb);\r\n\r\n return () => {\r\n entryRequestListeners.current.delete(cb);\r\n };\r\n },\r\n onEntryResponded: (cb: (err: any) => void) => {\r\n entryResponseListeners.current.add(cb);\r\n\r\n return () => {\r\n entryResponseListeners.current.delete(cb);\r\n };\r\n },\r\n onMeetingLeft: (cb: () => void) => {\r\n meetingLeftListeners.current.add(cb);\r\n return () => {\r\n meetingLeftListeners.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 private lastPong = Date.now();\r\n private iceServers: RTCIceServer[] = [];\r\n private intentionalDisconnect = false;\r\n\r\n private myId: string;\r\n private room: { id: string | null; name: string | null } = {\r\n id: null,\r\n name: null,\r\n };\r\n private localStream: MediaStream | null = null;\r\n private screenStream: MediaStream | null = null;\r\n private isScreenSharing = false;\r\n\r\n // Transceiver-based tracking: maps peerId -> { cameraTransceiver, screenTransceiver, screenMid }\r\n private peerTransceivers: Record<\r\n string,\r\n {\r\n cameraTransceiver: RTCRtpTransceiver;\r\n screenTransceiver: RTCRtpTransceiver;\r\n screenMid: string | null; // set after negotiation completes\r\n }\r\n > = {};\r\n\r\n private pingInterval: any = null;\r\n private pendingIceCandidates: Record<string, RTCIceCandidateInit[]> = {};\r\n private pendingOffers: Record<string, string> = {};\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\r\n // Track if we're in the waiting room (pending approval)\r\n private isWaitingForApproval = false;\r\n private pendingRequestId: string | null = null;\r\n\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.room.id,\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 try {\r\n this.localStream = await navigator.mediaDevices.getUserMedia({\r\n video: {\r\n width: { ideal: 1280 },\r\n height: { ideal: 720 },\r\n },\r\n audio: {\r\n echoCancellation: true,\r\n noiseSuppression: true,\r\n autoGainControl: true,\r\n },\r\n });\r\n\r\n // Verify tracks are actually live BEFORE connecting\r\n const hasVideo = this.localStream\r\n .getVideoTracks()\r\n .some((t) => t.readyState === \"live\");\r\n const hasAudio = this.localStream\r\n .getAudioTracks()\r\n .some((t) => t.readyState === \"live\");\r\n\r\n if (!hasVideo || !hasAudio) {\r\n throw new Error(`Missing tracks: video=${hasVideo}, audio=${hasAudio}`);\r\n }\r\n\r\n video.srcObject = this.localStream;\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 } catch (err: any) {\r\n this.emitError(\"GET_USER_MEDIA_FAILED\", err?.message, err, false);\r\n throw err;\r\n }\r\n }\r\n\r\n // CONNECT\r\n async connect(roomId: string, name: string) {\r\n this.room.id = 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 console.log(\"WebSocket connected, sending JOIN...\");\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.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 if (this.intentionalDisconnect) {\r\n return; // do NOT reconnect\r\n }\r\n\r\n if (e.code === 1000 || e.code === 1001) {\r\n return;\r\n }\r\n\r\n // Don't auto-reconnect if waiting for approval (user must reconnect after approval)\r\n if (this.isWaitingForApproval) {\r\n console.log(\"Waiting for approval, not auto-reconnecting\");\r\n return;\r\n }\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 getMeeting(): { id: string | null; name: string | null } {\r\n return this.room;\r\n }\r\n\r\n toggleMic() {\r\n const mediaState = this.state.localParticipant?.media;\r\n if (!mediaState) return;\r\n\r\n const nextEnabled = !mediaState.micEnabled;\r\n\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 const nextEnabled = !mediaState.camEnabled;\r\n\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.room.id) 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.room.id!, 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\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.peerTransceivers = {};\r\n this.initiators.clear();\r\n this.pendingIceCandidates = {};\r\n\r\n this.state.resetRemoteState();\r\n }\r\n\r\n private async handleJoinApproved(msg: any) {\r\n console.log(\"JOIN_APPROVED received, sending new JOIN...\");\r\n\r\n this.events.onEntryResponded?.({\r\n participantId: msg.user_id,\r\n decision: \"approved\",\r\n });\r\n\r\n this.isWaitingForApproval = false;\r\n this.pendingRequestId = null;\r\n\r\n // Send JOIN again on SAME socket\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.send({\r\n type: \"JOIN\",\r\n room_id: this.room.id,\r\n user_id: this.myId,\r\n sender_name: this.participantName,\r\n });\r\n console.log(\"Sent new JOIN after approval\");\r\n }\r\n }\r\n\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 \"PONG\":\r\n this.lastPong = Date.now();\r\n break;\r\n\r\n case \"OFFER\":\r\n console.log(\"[Offer] Received from\", msg.sender, {\r\n sdp: msg.payload.substring(0, 200),\r\n });\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 console.log(\"[Answer] Received from\", msg.sender, {\r\n signalingState: pc?.signalingState,\r\n iceConnectionState: pc?.iceConnectionState,\r\n connectionState: pc?.connectionState,\r\n });\r\n\r\n if (!pc) return;\r\n\r\n if (pc.signalingState !== \"have-local-offer\") {\r\n console.warn(\r\n `[Signaling] Unexpected ANSWER in state \"${pc.signalingState}\", ignoring`,\r\n );\r\n return;\r\n }\r\n\r\n try {\r\n await pc.setRemoteDescription({\r\n type: \"answer\",\r\n sdp: msg.payload,\r\n });\r\n\r\n // Capture screenMid after remote description is set\r\n this.captureScreenMid(msg.sender);\r\n\r\n await this.flushIce(msg.sender, pc);\r\n } catch (err) {\r\n console.error(\"[Signaling] Failed to apply answer:\", err);\r\n this.emitError(\r\n \"ANSWER_FAILED\",\r\n `Failed to apply answer from ${msg.sender}`,\r\n err,\r\n true,\r\n );\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 \"EXISTING_USERS\":\r\n if (msg.presenterId) {\r\n this.state.setPresenterId(msg.presenterId);\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 if (p.isScreenSharing && p.remoteScreenStreamId) {\r\n console.log(\r\n `[Existing Users] ${p.name} is sharing screen (stream: ${p.remoteScreenStreamId})`,\r\n );\r\n\r\n this.state.setPresenterId(p.id);\r\n this.state.updateParticipantMedia(p.id, {\r\n isScreenSharing: true,\r\n remoteScreenStreamId: p.remoteScreenStreamId,\r\n });\r\n console.log(\r\n `[EXISTING_USERS] Pre-seeded screen state for ${p.id}, waiting for ontrack`,\r\n );\r\n }\r\n\r\n if (this.shouldInitiate(p.id)) {\r\n await this.createOffer(p.id);\r\n }\r\n }\r\n break;\r\n\r\n case \"JOINED\": {\r\n if (msg.iceServers) {\r\n this.iceServers = msg.iceServers;\r\n }\r\n this.room.name = msg.room_name;\r\n\r\n this.isWaitingForApproval = false;\r\n this.pendingRequestId = null;\r\n this.intentionalDisconnect = false;\r\n this.reconnectAttempts = 0;\r\n\r\n // Process any OFFERs that arrived before JOINED\r\n if (Object.keys(this.pendingOffers).length > 0) {\r\n console.log(\r\n \"Processing\",\r\n Object.keys(this.pendingOffers).length,\r\n \"pending offers\",\r\n );\r\n for (const [peerId, sdp] of Object.entries(this.pendingOffers)) {\r\n console.log(\"Handling queued offer from\", peerId);\r\n await this.handleOffer(sdp, peerId);\r\n }\r\n this.pendingOffers = {};\r\n }\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\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 this.events.onUserJoined?.(p);\r\n\r\n if (this.shouldInitiate(p.id)) {\r\n await this.createOffer(p.id);\r\n }\r\n\r\n break;\r\n }\r\n\r\n case \"JOIN_PENDING\": {\r\n const req = msg.request;\r\n\r\n console.log(\"JOIN_PENDING - waiting for host approval\");\r\n this.isWaitingForApproval = true;\r\n this.pendingRequestId = req.request_id;\r\n\r\n this.events.onEntryRequested?.({\r\n requestId: req.request_id,\r\n userId: req.user_id,\r\n name: req.name,\r\n });\r\n\r\n break;\r\n }\r\n\r\n case \"JOIN_REQUEST\": {\r\n const req = msg.request;\r\n\r\n console.log(\"JOIN_REQUEST - show to host for approval\");\r\n\r\n this.events.onEntryRequested?.({\r\n requestId: req.id,\r\n userId: req.user_id,\r\n name: req.name,\r\n });\r\n\r\n break;\r\n }\r\n\r\n case \"JOIN_APPROVED\": {\r\n await this.handleJoinApproved(msg);\r\n break;\r\n }\r\n\r\n case \"JOIN_REJECTED\": {\r\n const decision = \"rejected\";\r\n\r\n console.log(\"JOIN_REJECTED - user not allowed to join\");\r\n this.isWaitingForApproval = false;\r\n this.pendingRequestId = null;\r\n\r\n this.events.onEntryResponded?.({\r\n participantId: msg.user_id,\r\n decision,\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 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;\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\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 // Screen stream will arrive via ontrack on the screen transceiver\r\n this.events.onScreenShareStarted?.(peerId, 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\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 /**\r\n * Create a peer connection with pre-established transceiver layout:\r\n * - Audio transceiver (sendrecv)\r\n * - Camera video transceiver (sendrecv)\r\n * - Screen video transceiver (initially recvonly, becomes sendrecv when sharing)\r\n *\r\n * This fixed layout ensures late joiners get the screen transceiver m-line\r\n * negotiated from the very first offer, even if no one is sharing yet.\r\n */\r\n private async createPeer(id: string) {\r\n if (!this.localStream) throw new Error(\"No local stream\");\r\n if (!this.iceServers || this.iceServers.length === 0) {\r\n throw new Error(\r\n \"ICE Servers not configured. Backend must provide iceServers on JOIN.\",\r\n );\r\n }\r\n\r\n console.log(\r\n \"Creating peer connection for\",\r\n id,\r\n \"with 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: this.iceServers,\r\n });\r\n\r\n // ===== AUDIO TRANSCEIVER =====\r\n const audioTransceiver = pc.addTransceiver(\"audio\", {\r\n direction: \"sendrecv\",\r\n });\r\n const audioTrack = this.localStream.getAudioTracks()[0];\r\n if (audioTrack) {\r\n await audioTransceiver.sender.replaceTrack(audioTrack);\r\n }\r\n\r\n // ===== CAMERA VIDEO TRANSCEIVER =====\r\n const cameraTransceiver = pc.addTransceiver(\"video\", {\r\n direction: \"sendrecv\",\r\n });\r\n const videoTrack = this.localStream.getVideoTracks()[0];\r\n if (videoTrack) {\r\n await cameraTransceiver.sender.replaceTrack(videoTrack);\r\n }\r\n\r\n // ===== SCREEN VIDEO TRANSCEIVER (initially recvonly) =====\r\n // This will be present in the SDP even if we're not currently sharing.\r\n // When we start sharing, we flip direction to \"sendrecv\" and replaceTrack.\r\n const screenTransceiver = pc.addTransceiver(\"video\", {\r\n direction: this.isScreenSharing ? \"sendrecv\" : \"recvonly\",\r\n });\r\n\r\n if (this.isScreenSharing && this.screenStream) {\r\n const screenTrack = this.screenStream.getVideoTracks()[0];\r\n if (screenTrack) {\r\n await screenTransceiver.sender.replaceTrack(screenTrack);\r\n }\r\n }\r\n\r\n // Store transceiver info for later use\r\n this.peerTransceivers[id] = {\r\n cameraTransceiver,\r\n screenTransceiver,\r\n screenMid: null, // will be populated after negotiation\r\n };\r\n\r\n // ===== TRACK HANDLER =====\r\n pc.ontrack = (event) => {\r\n const transceiver = event.transceiver;\r\n const isScreenTrack =\r\n transceiver.mid === this.peerTransceivers[id]?.screenMid;\r\n\r\n console.log(\r\n `[ontrack] ${id}: kind=${event.track.kind}, mid=${transceiver.mid}, isScreen=${isScreenTrack}`,\r\n );\r\n\r\n const incomingStream =\r\n event.streams?.[0] || new MediaStream([event.track]);\r\n\r\n // Handle track unmuting\r\n if (event.track.muted) {\r\n event.track.onunmute = () => {\r\n console.log(`[ontrack] ${event.track.kind} track unmuted for ${id}`);\r\n };\r\n }\r\n\r\n if (isScreenTrack) {\r\n // Screen share stream\r\n const videoTrack =\r\n event.track.kind === \"video\"\r\n ? event.track\r\n : incomingStream.getVideoTracks()[0];\r\n\r\n this.state.updateParticipantMedia(id, {\r\n screenStream: incomingStream,\r\n screenTrack: videoTrack,\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 // Camera/normal stream\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.oniceconnectionstatechange = () => {\r\n console.log(`[ICE Connection] ${id}: ${pc.iceConnectionState}`);\r\n };\r\n\r\n pc.onconnectionstatechange = () => {\r\n console.log(`[Connection] ${id}: ${pc.connectionState}`);\r\n if (pc.connectionState === \"failed\") {\r\n try {\r\n pc.restartIce();\r\n } catch (e) {\r\n console.warn(\"Failed to restart ICE:\", e);\r\n }\r\n }\r\n };\r\n\r\n return pc;\r\n }\r\n\r\n /**\r\n * Capture the screen transceiver's MID after SDP negotiation completes.\r\n * The MID is assigned during negotiation and is stable for the life of the connection.\r\n */\r\n private captureScreenMid(peerId: string) {\r\n const pc = this.peers[peerId];\r\n if (!pc) return;\r\n\r\n const transceivers = pc.getTransceivers();\r\n const screenTransceiver = this.peerTransceivers[peerId]?.screenTransceiver;\r\n\r\n if (!screenTransceiver) return;\r\n\r\n // Find the actual mid from the negotiated transceiver\r\n const negotiatedTransceiver = transceivers.find(\r\n (t) => t === screenTransceiver,\r\n );\r\n\r\n if (negotiatedTransceiver?.mid) {\r\n this.peerTransceivers[peerId].screenMid = negotiatedTransceiver.mid;\r\n console.log(\r\n `[Negotiation] Captured screenMid for ${peerId}: ${negotiatedTransceiver.mid}`,\r\n );\r\n }\r\n }\r\n\r\n // OFFER\r\n private async createOffer(id: string, isRenegotiation = false) {\r\n if (!isRenegotiation && !this.shouldInitiate(id)) {\r\n console.debug(\r\n `[Offer] ${id} should initiate (${id} > ${this.myId}), skipping`,\r\n );\r\n return;\r\n }\r\n\r\n if (!isRenegotiation && this.initiators.has(id)) {\r\n console.debug(\r\n `[Offer] Already initiating with ${id}, skipping duplicate`,\r\n );\r\n return;\r\n }\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] = await this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n try {\r\n const offer = await pc.createOffer();\r\n await pc.setLocalDescription(offer);\r\n\r\n // Capture screenMid after local description is set\r\n this.captureScreenMid(id);\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 console.debug(`[Offer] Sent to ${id}`);\r\n } catch (err) {\r\n console.error(`[Offer] Failed for ${id}:`, err);\r\n this.emitError(\r\n \"OFFER_CREATION_FAILED\",\r\n `Failed to create offer for ${id}`,\r\n err,\r\n true,\r\n );\r\n }\r\n }\r\n\r\n private shouldInitiate(peerId: string): boolean {\r\n // Lexicographic comparison: lower ID initiates\r\n return this.myId < peerId;\r\n }\r\n\r\n // ANSWER\r\n private async handleOffer(sdp: string, id: string) {\r\n if (!this.iceServers || this.iceServers.length === 0) {\r\n console.warn(\"[Offer] Waiting for iceServers, queuing offer from\", id);\r\n this.pendingOffers[id] = sdp;\r\n return;\r\n }\r\n\r\n if (!this.peers[id]) {\r\n this.peers[id] = await this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n try {\r\n // GLARE RECOVERY: Both peers sent OFFERs simultaneously\r\n if (pc.signalingState === \"have-local-offer\") {\r\n if (this.shouldInitiate(id)) {\r\n // WE WIN: Keep our offer, reject theirs\r\n console.warn(\r\n `[Glare] Both sent OFFERs, we win (${this.myId} < ${id}), keeping our OFFER`,\r\n );\r\n return; // Ignore their offer, wait for their ANSWER\r\n } else {\r\n // THEY WIN: Roll back and accept their offer\r\n console.warn(\r\n `[Glare] Both sent OFFERs, they win (${id} < ${this.myId}), rolling back`,\r\n );\r\n pc.close();\r\n delete this.peers[id];\r\n delete this.peerTransceivers[id];\r\n this.initiators.delete(id);\r\n\r\n // Create fresh peer connection to answer their offer\r\n this.peers[id] = await this.createPeer(id);\r\n }\r\n }\r\n\r\n // Only set remote description if we're in a valid state\r\n if (\r\n this.peers[id].signalingState !== \"stable\" &&\r\n this.peers[id].signalingState !== \"have-local-offer\"\r\n ) {\r\n console.warn(\r\n `[Signaling] Cannot accept OFFER in state \"${this.peers[id].signalingState}\"`,\r\n );\r\n return;\r\n }\r\n\r\n await this.peers[id].setRemoteDescription({\r\n type: \"offer\",\r\n sdp,\r\n });\r\n\r\n // Capture screenMid after remote description is set\r\n this.captureScreenMid(id);\r\n\r\n const pending = this.pendingIceCandidates[id] || [];\r\n\r\n for (const candidate of pending) {\r\n try {\r\n await this.peers[id].addIceCandidate(candidate);\r\n } catch (err) {\r\n console.warn(\"[ICE] Failed to add candidate:\", err);\r\n }\r\n }\r\n\r\n delete this.pendingIceCandidates[id];\r\n\r\n const answer = await this.peers[id].createAnswer();\r\n\r\n await this.peers[id].setLocalDescription(answer);\r\n await this.flushIce(id, this.peers[id]);\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 console.debug(`[Answer] Sent to ${id}`);\r\n } catch (err) {\r\n console.error(`[Signaling] Failed to handle OFFER from ${id}:`, err);\r\n this.emitError(\r\n \"OFFER_HANDLING_FAILED\",\r\n `Failed to handle offer from ${id}`,\r\n err,\r\n true,\r\n );\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 pc.oniceconnectionstatechange = null;\r\n\r\n pc.close();\r\n\r\n delete this.peers[id];\r\n delete this.peerTransceivers[id];\r\n\r\n this.initiators.delete(id);\r\n\r\n this.state.removeParticipant(id);\r\n }\r\n\r\n // SCREEN SHARE (TRANSCEIVER-BASED)\r\n /**\r\n * Start screen sharing using replaceTrack on the pre-established screen transceiver.\r\n * No need to add/remove tracks, no renegotiation needed (transceiver already in SDP).\r\n * Just swap the track and update direction if needed.\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 });\r\n\r\n const screenTrack = this.screenStream.getVideoTracks()[0];\r\n if (!screenTrack) {\r\n throw new Error(\"No video track in screen stream\");\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: screenTrack,\r\n },\r\n });\r\n\r\n this.state.setPresenterId(this.myId);\r\n\r\n // Handle user clicking browser's \"Stop Sharing\" button\r\n screenTrack.onended = () => {\r\n console.log(\"[Screen Share] User stopped via browser button\");\r\n this.stopScreenShare();\r\n };\r\n\r\n // Update all existing peer connections: use replaceTrack on screen transceiver\r\n for (const [peerId, pc] of Object.entries(this.peers)) {\r\n const txInfo = this.peerTransceivers[peerId];\r\n if (!txInfo) {\r\n console.warn(\r\n `[Screen Share] No transceiver info for ${peerId}, skipping`,\r\n );\r\n continue;\r\n }\r\n\r\n try {\r\n // Replace the track on the screen transceiver sender\r\n await txInfo.screenTransceiver.sender.replaceTrack(screenTrack);\r\n\r\n // Flip direction from recvonly to sendrecv if needed\r\n if (txInfo.screenTransceiver.currentDirection === \"recvonly\") {\r\n txInfo.screenTransceiver.direction = \"sendrecv\";\r\n console.log(\r\n `[Screen Share] Flipped ${peerId} screen transceiver to sendrecv`,\r\n );\r\n\r\n // Trigger renegotiation for the direction change\r\n await this.createOffer(peerId, true);\r\n }\r\n } catch (err) {\r\n console.error(\r\n `[Screen Share] Failed to update transceiver for ${peerId}:`,\r\n err,\r\n );\r\n }\r\n }\r\n\r\n this.send({\r\n type: \"SCREEN_SHARE_START\",\r\n sender: this.myId,\r\n room_id: this.room.id,\r\n stream_id: this.screenStream.id.replace(/[{}]/g, \"\"),\r\n });\r\n\r\n console.log(\"[Screen Share] Started successfully\");\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 async stopScreenShare() {\r\n if (!this.screenStream) return;\r\n\r\n console.log(\"[Screen Share] Stopping...\");\r\n\r\n // Stop all tracks in the screen stream\r\n this.screenStream.getTracks().forEach((t) => t.stop());\r\n\r\n // Update all peer connections: clear track and flip direction back to recvonly\r\n for (const [peerId, pc] of Object.entries(this.peers)) {\r\n const txInfo = this.peerTransceivers[peerId];\r\n if (!txInfo) continue;\r\n\r\n try {\r\n // Clear the screen transceiver track\r\n await txInfo.screenTransceiver.sender.replaceTrack(null);\r\n\r\n // Flip direction back to recvonly\r\n if (txInfo.screenTransceiver.currentDirection === \"sendrecv\") {\r\n txInfo.screenTransceiver.direction = \"recvonly\";\r\n console.log(\r\n `[Screen Share] Flipped ${peerId} screen transceiver to recvonly`,\r\n );\r\n\r\n // Trigger renegotiation for the direction change\r\n await this.createOffer(peerId, true);\r\n }\r\n } catch (err) {\r\n console.error(\r\n `[Screen Share] Failed to clear transceiver for ${peerId}:`,\r\n err,\r\n );\r\n }\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.room.id,\r\n });\r\n\r\n console.log(\"[Screen Share] Stopped\");\r\n }\r\n\r\n // CHAT\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.room.id) {\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.room.id,\r\n target: isPrivate ? (payload.target ?? null) : null,\r\n reply_to: payload.reply_to ?? null,\r\n client_ts: Date.now(),\r\n });\r\n }\r\n\r\n // DISCONNECT\r\n disconnect() {\r\n this.intentionalDisconnect = true;\r\n\r\n this.stopScreenShare();\r\n\r\n Object.values(this.peers).forEach((pc) => pc.close());\r\n this.peers = {};\r\n this.peerTransceivers = {};\r\n this.initiators.clear();\r\n\r\n this.stopHeartbeat();\r\n\r\n if (this.ws?.readyState === WebSocket.OPEN) {\r\n this.send({\r\n type: \"LEAVE\",\r\n room_id: this.room.id,\r\n user_id: this.myId,\r\n sender_name: this.state.localParticipant?.name,\r\n });\r\n\r\n // Allow the LEAVE frame to be flushed.\r\n setTimeout(() => {\r\n this.ws?.close(1000, \"Leaving meeting\");\r\n this.ws = null;\r\n }, 50);\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.room.id = null;\r\n\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.events.onMeetingLeft?.();\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 approveJoinRequest(requestId: string) {\r\n this.send({\r\n type: \"JOIN_APPROVE\",\r\n request_id: requestId,\r\n });\r\n }\r\n\r\n rejectJoinRequest(requestId: string) {\r\n this.send({\r\n type: \"JOIN_REJECT\",\r\n request_id: requestId,\r\n });\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?: {\r\n onError?: (err: any) => void;\r\n onEntryRequested?: (req: any) => void;\r\n onEntryResponded?: (payload: any, decision?: any) => void;\r\n onMeetingLeft?: () => void;\r\n}) => {\r\n const ctx = useMeetingContext();\r\n\r\n // Error Handler\r\n useEffect(() => {\r\n if (!handlers?.onError) return;\r\n return ctx.onError(handlers.onError);\r\n }, [handlers?.onError]);\r\n\r\n // Entry Request Handler\r\n useEffect(() => {\r\n if (!handlers?.onEntryRequested) return;\r\n return ctx.onEntryRequested(handlers.onEntryRequested);\r\n }, [handlers?.onEntryRequested]);\r\n\r\n // Entry Response Handler\r\n useEffect(() => {\r\n if (!handlers?.onEntryResponded) return;\r\n return ctx.onEntryResponded(handlers.onEntryResponded);\r\n }, [handlers?.onEntryResponded]);\r\n\r\n // Meeting Left Handler\r\n useEffect(() => {\r\n if (!handlers?.onMeetingLeft) return;\r\n return ctx.onMeetingLeft(handlers.onMeetingLeft);\r\n }, [handlers?.onMeetingLeft]);\r\n\r\n const { sdk: _, ...publicApi } = ctx;\r\n return publicApi;\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 const videoRef = useRef<HTMLVideoElement | null>(null);\r\n const audioRef = useRef<HTMLAudioElement | null>(null);\r\n\r\n const [participant, setParticipant] = useState<Participant | null>(\r\n () => sdk.state.getParticipant(participantId) || null,\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\r\n return unsub;\r\n }, [participantId, sdk]);\r\n\r\n useEffect(() => {\r\n const stream = participant?.media?.stream;\r\n if (!stream) return;\r\n\r\n // VIDEO\r\n if (videoRef.current) {\r\n videoRef.current.srcObject = stream;\r\n videoRef.current.muted = true;\r\n videoRef.current.playsInline = true;\r\n videoRef.current.play().catch(() => {});\r\n }\r\n\r\n // AUDIO\r\n if (audioRef.current) {\r\n audioRef.current.srcObject = stream;\r\n audioRef.current.play().catch(() => {});\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","videoTrack","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 room: { id: string | null; name: 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 approveJoinRequest: (requestId: string) => void;\r\n rejectJoinRequest: (requestId: string) => void;\r\n onError: (cb: (err: any) => void) => () => void;\r\n onEntryRequested: (cb: (req: any) => void) => () => void;\r\n onEntryResponded: (cb: (payload: any, decision?: any) => void) => () => void;\r\n onMeetingLeft: (cb: () => 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 const entryRequestListeners = useRef(new Set<(req: any) => void>());\r\n const entryResponseListeners = useRef(\r\n new Set<(payload: any, decision?: any) => void>(),\r\n );\r\n const meetingLeftListeners = useRef(new Set<() => void>());\r\n\r\n if (!sdkRef.current) {\r\n sdkRef.current = new VideoSDKCore({\r\n onError: (err) => errorListeners.current.forEach((fn) => fn(err)),\r\n onEntryRequested: (req) =>\r\n entryRequestListeners.current.forEach((fn) => fn(req)),\r\n onEntryResponded: (p, d) =>\r\n entryResponseListeners.current.forEach((fn) => fn(p, d)),\r\n onMeetingLeft: () => meetingLeftListeners.current.forEach((fn) => fn()),\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 room: sdk.getMeeting(),\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 approveJoinRequest: sdk.approveJoinRequest.bind(sdk),\r\n rejectJoinRequest: sdk.rejectJoinRequest.bind(sdk),\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 onEntryRequested: (cb: (err: any) => void) => {\r\n entryRequestListeners.current.add(cb);\r\n\r\n return () => {\r\n entryRequestListeners.current.delete(cb);\r\n };\r\n },\r\n onEntryResponded: (cb: (err: any) => void) => {\r\n entryResponseListeners.current.add(cb);\r\n\r\n return () => {\r\n entryResponseListeners.current.delete(cb);\r\n };\r\n },\r\n onMeetingLeft: (cb: () => void) => {\r\n meetingLeftListeners.current.add(cb);\r\n return () => {\r\n meetingLeftListeners.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 private lastPong = Date.now();\r\n private iceServers: RTCIceServer[] = [];\r\n private intentionalDisconnect = false;\r\n\r\n private myId: string;\r\n private room: { id: string | null; name: string | null } = {\r\n id: null,\r\n name: null,\r\n };\r\n private localStream: MediaStream | null = null;\r\n private screenStream: MediaStream | null = null;\r\n private isScreenSharing = false;\r\n\r\n // Transceiver-based tracking: maps peerId -> { cameraTransceiver, screenTransceiver, screenMid }\r\n private peerTransceivers: Record<\r\n string,\r\n {\r\n cameraTransceiver: RTCRtpTransceiver;\r\n screenTransceiver: RTCRtpTransceiver;\r\n screenMid: string | null; // set after negotiation completes\r\n }\r\n > = {};\r\n\r\n private pingInterval: any = null;\r\n private pendingIceCandidates: Record<string, RTCIceCandidateInit[]> = {};\r\n private pendingOffers: Record<string, string> = {};\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\r\n // Track if we're in the waiting room (pending approval)\r\n private isWaitingForApproval = false;\r\n private pendingRequestId: string | null = null;\r\n\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.room.id,\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 try {\r\n this.localStream = await navigator.mediaDevices.getUserMedia({\r\n video: {\r\n width: { ideal: 1280 },\r\n height: { ideal: 720 },\r\n },\r\n audio: {\r\n echoCancellation: true,\r\n noiseSuppression: true,\r\n autoGainControl: true,\r\n },\r\n });\r\n\r\n // Verify tracks are actually live BEFORE connecting\r\n const hasVideo = this.localStream\r\n .getVideoTracks()\r\n .some((t) => t.readyState === \"live\");\r\n const hasAudio = this.localStream\r\n .getAudioTracks()\r\n .some((t) => t.readyState === \"live\");\r\n\r\n if (!hasVideo || !hasAudio) {\r\n throw new Error(`Missing tracks: video=${hasVideo}, audio=${hasAudio}`);\r\n }\r\n\r\n video.srcObject = this.localStream;\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 } catch (err: any) {\r\n this.emitError(\"GET_USER_MEDIA_FAILED\", err?.message, err, false);\r\n throw err;\r\n }\r\n }\r\n\r\n // CONNECT\r\n async connect(roomId: string, name: string) {\r\n this.room.id = 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 console.log(\"WebSocket connected, sending JOIN...\");\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.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 if (this.intentionalDisconnect) {\r\n return; // do NOT reconnect\r\n }\r\n\r\n if (e.code === 1000 || e.code === 1001) {\r\n return;\r\n }\r\n\r\n // Don't auto-reconnect if waiting for approval (user must reconnect after approval)\r\n if (this.isWaitingForApproval) {\r\n console.log(\"Waiting for approval, not auto-reconnecting\");\r\n return;\r\n }\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 getMeeting(): { id: string | null; name: string | null } {\r\n return this.room;\r\n }\r\n\r\n toggleMic() {\r\n const mediaState = this.state.localParticipant?.media;\r\n if (!mediaState) return;\r\n\r\n const nextEnabled = !mediaState.micEnabled;\r\n\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 const nextEnabled = !mediaState.camEnabled;\r\n\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.room.id) 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.room.id!, 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\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.peerTransceivers = {};\r\n this.initiators.clear();\r\n this.pendingIceCandidates = {};\r\n\r\n this.state.resetRemoteState();\r\n }\r\n\r\n private async handleJoinApproved(msg: any) {\r\n console.log(\"JOIN_APPROVED received, sending new JOIN...\");\r\n\r\n this.events.onEntryResponded?.({\r\n participantId: msg.user_id,\r\n decision: \"approved\",\r\n });\r\n\r\n this.isWaitingForApproval = false;\r\n this.pendingRequestId = null;\r\n\r\n // Send JOIN again on SAME socket\r\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\r\n this.send({\r\n type: \"JOIN\",\r\n room_id: this.room.id,\r\n user_id: this.myId,\r\n sender_name: this.participantName,\r\n });\r\n console.log(\"Sent new JOIN after approval\");\r\n }\r\n }\r\n\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 \"PONG\":\r\n this.lastPong = Date.now();\r\n break;\r\n\r\n case \"OFFER\":\r\n console.log(\"[Offer] Received from\", msg.sender, {\r\n sdp: msg.payload.substring(0, 200),\r\n });\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 console.log(\"[Answer] Received from\", msg.sender, {\r\n signalingState: pc?.signalingState,\r\n iceConnectionState: pc?.iceConnectionState,\r\n connectionState: pc?.connectionState,\r\n });\r\n\r\n if (!pc) return;\r\n\r\n if (pc.signalingState !== \"have-local-offer\") {\r\n console.warn(\r\n `[Signaling] Unexpected ANSWER in state \"${pc.signalingState}\", ignoring`,\r\n );\r\n return;\r\n }\r\n\r\n try {\r\n await pc.setRemoteDescription({\r\n type: \"answer\",\r\n sdp: msg.payload,\r\n });\r\n\r\n // Capture screenMid after remote description is set\r\n this.captureScreenMid(msg.sender);\r\n\r\n await this.flushIce(msg.sender, pc);\r\n } catch (err) {\r\n console.error(\"[Signaling] Failed to apply answer:\", err);\r\n this.emitError(\r\n \"ANSWER_FAILED\",\r\n `Failed to apply answer from ${msg.sender}`,\r\n err,\r\n true,\r\n );\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 \"EXISTING_USERS\":\r\n if (msg.presenterId) {\r\n this.state.setPresenterId(msg.presenterId);\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 if (p.isScreenSharing && p.remoteScreenStreamId) {\r\n console.log(\r\n `[Existing Users] ${p.name} is sharing screen (stream: ${p.remoteScreenStreamId})`,\r\n );\r\n\r\n this.state.setPresenterId(p.id);\r\n this.state.updateParticipantMedia(p.id, {\r\n isScreenSharing: true,\r\n remoteScreenStreamId: p.remoteScreenStreamId,\r\n });\r\n console.log(\r\n `[EXISTING_USERS] Pre-seeded screen state for ${p.id}, waiting for ontrack`,\r\n );\r\n }\r\n\r\n if (this.shouldInitiate(p.id)) {\r\n await this.createOffer(p.id);\r\n }\r\n }\r\n break;\r\n\r\n case \"JOINED\": {\r\n if (msg.iceServers) {\r\n this.iceServers = msg.iceServers;\r\n }\r\n this.room.name = msg.room_name;\r\n\r\n this.isWaitingForApproval = false;\r\n this.pendingRequestId = null;\r\n this.intentionalDisconnect = false;\r\n this.reconnectAttempts = 0;\r\n\r\n // Process any OFFERs that arrived before JOINED\r\n if (Object.keys(this.pendingOffers).length > 0) {\r\n console.log(\r\n \"Processing\",\r\n Object.keys(this.pendingOffers).length,\r\n \"pending offers\",\r\n );\r\n for (const [peerId, sdp] of Object.entries(this.pendingOffers)) {\r\n console.log(\"Handling queued offer from\", peerId);\r\n await this.handleOffer(sdp, peerId);\r\n }\r\n this.pendingOffers = {};\r\n }\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\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 this.events.onUserJoined?.(p);\r\n\r\n if (this.shouldInitiate(p.id)) {\r\n await this.createOffer(p.id);\r\n }\r\n\r\n break;\r\n }\r\n\r\n case \"JOIN_PENDING\": {\r\n const req = msg.request;\r\n\r\n console.log(\"JOIN_PENDING - waiting for host approval\");\r\n this.isWaitingForApproval = true;\r\n this.pendingRequestId = req.request_id;\r\n\r\n this.events.onEntryRequested?.({\r\n requestId: req.request_id,\r\n userId: req.user_id,\r\n name: req.name,\r\n });\r\n\r\n break;\r\n }\r\n\r\n case \"JOIN_REQUEST\": {\r\n const req = msg.request;\r\n\r\n console.log(\"JOIN_REQUEST - show to host for approval\");\r\n\r\n this.events.onEntryRequested?.({\r\n requestId: req.id,\r\n userId: req.user_id,\r\n name: req.name,\r\n });\r\n\r\n break;\r\n }\r\n\r\n case \"JOIN_APPROVED\": {\r\n await this.handleJoinApproved(msg);\r\n break;\r\n }\r\n\r\n case \"JOIN_REJECTED\": {\r\n const decision = \"rejected\";\r\n\r\n console.log(\"JOIN_REJECTED - user not allowed to join\");\r\n this.isWaitingForApproval = false;\r\n this.pendingRequestId = null;\r\n\r\n this.events.onEntryResponded?.({\r\n participantId: msg.user_id,\r\n decision,\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 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;\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\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 // Screen stream will arrive via ontrack on the screen transceiver\r\n this.events.onScreenShareStarted?.(peerId, 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\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 private async createPeer(id: string) {\r\n if (!this.localStream) throw new Error(\"No local stream\");\r\n if (!this.iceServers || this.iceServers.length === 0) {\r\n throw new Error(\r\n \"ICE Servers not configured. Backend must provide iceServers on JOIN.\",\r\n );\r\n }\r\n\r\n console.log(\r\n \"Creating peer connection for\",\r\n id,\r\n \"with 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: this.iceServers,\r\n });\r\n\r\n // AUDIO TRANSCEIVER\r\n const audioTransceiver = pc.addTransceiver(\"audio\", {\r\n direction: \"sendrecv\",\r\n });\r\n const audioTrack = this.localStream.getAudioTracks()[0];\r\n if (audioTrack) {\r\n await audioTransceiver.sender.replaceTrack(audioTrack);\r\n }\r\n\r\n // CAMERA VIDEO TRANSCEIVER\r\n const cameraTransceiver = pc.addTransceiver(\"video\", {\r\n direction: \"sendrecv\",\r\n });\r\n const videoTrack = this.localStream.getVideoTracks()[0];\r\n if (videoTrack) {\r\n await cameraTransceiver.sender.replaceTrack(videoTrack);\r\n }\r\n\r\n const screenTransceiver = pc.addTransceiver(\"video\", {\r\n direction: this.isScreenSharing ? \"sendrecv\" : \"recvonly\",\r\n });\r\n\r\n if (this.isScreenSharing && this.screenStream) {\r\n const screenTrack = this.screenStream.getVideoTracks()[0];\r\n if (screenTrack) {\r\n await screenTransceiver.sender.replaceTrack(screenTrack);\r\n }\r\n }\r\n\r\n // Store transceiver info for later use\r\n this.peerTransceivers[id] = {\r\n cameraTransceiver,\r\n screenTransceiver,\r\n screenMid: null, // will be populated after negotiation\r\n };\r\n\r\n // TRACK HANDLER\r\n pc.ontrack = (event) => {\r\n const transceiver = event.transceiver;\r\n // Compare transceiver object reference, not mid (mid may not be assigned yet)\r\n const isScreenTrack =\r\n transceiver === this.peerTransceivers[id]?.screenTransceiver;\r\n\r\n console.log(\r\n `[ontrack] ${id}: kind=${event.track.kind}, mid=${transceiver.mid}, isScreen=${isScreenTrack}`,\r\n );\r\n\r\n const incomingStream =\r\n event.streams?.[0] || new MediaStream([event.track]);\r\n\r\n // Handle track unmuting\r\n if (event.track.muted) {\r\n event.track.onunmute = () => {\r\n console.log(`[ontrack] ${event.track.kind} track unmuted for ${id}`);\r\n };\r\n }\r\n\r\n if (isScreenTrack) {\r\n // Screen share stream\r\n const videoTrack =\r\n event.track.kind === \"video\"\r\n ? event.track\r\n : incomingStream.getVideoTracks()[0];\r\n\r\n this.state.updateParticipantMedia(id, {\r\n screenStream: incomingStream,\r\n screenTrack: videoTrack,\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 // Camera/normal stream\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.oniceconnectionstatechange = () => {\r\n console.log(`[ICE Connection] ${id}: ${pc.iceConnectionState}`);\r\n };\r\n\r\n pc.onconnectionstatechange = () => {\r\n console.log(`[Connection] ${id}: ${pc.connectionState}`);\r\n if (pc.connectionState === \"failed\") {\r\n try {\r\n pc.restartIce();\r\n } catch (e) {\r\n console.warn(\"Failed to restart ICE:\", e);\r\n }\r\n }\r\n };\r\n\r\n return pc;\r\n }\r\n\r\n /**\r\n * Capture the screen transceiver's MID after SDP negotiation completes.\r\n * The MID is assigned during negotiation and is stable for the life of the connection.\r\n */\r\n private captureScreenMid(peerId: string) {\r\n const pc = this.peers[peerId];\r\n if (!pc) return;\r\n\r\n const transceivers = pc.getTransceivers();\r\n const screenTransceiver = this.peerTransceivers[peerId]?.screenTransceiver;\r\n\r\n if (!screenTransceiver) return;\r\n\r\n // Find the actual mid from the negotiated transceiver\r\n const negotiatedTransceiver = transceivers.find(\r\n (t) => t === screenTransceiver,\r\n );\r\n\r\n if (negotiatedTransceiver?.mid) {\r\n this.peerTransceivers[peerId].screenMid = negotiatedTransceiver.mid;\r\n console.log(\r\n `[Negotiation] Captured screenMid for ${peerId}: ${negotiatedTransceiver.mid}`,\r\n );\r\n }\r\n }\r\n\r\n // OFFER\r\n private async createOffer(id: string, isRenegotiation = false) {\r\n if (!isRenegotiation && !this.shouldInitiate(id)) {\r\n console.debug(\r\n `[Offer] ${id} should initiate (${id} > ${this.myId}), skipping`,\r\n );\r\n return;\r\n }\r\n\r\n if (!isRenegotiation && this.initiators.has(id)) {\r\n console.debug(\r\n `[Offer] Already initiating with ${id}, skipping duplicate`,\r\n );\r\n return;\r\n }\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] = await this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n try {\r\n const offer = await pc.createOffer();\r\n await pc.setLocalDescription(offer);\r\n\r\n // Capture screenMid after local description is set\r\n this.captureScreenMid(id);\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 console.debug(`[Offer] Sent to ${id}`);\r\n } catch (err) {\r\n console.error(`[Offer] Failed for ${id}:`, err);\r\n this.emitError(\r\n \"OFFER_CREATION_FAILED\",\r\n `Failed to create offer for ${id}`,\r\n err,\r\n true,\r\n );\r\n }\r\n }\r\n\r\n private shouldInitiate(peerId: string): boolean {\r\n // Lexicographic comparison: lower ID initiates\r\n return this.myId < peerId;\r\n }\r\n\r\n // ANSWER\r\n private async handleOffer(sdp: string, id: string) {\r\n if (!this.iceServers || this.iceServers.length === 0) {\r\n console.warn(\"[Offer] Waiting for iceServers, queuing offer from\", id);\r\n this.pendingOffers[id] = sdp;\r\n return;\r\n }\r\n\r\n if (!this.peers[id]) {\r\n this.peers[id] = await this.createPeer(id);\r\n }\r\n\r\n const pc = this.peers[id];\r\n\r\n try {\r\n // GLARE RECOVERY: Both peers sent OFFERs simultaneously\r\n if (pc.signalingState === \"have-local-offer\") {\r\n if (this.shouldInitiate(id)) {\r\n // WE WIN: Keep our offer, reject theirs\r\n console.warn(\r\n `[Glare] Both sent OFFERs, we win (${this.myId} < ${id}), keeping our OFFER`,\r\n );\r\n return; // Ignore their offer, wait for their ANSWER\r\n } else {\r\n // THEY WIN: Roll back and accept their offer\r\n console.warn(\r\n `[Glare] Both sent OFFERs, they win (${id} < ${this.myId}), rolling back`,\r\n );\r\n pc.close();\r\n delete this.peers[id];\r\n delete this.peerTransceivers[id];\r\n this.initiators.delete(id);\r\n\r\n // Create fresh peer connection to answer their offer\r\n this.peers[id] = await this.createPeer(id);\r\n }\r\n }\r\n\r\n // Only set remote description if we're in a valid state\r\n if (\r\n this.peers[id].signalingState !== \"stable\" &&\r\n this.peers[id].signalingState !== \"have-local-offer\"\r\n ) {\r\n console.warn(\r\n `[Signaling] Cannot accept OFFER in state \"${this.peers[id].signalingState}\"`,\r\n );\r\n return;\r\n }\r\n\r\n await this.peers[id].setRemoteDescription({\r\n type: \"offer\",\r\n sdp,\r\n });\r\n\r\n // Capture screenMid after remote description is set\r\n this.captureScreenMid(id);\r\n\r\n const pending = this.pendingIceCandidates[id] || [];\r\n\r\n for (const candidate of pending) {\r\n try {\r\n await this.peers[id].addIceCandidate(candidate);\r\n } catch (err) {\r\n console.warn(\"[ICE] Failed to add candidate:\", err);\r\n }\r\n }\r\n\r\n delete this.pendingIceCandidates[id];\r\n\r\n const answer = await this.peers[id].createAnswer();\r\n\r\n await this.peers[id].setLocalDescription(answer);\r\n await this.flushIce(id, this.peers[id]);\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 console.debug(`[Answer] Sent to ${id}`);\r\n } catch (err) {\r\n console.error(`[Signaling] Failed to handle OFFER from ${id}:`, err);\r\n this.emitError(\r\n \"OFFER_HANDLING_FAILED\",\r\n `Failed to handle offer from ${id}`,\r\n err,\r\n true,\r\n );\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 pc.oniceconnectionstatechange = null;\r\n\r\n pc.close();\r\n\r\n delete this.peers[id];\r\n delete this.peerTransceivers[id];\r\n\r\n this.initiators.delete(id);\r\n\r\n this.state.removeParticipant(id);\r\n }\r\n\r\n // SCREEN SHARE (TRANSCEIVER-BASED)\r\n /**\r\n * Start screen sharing using replaceTrack on the pre-established screen transceiver.\r\n * No need to add/remove tracks, no renegotiation needed (transceiver already in SDP).\r\n * Just swap the track and update direction if needed.\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 });\r\n\r\n const screenTrack = this.screenStream.getVideoTracks()[0];\r\n if (!screenTrack) {\r\n throw new Error(\"No video track in screen stream\");\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: screenTrack,\r\n },\r\n });\r\n\r\n this.state.setPresenterId(this.myId);\r\n\r\n // Handle user clicking browser's \"Stop Sharing\" button\r\n screenTrack.onended = () => {\r\n console.log(\"[Screen Share] User stopped via browser button\");\r\n this.stopScreenShare();\r\n };\r\n\r\n // Update all existing peer connections: use replaceTrack on screen transceiver\r\n for (const [peerId, pc] of Object.entries(this.peers)) {\r\n const txInfo = this.peerTransceivers[peerId];\r\n if (!txInfo) {\r\n console.warn(\r\n `[Screen Share] No transceiver info for ${peerId}, skipping`,\r\n );\r\n continue;\r\n }\r\n\r\n try {\r\n // Replace the track on the screen transceiver sender\r\n await txInfo.screenTransceiver.sender.replaceTrack(screenTrack);\r\n\r\n // Flip direction from recvonly to sendrecv if needed\r\n if (txInfo.screenTransceiver.currentDirection === \"recvonly\") {\r\n txInfo.screenTransceiver.direction = \"sendrecv\";\r\n console.log(\r\n `[Screen Share] Flipped ${peerId} screen transceiver to sendrecv`,\r\n );\r\n\r\n // Trigger renegotiation for the direction change\r\n await this.createOffer(peerId, true);\r\n }\r\n } catch (err) {\r\n console.error(\r\n `[Screen Share] Failed to update transceiver for ${peerId}:`,\r\n err,\r\n );\r\n }\r\n }\r\n\r\n this.send({\r\n type: \"SCREEN_SHARE_START\",\r\n sender: this.myId,\r\n room_id: this.room.id,\r\n stream_id: this.screenStream.id.replace(/[{}]/g, \"\"),\r\n });\r\n\r\n console.log(\"[Screen Share] Started successfully\");\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 /**\r\n * Stop screen sharing: clear the screen transceiver track and flip direction back to recvonly.\r\n */\r\n async stopScreenShare() {\r\n if (!this.screenStream) return;\r\n\r\n console.log(\"[Screen Share] Stopping...\");\r\n\r\n // Stop all tracks in the screen stream\r\n this.screenStream.getTracks().forEach((t) => t.stop());\r\n\r\n // Update all peer connections: clear track and flip direction back to recvonly\r\n for (const [peerId, pc] of Object.entries(this.peers)) {\r\n const txInfo = this.peerTransceivers[peerId];\r\n if (!txInfo) continue;\r\n\r\n try {\r\n // Clear the screen transceiver track\r\n await txInfo.screenTransceiver.sender.replaceTrack(null);\r\n\r\n // Flip direction back to recvonly\r\n if (txInfo.screenTransceiver.currentDirection === \"sendrecv\") {\r\n txInfo.screenTransceiver.direction = \"recvonly\";\r\n console.log(\r\n `[Screen Share] Flipped ${peerId} screen transceiver to recvonly`,\r\n );\r\n\r\n // Trigger renegotiation for the direction change\r\n await this.createOffer(peerId, true);\r\n }\r\n } catch (err) {\r\n console.error(\r\n `[Screen Share] Failed to clear transceiver for ${peerId}:`,\r\n err,\r\n );\r\n }\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.room.id,\r\n });\r\n\r\n console.log(\"[Screen Share] Stopped\");\r\n }\r\n\r\n // CHAT\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.room.id) {\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.room.id,\r\n target: isPrivate ? (payload.target ?? null) : null,\r\n reply_to: payload.reply_to ?? null,\r\n client_ts: Date.now(),\r\n });\r\n }\r\n\r\n // DISCONNECT\r\n async disconnect() {\r\n this.intentionalDisconnect = true;\r\n\r\n await this.stopScreenShare();\r\n\r\n Object.values(this.peers).forEach((pc) => pc.close());\r\n this.peers = {};\r\n this.peerTransceivers = {};\r\n this.initiators.clear();\r\n\r\n this.stopHeartbeat();\r\n\r\n if (this.ws?.readyState === WebSocket.OPEN) {\r\n this.send({\r\n type: \"LEAVE\",\r\n room_id: this.room.id,\r\n user_id: this.myId,\r\n sender_name: this.state.localParticipant?.name,\r\n });\r\n\r\n // Allow the LEAVE frame to be flushed.\r\n setTimeout(() => {\r\n this.ws?.close(1000, \"Leaving meeting\");\r\n this.ws = null;\r\n }, 50);\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.room.id = null;\r\n\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.events.onMeetingLeft?.();\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 approveJoinRequest(requestId: string) {\r\n this.send({\r\n type: \"JOIN_APPROVE\",\r\n request_id: requestId,\r\n });\r\n }\r\n\r\n rejectJoinRequest(requestId: string) {\r\n this.send({\r\n type: \"JOIN_REJECT\",\r\n request_id: requestId,\r\n });\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?: {\r\n onError?: (err: any) => void;\r\n onEntryRequested?: (req: any) => void;\r\n onEntryResponded?: (payload: any, decision?: any) => void;\r\n onMeetingLeft?: () => void;\r\n}) => {\r\n const ctx = useMeetingContext();\r\n\r\n // Error Handler\r\n useEffect(() => {\r\n if (!handlers?.onError) return;\r\n return ctx.onError(handlers.onError);\r\n }, [handlers?.onError]);\r\n\r\n // Entry Request Handler\r\n useEffect(() => {\r\n if (!handlers?.onEntryRequested) return;\r\n return ctx.onEntryRequested(handlers.onEntryRequested);\r\n }, [handlers?.onEntryRequested]);\r\n\r\n // Entry Response Handler\r\n useEffect(() => {\r\n if (!handlers?.onEntryResponded) return;\r\n return ctx.onEntryResponded(handlers.onEntryResponded);\r\n }, [handlers?.onEntryResponded]);\r\n\r\n // Meeting Left Handler\r\n useEffect(() => {\r\n if (!handlers?.onMeetingLeft) return;\r\n return ctx.onMeetingLeft(handlers.onMeetingLeft);\r\n }, [handlers?.onMeetingLeft]);\r\n\r\n const { sdk: _, ...publicApi } = ctx;\r\n return publicApi;\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 const videoRef = useRef<HTMLVideoElement | null>(null);\r\n const audioRef = useRef<HTMLAudioElement | null>(null);\r\n\r\n const [participant, setParticipant] = useState<Participant | null>(\r\n () => sdk.state.getParticipant(participantId) || null,\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\r\n return unsub;\r\n }, [participantId, sdk]);\r\n\r\n useEffect(() => {\r\n const stream = participant?.media?.stream;\r\n if (!stream) return;\r\n\r\n // VIDEO\r\n if (videoRef.current) {\r\n videoRef.current.srcObject = stream;\r\n videoRef.current.muted = true;\r\n videoRef.current.playsInline = true;\r\n videoRef.current.play().catch(() => {});\r\n }\r\n\r\n // AUDIO\r\n if (audioRef.current) {\r\n audioRef.current.srcObject = stream;\r\n audioRef.current.play().catch(() => {});\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","videoTrack","useState","useEffect","useRef","useEffect","useEffect","useEffect","useState","useState","useEffect","useEffect","useRef","useState","useRef","useState","useEffect"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@afosecure/meetingsdk",
3
- "version": "1.3.1",
3
+ "version": "1.3.2",
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",