@defcomm/meetingsdk 0.2.3 → 0.2.5

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
@@ -259,6 +259,7 @@ interface SFUClientOptions {
259
259
  onParticipantJoined?: (participant: ParticipantInfo) => void;
260
260
  onParticipantLeft?: (participant: string | ParticipantInfo) => void;
261
261
  onParticipantPresence?: (participantId: string, presence: string) => void;
262
+ onParticipantMediaState?: (participantId: string, audioMuted: boolean, videoMuted: boolean) => void;
262
263
  onTrackPublished?: (track: TrackInfo) => void;
263
264
  onTrackUnpublished?: (track: TrackInfo) => void;
264
265
  onRemoteTrack?: (track: MediaStreamTrack, stream: MediaStream, metadata: TrackInfo) => void;
@@ -280,8 +281,6 @@ interface SFUClientOptions {
280
281
  recording_id: string;
281
282
  stopped_at: string;
282
283
  }) => void;
283
- /** Authoritative room snapshot received after join/reconnect/resync. */
284
- onRoomState?: (state: any) => void;
285
284
  onError?: (error: any) => void;
286
285
  autoSubscribe?: boolean;
287
286
  }
@@ -335,12 +334,11 @@ declare class SFUClient {
335
334
  private trackToSource;
336
335
  private localTrackIds;
337
336
  private subscriberStatsTimer;
338
- private roomSyncTimer;
339
- private roomSyncInFlight;
340
337
  private onStateChange;
341
338
  private onParticipantJoined;
342
339
  private onParticipantLeft;
343
340
  private onParticipantPresence;
341
+ private onParticipantMediaState;
344
342
  private onTrackPublished;
345
343
  private onTrackUnpublished;
346
344
  private onRemoteTrack;
@@ -356,16 +354,13 @@ declare class SFUClient {
356
354
  private onTrackStateChanged;
357
355
  private onRecordingStarted;
358
356
  private onRecordingStopped;
359
- private onRoomState;
357
+ private lastRecordingId;
360
358
  private onError;
361
359
  private autoSubscribe;
362
360
  constructor(options?: SFUClientOptions);
363
361
  private heartbeatTimer;
364
362
  private startHeartbeat;
365
363
  private stopHeartbeat;
366
- private startRoomStateSync;
367
- private stopRoomStateSync;
368
- private syncRoomState;
369
364
  private createId;
370
365
  private resumeStorageKey;
371
366
  private loadResumeToken;
@@ -392,6 +387,8 @@ declare class SFUClient {
392
387
  displayName?: string;
393
388
  avatarUrl?: string;
394
389
  participantId?: string;
390
+ audioMuted?: boolean;
391
+ videoMuted?: boolean;
395
392
  }): Promise<void>;
396
393
  private refreshAccessToken;
397
394
  private connect;
@@ -400,7 +397,7 @@ declare class SFUClient {
400
397
  private failJoin;
401
398
  private resolveJoin;
402
399
  private handleServerMessage;
403
- private applyRoomState;
400
+ private reconcileRoomState;
404
401
  private rememberTrack;
405
402
  private handleJoined;
406
403
  private initializePeerConnections;
@@ -478,7 +475,6 @@ type MeetingContextValue = {
478
475
  onEntryRequested: (cb: (req: any) => void) => () => void;
479
476
  onEntryResponded: (cb: (payload: any, decision?: any) => void) => () => void;
480
477
  onMeetingLeft: (cb: () => void) => () => void;
481
- onRoomUpdate: (cb: (data: any) => void) => () => void;
482
478
  onTrackStateChanged: (cb: (track: any) => void) => () => void;
483
479
  onRecordingStarted: (cb: (recording: Recording) => void) => () => void;
484
480
  onRecordingStopped: (cb: (recording: any) => void) => () => void;
@@ -537,7 +533,6 @@ declare const useMeeting: (handlers?: {
537
533
  onEntryRequested: (cb: (req: any) => void) => () => void;
538
534
  onEntryResponded: (cb: (payload: any, decision?: any) => void) => () => void;
539
535
  onMeetingLeft: (cb: () => void) => () => void;
540
- onRoomUpdate: (cb: (data: any) => void) => () => void;
541
536
  onTrackStateChanged: (cb: (track: any) => void) => () => void;
542
537
  onRecordingStarted: (cb: (recording: Recording) => void) => () => void;
543
538
  onRecordingStopped: (cb: (recording: any) => void) => () => void;
package/dist/index.d.ts CHANGED
@@ -259,6 +259,7 @@ interface SFUClientOptions {
259
259
  onParticipantJoined?: (participant: ParticipantInfo) => void;
260
260
  onParticipantLeft?: (participant: string | ParticipantInfo) => void;
261
261
  onParticipantPresence?: (participantId: string, presence: string) => void;
262
+ onParticipantMediaState?: (participantId: string, audioMuted: boolean, videoMuted: boolean) => void;
262
263
  onTrackPublished?: (track: TrackInfo) => void;
263
264
  onTrackUnpublished?: (track: TrackInfo) => void;
264
265
  onRemoteTrack?: (track: MediaStreamTrack, stream: MediaStream, metadata: TrackInfo) => void;
@@ -280,8 +281,6 @@ interface SFUClientOptions {
280
281
  recording_id: string;
281
282
  stopped_at: string;
282
283
  }) => void;
283
- /** Authoritative room snapshot received after join/reconnect/resync. */
284
- onRoomState?: (state: any) => void;
285
284
  onError?: (error: any) => void;
286
285
  autoSubscribe?: boolean;
287
286
  }
@@ -335,12 +334,11 @@ declare class SFUClient {
335
334
  private trackToSource;
336
335
  private localTrackIds;
337
336
  private subscriberStatsTimer;
338
- private roomSyncTimer;
339
- private roomSyncInFlight;
340
337
  private onStateChange;
341
338
  private onParticipantJoined;
342
339
  private onParticipantLeft;
343
340
  private onParticipantPresence;
341
+ private onParticipantMediaState;
344
342
  private onTrackPublished;
345
343
  private onTrackUnpublished;
346
344
  private onRemoteTrack;
@@ -356,16 +354,13 @@ declare class SFUClient {
356
354
  private onTrackStateChanged;
357
355
  private onRecordingStarted;
358
356
  private onRecordingStopped;
359
- private onRoomState;
357
+ private lastRecordingId;
360
358
  private onError;
361
359
  private autoSubscribe;
362
360
  constructor(options?: SFUClientOptions);
363
361
  private heartbeatTimer;
364
362
  private startHeartbeat;
365
363
  private stopHeartbeat;
366
- private startRoomStateSync;
367
- private stopRoomStateSync;
368
- private syncRoomState;
369
364
  private createId;
370
365
  private resumeStorageKey;
371
366
  private loadResumeToken;
@@ -392,6 +387,8 @@ declare class SFUClient {
392
387
  displayName?: string;
393
388
  avatarUrl?: string;
394
389
  participantId?: string;
390
+ audioMuted?: boolean;
391
+ videoMuted?: boolean;
395
392
  }): Promise<void>;
396
393
  private refreshAccessToken;
397
394
  private connect;
@@ -400,7 +397,7 @@ declare class SFUClient {
400
397
  private failJoin;
401
398
  private resolveJoin;
402
399
  private handleServerMessage;
403
- private applyRoomState;
400
+ private reconcileRoomState;
404
401
  private rememberTrack;
405
402
  private handleJoined;
406
403
  private initializePeerConnections;
@@ -478,7 +475,6 @@ type MeetingContextValue = {
478
475
  onEntryRequested: (cb: (req: any) => void) => () => void;
479
476
  onEntryResponded: (cb: (payload: any, decision?: any) => void) => () => void;
480
477
  onMeetingLeft: (cb: () => void) => () => void;
481
- onRoomUpdate: (cb: (data: any) => void) => () => void;
482
478
  onTrackStateChanged: (cb: (track: any) => void) => () => void;
483
479
  onRecordingStarted: (cb: (recording: Recording) => void) => () => void;
484
480
  onRecordingStopped: (cb: (recording: any) => void) => () => void;
@@ -537,7 +533,6 @@ declare const useMeeting: (handlers?: {
537
533
  onEntryRequested: (cb: (req: any) => void) => () => void;
538
534
  onEntryResponded: (cb: (payload: any, decision?: any) => void) => () => void;
539
535
  onMeetingLeft: (cb: () => void) => () => void;
540
- onRoomUpdate: (cb: (data: any) => void) => () => void;
541
536
  onTrackStateChanged: (cb: (track: any) => void) => () => void;
542
537
  onRecordingStarted: (cb: (recording: Recording) => void) => () => void;
543
538
  onRecordingStopped: (cb: (recording: any) => void) => () => void;
package/dist/index.js CHANGED
@@ -428,8 +428,7 @@ var SFUClient = class {
428
428
  this.trackToSource = /* @__PURE__ */ new Map();
429
429
  this.localTrackIds = /* @__PURE__ */ new Map();
430
430
  this.subscriberStatsTimer = null;
431
- this.roomSyncTimer = null;
432
- this.roomSyncInFlight = false;
431
+ this.lastRecordingId = null;
433
432
  this.heartbeatTimer = null;
434
433
  this.apiBase = options.apiBase || "https://api.example.com/api";
435
434
  this.wsBase = options.wsBase || "wss://api.example.com/ws";
@@ -442,6 +441,8 @@ var SFUClient = class {
442
441
  });
443
442
  this.onParticipantPresence = options.onParticipantPresence || (() => {
444
443
  });
444
+ this.onParticipantMediaState = options.onParticipantMediaState || (() => {
445
+ });
445
446
  this.onTrackPublished = options.onTrackPublished || (() => {
446
447
  });
447
448
  this.onTrackUnpublished = options.onTrackUnpublished || (() => {
@@ -472,8 +473,6 @@ var SFUClient = class {
472
473
  });
473
474
  this.onRecordingStopped = options.onRecordingStopped || (() => {
474
475
  });
475
- this.onRoomState = options.onRoomState || (() => {
476
- });
477
476
  this.onError = options.onError || console.error;
478
477
  this.autoSubscribe = options.autoSubscribe !== false;
479
478
  }
@@ -495,29 +494,6 @@ var SFUClient = class {
495
494
  this.heartbeatTimer = null;
496
495
  }
497
496
  }
498
- startRoomStateSync() {
499
- this.stopRoomStateSync();
500
- this.roomSyncTimer = setInterval(() => {
501
- if (this.state !== "connected") return;
502
- void this.syncRoomState();
503
- }, 5e3);
504
- }
505
- stopRoomStateSync() {
506
- if (this.roomSyncTimer) {
507
- clearInterval(this.roomSyncTimer);
508
- this.roomSyncTimer = null;
509
- }
510
- this.roomSyncInFlight = false;
511
- }
512
- async syncRoomState() {
513
- if (this.roomSyncInFlight || !this.isSocketOpen()) return;
514
- this.roomSyncInFlight = true;
515
- try {
516
- this.send({ type: "sync_room_state" });
517
- } finally {
518
- this.roomSyncInFlight = false;
519
- }
520
- }
521
497
  createId() {
522
498
  return typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `id_${Math.random().toString(36).slice(2)}_${Date.now()}`;
523
499
  }
@@ -617,7 +593,9 @@ var SFUClient = class {
617
593
  this.room = nextRoom;
618
594
  this.joinOptions = {
619
595
  displayName: options.displayName,
620
- avatarUrl: options.avatarUrl
596
+ avatarUrl: options.avatarUrl,
597
+ audioMuted: options.audioMuted,
598
+ videoMuted: options.videoMuted
621
599
  };
622
600
  if (options.participantId) this.participantId = options.participantId;
623
601
  if (!this.resumeToken) {
@@ -686,7 +664,9 @@ var SFUClient = class {
686
664
  room_code: this.room,
687
665
  resume_token: this.resumeToken,
688
666
  display_name: options.displayName,
689
- avatar_url: options.avatarUrl
667
+ avatar_url: options.avatarUrl,
668
+ audio_muted: options.audioMuted ?? this.joinOptions.audioMuted ?? false,
669
+ video_muted: options.videoMuted ?? this.joinOptions.videoMuted ?? false
690
670
  };
691
671
  if (this.pendingJoinRequestId) {
692
672
  this.send({
@@ -800,7 +780,9 @@ var SFUClient = class {
800
780
  room_code: this.room,
801
781
  resume_token: this.resumeToken,
802
782
  display_name: this.joinOptions.displayName,
803
- avatar_url: this.joinOptions.avatarUrl
783
+ avatar_url: this.joinOptions.avatarUrl,
784
+ audio_muted: this.joinOptions.audioMuted ?? false,
785
+ video_muted: this.joinOptions.videoMuted ?? false
804
786
  }
805
787
  });
806
788
  }
@@ -940,11 +922,8 @@ var SFUClient = class {
940
922
  case "subscription_updated": {
941
923
  break;
942
924
  }
943
- case "room_state": {
944
- await this.applyRoomState(payload);
945
- break;
946
- }
947
925
  case "recording_started": {
926
+ this.lastRecordingId = payload.recording_id || this.lastRecordingId;
948
927
  this.onRecordingStarted({
949
928
  recording_id: payload.recording_id,
950
929
  started_at: payload.started_at
@@ -952,6 +931,7 @@ var SFUClient = class {
952
931
  break;
953
932
  }
954
933
  case "recording_stopped": {
934
+ this.lastRecordingId = null;
955
935
  this.onRecordingStopped({
956
936
  recording_id: payload.recording_id,
957
937
  stopped_at: payload.stopped_at
@@ -987,6 +967,29 @@ var SFUClient = class {
987
967
  this.onScreenShareStopped(participantId, trackId);
988
968
  break;
989
969
  }
970
+ case "room_state": {
971
+ await this.reconcileRoomState(payload);
972
+ break;
973
+ }
974
+ case "participant_media_state": {
975
+ const participantId = payload.participant_id;
976
+ if (participantId) {
977
+ const current = this.participants.get(participantId);
978
+ if (current) {
979
+ this.participants.set(participantId, {
980
+ ...current,
981
+ audio_muted: Boolean(payload.audio_muted),
982
+ video_muted: Boolean(payload.video_muted)
983
+ });
984
+ }
985
+ }
986
+ this.onParticipantMediaState?.(
987
+ participantId,
988
+ Boolean(payload.audio_muted),
989
+ Boolean(payload.video_muted)
990
+ );
991
+ break;
992
+ }
990
993
  case "error": {
991
994
  const message2 = payload.message || "SFU signaling error";
992
995
  if (this.state === "reconnecting" && message2.includes("participant session is stale")) {
@@ -1009,71 +1012,89 @@ var SFUClient = class {
1009
1012
  console.debug("[SFU] Ignoring unknown server message", message);
1010
1013
  }
1011
1014
  }
1012
- async applyRoomState(payload) {
1015
+ async reconcileRoomState(payload) {
1013
1016
  const nextParticipants = /* @__PURE__ */ new Map();
1014
1017
  for (const participant of payload.participants || []) {
1015
- const id = participant?.participant_id;
1016
- if (!id) continue;
1017
- nextParticipants.set(id, participant);
1018
- }
1019
- for (const [id] of this.participants) {
1020
- if (id === this.currentParticipantId) continue;
1021
- if (!nextParticipants.has(id)) {
1022
- this.participants.delete(id);
1023
- this.removeRemoteParticipant(id);
1024
- this.onParticipantLeft(id);
1025
- }
1026
- }
1027
- for (const [id, participant] of nextParticipants) {
1028
- if (id === this.currentParticipantId) continue;
1029
- const existed = this.participants.has(id);
1030
- this.participants.set(id, participant);
1031
- if (!existed) {
1018
+ if (participant?.participant_id) {
1019
+ nextParticipants.set(participant.participant_id, participant);
1020
+ }
1021
+ }
1022
+ for (const participantId of this.participants.keys()) {
1023
+ if (participantId !== this.currentParticipantId && !nextParticipants.has(participantId)) {
1024
+ this.participants.delete(participantId);
1025
+ this.onParticipantLeft(participantId);
1026
+ }
1027
+ }
1028
+ for (const participant of nextParticipants.values()) {
1029
+ if (participant.participant_id === this.currentParticipantId) continue;
1030
+ const previous = this.participants.get(participant.participant_id);
1031
+ const wasScreenSharing = Boolean(previous?.screen_sharing);
1032
+ const isScreenSharing = Boolean(participant.screen_sharing);
1033
+ this.participants.set(participant.participant_id, participant);
1034
+ if (!previous) {
1032
1035
  this.onParticipantJoined(participant);
1033
1036
  }
1034
- const sharing = Boolean(participant.screen_sharing);
1035
- const screenTrackId = participant.screen_track_id ? String(participant.screen_track_id) : void 0;
1036
- if (sharing) {
1037
- const track = screenTrackId ? (payload.tracks || []).find(
1038
- (candidate) => String(candidate.track_id) === screenTrackId
1039
- ) : void 0;
1040
- this.onScreenShareStarted(id, track);
1041
- } else if (this.remoteScreenStreams.has(id)) {
1042
- this.remoteScreenStreams.get(id)?.getTracks().forEach((track) => track.stop());
1043
- this.remoteScreenStreams.delete(id);
1044
- this.onScreenShareStopped(id, screenTrackId);
1037
+ if (participant.presence) {
1038
+ this.onParticipantPresence(
1039
+ participant.participant_id,
1040
+ String(participant.presence).toLowerCase()
1041
+ );
1042
+ }
1043
+ this.onParticipantMediaState(
1044
+ participant.participant_id,
1045
+ Boolean(participant.audio_muted),
1046
+ Boolean(participant.video_muted)
1047
+ );
1048
+ if (!wasScreenSharing && isScreenSharing) {
1049
+ this.onScreenShareStarted(participant.participant_id, void 0);
1050
+ } else if (wasScreenSharing && !isScreenSharing) {
1051
+ this.onScreenShareStopped(
1052
+ participant.participant_id,
1053
+ participant.screen_track_id || void 0
1054
+ );
1045
1055
  }
1046
1056
  }
1047
1057
  const nextTracks = /* @__PURE__ */ new Map();
1048
1058
  for (const track of payload.tracks || []) {
1049
- if (track?.track_id) nextTracks.set(String(track.track_id), track);
1059
+ if (track?.track_id) {
1060
+ nextTracks.set(track.track_id, track);
1061
+ this.rememberTrack(track);
1062
+ }
1050
1063
  }
1051
- for (const [trackId, existing] of this.trackMetadata) {
1064
+ for (const [trackId, oldTrack] of [...this.trackMetadata.entries()]) {
1052
1065
  if (!nextTracks.has(trackId)) {
1053
1066
  this.trackMetadata.delete(trackId);
1054
1067
  this.trackToParticipant.delete(trackId);
1055
1068
  this.trackToSource.delete(trackId);
1056
1069
  this.subscribedTracks.delete(trackId);
1057
- for (const [source, localId] of this.localTrackIds) {
1058
- if (localId === trackId) this.localTrackIds.delete(source);
1059
- }
1060
- if (existing.participant_id !== this.currentParticipantId) {
1061
- this.onTrackUnpublished(existing);
1070
+ if (oldTrack.participant_id !== this.currentParticipantId) {
1071
+ this.onTrackUnpublished(oldTrack);
1062
1072
  }
1063
1073
  }
1064
1074
  }
1065
- for (const track of nextTracks.values()) {
1066
- this.rememberTrack(track);
1067
- }
1068
1075
  if (this.autoSubscribe) {
1069
1076
  const missing = [...nextTracks.values()].filter(
1070
1077
  (track) => track.participant_id !== this.currentParticipantId && !this.subscribedTracks.has(track.track_id)
1071
1078
  ).map((track) => track.track_id);
1072
- if (missing.length) {
1073
- await this.subscribeMultiple(missing);
1079
+ if (missing.length) await this.subscribeMultiple(missing);
1080
+ }
1081
+ const nextRecordingId = payload.recording?.recording_id || null;
1082
+ if (payload.recording) {
1083
+ if (this.lastRecordingId !== nextRecordingId) {
1084
+ this.lastRecordingId = nextRecordingId;
1085
+ this.onRecordingStarted({
1086
+ recording_id: payload.recording.recording_id,
1087
+ started_at: payload.recording.started_at
1088
+ });
1074
1089
  }
1090
+ } else if (this.lastRecordingId) {
1091
+ const recordingId = this.lastRecordingId;
1092
+ this.lastRecordingId = null;
1093
+ this.onRecordingStopped({
1094
+ recording_id: recordingId,
1095
+ stopped_at: (/* @__PURE__ */ new Date()).toISOString()
1096
+ });
1075
1097
  }
1076
- this.onRoomState(payload);
1077
1098
  }
1078
1099
  rememberTrack(track) {
1079
1100
  this.trackMetadata.set(track.track_id, track);
@@ -1104,15 +1125,8 @@ var SFUClient = class {
1104
1125
  for (const track of payload.tracks || []) {
1105
1126
  if (track?.track_id) this.rememberTrack(track);
1106
1127
  }
1107
- if (payload.recording) {
1108
- this.onRecordingStarted({
1109
- recording_id: payload.recording.recording_id,
1110
- started_at: payload.recording.started_at
1111
- });
1112
- }
1113
1128
  await this.initializePeerConnections();
1114
1129
  this.setState("connected");
1115
- this.startRoomStateSync();
1116
1130
  this.onConnected(payload, resumed);
1117
1131
  for (const participant of this.participants.values()) {
1118
1132
  if (participant.participant_id !== this.currentParticipantId) {
@@ -1568,31 +1582,25 @@ var SFUClient = class {
1568
1582
  this.localStream?.getAudioTracks().forEach((track) => {
1569
1583
  track.enabled = !muted;
1570
1584
  });
1571
- const trackId = this.localTrackIds.get("audio");
1572
- if (trackId) {
1573
- this.send({
1574
- type: "track_state",
1575
- payload: {
1576
- track_id: trackId,
1577
- muted
1578
- }
1579
- });
1580
- }
1585
+ this.send({
1586
+ type: "set_media_state",
1587
+ payload: {
1588
+ audio_muted: muted,
1589
+ video_muted: !(this.localStream?.getVideoTracks()[0]?.enabled ?? true)
1590
+ }
1591
+ });
1581
1592
  }
1582
1593
  muteVideo(muted) {
1583
1594
  this.localStream?.getVideoTracks().forEach((track) => {
1584
1595
  track.enabled = !muted;
1585
1596
  });
1586
- const trackId = this.localTrackIds.get("camera");
1587
- if (trackId) {
1588
- this.send({
1589
- type: "track_state",
1590
- payload: {
1591
- track_id: trackId,
1592
- muted
1593
- }
1594
- });
1595
- }
1597
+ this.send({
1598
+ type: "set_media_state",
1599
+ payload: {
1600
+ audio_muted: !(this.localStream?.getAudioTracks()[0]?.enabled ?? true),
1601
+ video_muted: muted
1602
+ }
1603
+ });
1596
1604
  }
1597
1605
  async startRecording() {
1598
1606
  if (!this.send({ type: "start_recording" })) {
@@ -1667,7 +1675,6 @@ var SFUClient = class {
1667
1675
  }
1668
1676
  async leave() {
1669
1677
  this.stopHeartbeat();
1670
- this.stopRoomStateSync();
1671
1678
  this.intentionalClose = true;
1672
1679
  this.setState("leaving");
1673
1680
  if (this.reconnectTimer) {
@@ -2052,32 +2059,12 @@ var VideoSDKCore = class {
2052
2059
  console.log(`[SFU] ${resumed ? "Resumed" : "Joined"} room`, payload);
2053
2060
  this.state.updateLocalParticipant({
2054
2061
  id: this.myId,
2055
- name: this.participantName
2056
- });
2057
- },
2058
- onRoomState: (roomState) => {
2059
- for (const participant of roomState?.participants || []) {
2060
- const pid = participant?.participant_id;
2061
- if (!pid || pid === this.myId) continue;
2062
- const current = this.state.getParticipant(pid);
2063
- if (!current) continue;
2064
- if (participant.screen_sharing) {
2065
- this.state.updateParticipantMedia(pid, {
2066
- isScreenSharing: true,
2067
- screenStream: current.media?.screenStream ?? null
2068
- });
2069
- } else if (current.media?.isScreenSharing) {
2070
- const screen = this.remoteScreenStreams.get(pid);
2071
- screen?.getTracks().forEach((track) => track.stop());
2072
- this.remoteScreenStreams.delete(pid);
2073
- this.state.updateParticipantMedia(pid, {
2074
- isScreenSharing: false,
2075
- screenStream: null,
2076
- screenTrack: void 0
2077
- });
2062
+ name: this.participantName,
2063
+ media: {
2064
+ micEnabled: this.localStream?.getAudioTracks()[0]?.enabled ?? true,
2065
+ camEnabled: this.localStream?.getVideoTracks()[0]?.enabled ?? true
2078
2066
  }
2079
- }
2080
- this.events.onRoomUpdate?.(roomState);
2067
+ });
2081
2068
  },
2082
2069
  onDisconnected: () => {
2083
2070
  console.log("[SFU] Signaling WebSocket disconnected; reconnecting");
@@ -2145,22 +2132,44 @@ var VideoSDKCore = class {
2145
2132
  return;
2146
2133
  }
2147
2134
  const pname = participant.user_metadata?.name || participant.display_name || "Participant";
2148
- this.state.addParticipant({
2135
+ const participantMedia = participant.media || {};
2136
+ const participantState = {
2149
2137
  id: pid,
2150
2138
  name: pname,
2151
- connectionState: "connected",
2139
+ connectionState: participant.presence === "reconnecting" ? "reconnecting" : "connected",
2152
2140
  media: {
2153
- micEnabled: true,
2154
- camEnabled: true,
2155
- isScreenSharing: false,
2141
+ micEnabled: !Boolean(participant.audio_muted),
2142
+ camEnabled: !Boolean(participant.video_muted),
2143
+ isScreenSharing: Boolean(participant.screen_sharing),
2144
+ screenStream: participantMedia.screenStream ?? null,
2156
2145
  stream: null
2157
2146
  }
2158
- });
2147
+ };
2148
+ if (!this.state.addParticipant(participantState)) {
2149
+ this.state.updateParticipantMedia(pid, participantState.media);
2150
+ }
2159
2151
  this.events.onUserJoined?.({
2160
2152
  id: pid,
2161
2153
  name: pname
2162
2154
  });
2163
2155
  },
2156
+ onParticipantMediaState: (participantId, audioMuted, videoMuted) => {
2157
+ if (participantId === this.myId) {
2158
+ this.state.updateLocalParticipant({
2159
+ media: {
2160
+ micEnabled: !audioMuted,
2161
+ camEnabled: !videoMuted
2162
+ }
2163
+ });
2164
+ return;
2165
+ }
2166
+ this.state.updateParticipantMedia(participantId, {
2167
+ micEnabled: !audioMuted,
2168
+ camEnabled: !videoMuted
2169
+ });
2170
+ this.events.onMuteStateChanged?.(participantId, "audio", audioMuted);
2171
+ this.events.onMuteStateChanged?.(participantId, "video", videoMuted);
2172
+ },
2164
2173
  onParticipantPresence: (participantId, presence) => {
2165
2174
  if (participantId === this.myId) {
2166
2175
  return;
@@ -2324,7 +2333,9 @@ var VideoSDKCore = class {
2324
2333
  client.setLocalStream(this.localStream);
2325
2334
  await client.getToken(roomId, {
2326
2335
  displayName: name,
2327
- participantId: this.myId
2336
+ participantId: this.myId,
2337
+ audioMuted: !this.localStream?.getAudioTracks()[0]?.enabled,
2338
+ videoMuted: !this.localStream?.getVideoTracks()[0]?.enabled
2328
2339
  });
2329
2340
  }
2330
2341
  // SFU STATE
@@ -2488,6 +2499,7 @@ var VideoSDKCore = class {
2488
2499
  screenTrack: this.screenStream.getVideoTracks()[0]
2489
2500
  }
2490
2501
  });
2502
+ this.state.setPresenterId(this.myId);
2491
2503
  return this.screenStream;
2492
2504
  } catch (err) {
2493
2505
  this.emitError(
@@ -2509,6 +2521,9 @@ var VideoSDKCore = class {
2509
2521
  this.screenStream.getTracks().forEach((track) => track.stop());
2510
2522
  this.screenStream = null;
2511
2523
  this.isScreenSharing = false;
2524
+ if (this.state.presenterId === this.myId) {
2525
+ this.state.setPresenterId(null);
2526
+ }
2512
2527
  this.state.updateLocalParticipant({
2513
2528
  media: {
2514
2529
  isScreenSharing: false,
@@ -2638,7 +2653,6 @@ var MeetingProvider = ({
2638
2653
  /* @__PURE__ */ new Set()
2639
2654
  );
2640
2655
  const meetingLeftListeners = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
2641
- const roomUpdateListeners = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
2642
2656
  const trackStateListeners = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
2643
2657
  const recordingStartedListeners = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
2644
2658
  const recordingStoppedListeners = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
@@ -2663,9 +2677,6 @@ var MeetingProvider = ({
2663
2677
  onMeetingLeft: () => {
2664
2678
  meetingLeftListeners.current.forEach((fn) => fn());
2665
2679
  },
2666
- onRoomUpdate: (data) => {
2667
- roomUpdateListeners.current.forEach((fn) => fn(data));
2668
- },
2669
2680
  onTrackStateChanged: (track) => {
2670
2681
  trackStateListeners.current.forEach((fn) => fn(track));
2671
2682
  },
@@ -2764,10 +2775,6 @@ var MeetingProvider = ({
2764
2775
  meetingLeftListeners.current.delete(cb);
2765
2776
  };
2766
2777
  },
2767
- onRoomUpdate: (cb) => {
2768
- roomUpdateListeners.current.add(cb);
2769
- return () => roomUpdateListeners.current.delete(cb);
2770
- },
2771
2778
  onTrackStateChanged: (cb) => {
2772
2779
  trackStateListeners.current.add(cb);
2773
2780
  return () => trackStateListeners.current.delete(cb);