@defcomm/meetingsdk 0.2.2 → 0.2.3

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.mjs CHANGED
@@ -392,6 +392,8 @@ var SFUClient = class {
392
392
  this.trackToSource = /* @__PURE__ */ new Map();
393
393
  this.localTrackIds = /* @__PURE__ */ new Map();
394
394
  this.subscriberStatsTimer = null;
395
+ this.roomSyncTimer = null;
396
+ this.roomSyncInFlight = false;
395
397
  this.heartbeatTimer = null;
396
398
  this.apiBase = options.apiBase || "https://api.example.com/api";
397
399
  this.wsBase = options.wsBase || "wss://api.example.com/ws";
@@ -434,6 +436,8 @@ var SFUClient = class {
434
436
  });
435
437
  this.onRecordingStopped = options.onRecordingStopped || (() => {
436
438
  });
439
+ this.onRoomState = options.onRoomState || (() => {
440
+ });
437
441
  this.onError = options.onError || console.error;
438
442
  this.autoSubscribe = options.autoSubscribe !== false;
439
443
  }
@@ -455,6 +459,29 @@ var SFUClient = class {
455
459
  this.heartbeatTimer = null;
456
460
  }
457
461
  }
462
+ startRoomStateSync() {
463
+ this.stopRoomStateSync();
464
+ this.roomSyncTimer = setInterval(() => {
465
+ if (this.state !== "connected") return;
466
+ void this.syncRoomState();
467
+ }, 5e3);
468
+ }
469
+ stopRoomStateSync() {
470
+ if (this.roomSyncTimer) {
471
+ clearInterval(this.roomSyncTimer);
472
+ this.roomSyncTimer = null;
473
+ }
474
+ this.roomSyncInFlight = false;
475
+ }
476
+ async syncRoomState() {
477
+ if (this.roomSyncInFlight || !this.isSocketOpen()) return;
478
+ this.roomSyncInFlight = true;
479
+ try {
480
+ this.send({ type: "sync_room_state" });
481
+ } finally {
482
+ this.roomSyncInFlight = false;
483
+ }
484
+ }
458
485
  createId() {
459
486
  return typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `id_${Math.random().toString(36).slice(2)}_${Date.now()}`;
460
487
  }
@@ -877,6 +904,10 @@ var SFUClient = class {
877
904
  case "subscription_updated": {
878
905
  break;
879
906
  }
907
+ case "room_state": {
908
+ await this.applyRoomState(payload);
909
+ break;
910
+ }
880
911
  case "recording_started": {
881
912
  this.onRecordingStarted({
882
913
  recording_id: payload.recording_id,
@@ -942,6 +973,72 @@ var SFUClient = class {
942
973
  console.debug("[SFU] Ignoring unknown server message", message);
943
974
  }
944
975
  }
976
+ async applyRoomState(payload) {
977
+ const nextParticipants = /* @__PURE__ */ new Map();
978
+ for (const participant of payload.participants || []) {
979
+ const id = participant?.participant_id;
980
+ if (!id) continue;
981
+ nextParticipants.set(id, participant);
982
+ }
983
+ for (const [id] of this.participants) {
984
+ if (id === this.currentParticipantId) continue;
985
+ if (!nextParticipants.has(id)) {
986
+ this.participants.delete(id);
987
+ this.removeRemoteParticipant(id);
988
+ this.onParticipantLeft(id);
989
+ }
990
+ }
991
+ for (const [id, participant] of nextParticipants) {
992
+ if (id === this.currentParticipantId) continue;
993
+ const existed = this.participants.has(id);
994
+ this.participants.set(id, participant);
995
+ if (!existed) {
996
+ this.onParticipantJoined(participant);
997
+ }
998
+ const sharing = Boolean(participant.screen_sharing);
999
+ const screenTrackId = participant.screen_track_id ? String(participant.screen_track_id) : void 0;
1000
+ if (sharing) {
1001
+ const track = screenTrackId ? (payload.tracks || []).find(
1002
+ (candidate) => String(candidate.track_id) === screenTrackId
1003
+ ) : void 0;
1004
+ this.onScreenShareStarted(id, track);
1005
+ } else if (this.remoteScreenStreams.has(id)) {
1006
+ this.remoteScreenStreams.get(id)?.getTracks().forEach((track) => track.stop());
1007
+ this.remoteScreenStreams.delete(id);
1008
+ this.onScreenShareStopped(id, screenTrackId);
1009
+ }
1010
+ }
1011
+ const nextTracks = /* @__PURE__ */ new Map();
1012
+ for (const track of payload.tracks || []) {
1013
+ if (track?.track_id) nextTracks.set(String(track.track_id), track);
1014
+ }
1015
+ for (const [trackId, existing] of this.trackMetadata) {
1016
+ if (!nextTracks.has(trackId)) {
1017
+ this.trackMetadata.delete(trackId);
1018
+ this.trackToParticipant.delete(trackId);
1019
+ this.trackToSource.delete(trackId);
1020
+ this.subscribedTracks.delete(trackId);
1021
+ for (const [source, localId] of this.localTrackIds) {
1022
+ if (localId === trackId) this.localTrackIds.delete(source);
1023
+ }
1024
+ if (existing.participant_id !== this.currentParticipantId) {
1025
+ this.onTrackUnpublished(existing);
1026
+ }
1027
+ }
1028
+ }
1029
+ for (const track of nextTracks.values()) {
1030
+ this.rememberTrack(track);
1031
+ }
1032
+ if (this.autoSubscribe) {
1033
+ const missing = [...nextTracks.values()].filter(
1034
+ (track) => track.participant_id !== this.currentParticipantId && !this.subscribedTracks.has(track.track_id)
1035
+ ).map((track) => track.track_id);
1036
+ if (missing.length) {
1037
+ await this.subscribeMultiple(missing);
1038
+ }
1039
+ }
1040
+ this.onRoomState(payload);
1041
+ }
945
1042
  rememberTrack(track) {
946
1043
  this.trackMetadata.set(track.track_id, track);
947
1044
  this.trackToParticipant.set(track.track_id, track.participant_id);
@@ -971,8 +1068,15 @@ var SFUClient = class {
971
1068
  for (const track of payload.tracks || []) {
972
1069
  if (track?.track_id) this.rememberTrack(track);
973
1070
  }
1071
+ if (payload.recording) {
1072
+ this.onRecordingStarted({
1073
+ recording_id: payload.recording.recording_id,
1074
+ started_at: payload.recording.started_at
1075
+ });
1076
+ }
974
1077
  await this.initializePeerConnections();
975
1078
  this.setState("connected");
1079
+ this.startRoomStateSync();
976
1080
  this.onConnected(payload, resumed);
977
1081
  for (const participant of this.participants.values()) {
978
1082
  if (participant.participant_id !== this.currentParticipantId) {
@@ -1527,6 +1631,7 @@ var SFUClient = class {
1527
1631
  }
1528
1632
  async leave() {
1529
1633
  this.stopHeartbeat();
1634
+ this.stopRoomStateSync();
1530
1635
  this.intentionalClose = true;
1531
1636
  this.setState("leaving");
1532
1637
  if (this.reconnectTimer) {
@@ -1914,6 +2019,30 @@ var VideoSDKCore = class {
1914
2019
  name: this.participantName
1915
2020
  });
1916
2021
  },
2022
+ onRoomState: (roomState) => {
2023
+ for (const participant of roomState?.participants || []) {
2024
+ const pid = participant?.participant_id;
2025
+ if (!pid || pid === this.myId) continue;
2026
+ const current = this.state.getParticipant(pid);
2027
+ if (!current) continue;
2028
+ if (participant.screen_sharing) {
2029
+ this.state.updateParticipantMedia(pid, {
2030
+ isScreenSharing: true,
2031
+ screenStream: current.media?.screenStream ?? null
2032
+ });
2033
+ } else if (current.media?.isScreenSharing) {
2034
+ const screen = this.remoteScreenStreams.get(pid);
2035
+ screen?.getTracks().forEach((track) => track.stop());
2036
+ this.remoteScreenStreams.delete(pid);
2037
+ this.state.updateParticipantMedia(pid, {
2038
+ isScreenSharing: false,
2039
+ screenStream: null,
2040
+ screenTrack: void 0
2041
+ });
2042
+ }
2043
+ }
2044
+ this.events.onRoomUpdate?.(roomState);
2045
+ },
1917
2046
  onDisconnected: () => {
1918
2047
  console.log("[SFU] Signaling WebSocket disconnected; reconnecting");
1919
2048
  },
@@ -2473,6 +2602,7 @@ var MeetingProvider = ({
2473
2602
  /* @__PURE__ */ new Set()
2474
2603
  );
2475
2604
  const meetingLeftListeners = useRef(/* @__PURE__ */ new Set());
2605
+ const roomUpdateListeners = useRef(/* @__PURE__ */ new Set());
2476
2606
  const trackStateListeners = useRef(/* @__PURE__ */ new Set());
2477
2607
  const recordingStartedListeners = useRef(/* @__PURE__ */ new Set());
2478
2608
  const recordingStoppedListeners = useRef(/* @__PURE__ */ new Set());
@@ -2497,6 +2627,9 @@ var MeetingProvider = ({
2497
2627
  onMeetingLeft: () => {
2498
2628
  meetingLeftListeners.current.forEach((fn) => fn());
2499
2629
  },
2630
+ onRoomUpdate: (data) => {
2631
+ roomUpdateListeners.current.forEach((fn) => fn(data));
2632
+ },
2500
2633
  onTrackStateChanged: (track) => {
2501
2634
  trackStateListeners.current.forEach((fn) => fn(track));
2502
2635
  },
@@ -2595,6 +2728,10 @@ var MeetingProvider = ({
2595
2728
  meetingLeftListeners.current.delete(cb);
2596
2729
  };
2597
2730
  },
2731
+ onRoomUpdate: (cb) => {
2732
+ roomUpdateListeners.current.add(cb);
2733
+ return () => roomUpdateListeners.current.delete(cb);
2734
+ },
2598
2735
  onTrackStateChanged: (cb) => {
2599
2736
  trackStateListeners.current.add(cb);
2600
2737
  return () => trackStateListeners.current.delete(cb);