@defcomm/meetingsdk 0.2.6 → 0.2.7

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
@@ -367,6 +367,8 @@ var SFUClient = class {
367
367
  this.intentionalClose = false;
368
368
  this.leaveAcknowledged = false;
369
369
  this.joiningWithResume = false;
370
+ this.desiredAudioMuted = false;
371
+ this.desiredVideoMuted = false;
370
372
  this.reconnectTimer = null;
371
373
  this.reconnectAttempt = 0;
372
374
  this.maxReconnectAttempts = 8;
@@ -435,6 +437,12 @@ var SFUClient = class {
435
437
  this.onRecordingStopped = options.onRecordingStopped || (() => {
436
438
  });
437
439
  this.onError = options.onError || console.error;
440
+ this.onChatMessage = options.onChatMessage || (() => {
441
+ });
442
+ this.onRoomUpdate = options.onRoomUpdate || (() => {
443
+ });
444
+ this.onParticipantMediaState = options.onParticipantMediaState || (() => {
445
+ });
438
446
  this.autoSubscribe = options.autoSubscribe !== false;
439
447
  }
440
448
  startHeartbeat() {
@@ -557,6 +565,8 @@ var SFUClient = class {
557
565
  avatarUrl: options.avatarUrl
558
566
  };
559
567
  if (options.participantId) this.participantId = options.participantId;
568
+ if (typeof options.audioMuted === "boolean") this.desiredAudioMuted = options.audioMuted;
569
+ if (typeof options.videoMuted === "boolean") this.desiredVideoMuted = options.videoMuted;
560
570
  if (!this.resumeToken) {
561
571
  this.resumeToken = this.loadResumeToken(this.room);
562
572
  }
@@ -623,7 +633,9 @@ var SFUClient = class {
623
633
  room_code: this.room,
624
634
  resume_token: this.resumeToken,
625
635
  display_name: options.displayName,
626
- avatar_url: options.avatarUrl
636
+ avatar_url: options.avatarUrl,
637
+ audio_muted: this.desiredAudioMuted,
638
+ video_muted: this.desiredVideoMuted
627
639
  };
628
640
  if (this.pendingJoinRequestId) {
629
641
  this.send({
@@ -798,6 +810,16 @@ var SFUClient = class {
798
810
  this.onParticipantLeft(participantId);
799
811
  break;
800
812
  }
813
+ case "participant_media_state": {
814
+ const participantId = payload.participant_id;
815
+ if (!participantId) break;
816
+ this.onParticipantMediaState(
817
+ participantId,
818
+ Boolean(payload.audio_muted),
819
+ Boolean(payload.video_muted)
820
+ );
821
+ break;
822
+ }
801
823
  case "participant_presence":
802
824
  case "participant_presence_changed": {
803
825
  const participantId = payload.participant_id;
@@ -920,6 +942,37 @@ var SFUClient = class {
920
942
  this.onScreenShareStopped(participantId, trackId);
921
943
  break;
922
944
  }
945
+ case "room_state": {
946
+ const participants = payload.participants || [];
947
+ this.onRoomStateReconciled(payload);
948
+ this.onRoomUpdate(payload);
949
+ for (const participant of participants) {
950
+ const pid = participant?.participant_id;
951
+ if (!pid) continue;
952
+ if (pid !== this.currentParticipantId) {
953
+ this.participants.set(pid, participant);
954
+ this.onParticipantPresence(pid, String(participant.presence || "connected"));
955
+ this.onParticipantMediaState(pid, Boolean(participant.audio_muted), Boolean(participant.video_muted));
956
+ }
957
+ }
958
+ for (const [pid] of this.participants) {
959
+ if (!participants.some((p) => p?.participant_id === pid)) {
960
+ this.participants.delete(pid);
961
+ this.removeRemoteParticipant(pid);
962
+ this.onParticipantLeft(pid);
963
+ }
964
+ }
965
+ for (const track of payload.tracks || []) this.rememberTrack(track);
966
+ if (Array.isArray(payload.chat_history)) {
967
+ for (const message2 of payload.chat_history) this.onChatMessage(message2);
968
+ }
969
+ break;
970
+ }
971
+ case "chat_message": {
972
+ const msg = payload.message || payload;
973
+ if (msg?.id) this.onChatMessage(msg);
974
+ break;
975
+ }
923
976
  case "error": {
924
977
  const message2 = payload.message || "SFU signaling error";
925
978
  if (this.state === "reconnecting" && message2.includes("participant session is stale")) {
@@ -942,6 +995,14 @@ var SFUClient = class {
942
995
  console.debug("[SFU] Ignoring unknown server message", message);
943
996
  }
944
997
  }
998
+ onRoomStateReconciled(payload) {
999
+ const participants = payload.participants || [];
1000
+ const local = participants.find((p) => p?.participant_id === this.currentParticipantId);
1001
+ if (local) {
1002
+ this.desiredAudioMuted = Boolean(local.audio_muted);
1003
+ this.desiredVideoMuted = Boolean(local.video_muted);
1004
+ }
1005
+ }
945
1006
  rememberTrack(track) {
946
1007
  this.trackMetadata.set(track.track_id, track);
947
1008
  this.trackToParticipant.set(track.track_id, track.participant_id);
@@ -977,8 +1038,21 @@ var SFUClient = class {
977
1038
  for (const participant of this.participants.values()) {
978
1039
  if (participant.participant_id !== this.currentParticipantId) {
979
1040
  this.onParticipantJoined(participant);
1041
+ this.onParticipantPresence(
1042
+ participant.participant_id,
1043
+ String(participant.presence || "connected")
1044
+ );
1045
+ this.onParticipantMediaState(
1046
+ participant.participant_id,
1047
+ Boolean(participant.audio_muted),
1048
+ Boolean(participant.video_muted)
1049
+ );
980
1050
  }
981
1051
  }
1052
+ if (Array.isArray(payload.chat_history)) {
1053
+ for (const message of payload.chat_history) this.onChatMessage(message);
1054
+ }
1055
+ this.onRoomUpdate(payload);
982
1056
  if (this.localStream) {
983
1057
  await this.publishExistingTracks();
984
1058
  }
@@ -1351,9 +1425,6 @@ var SFUClient = class {
1351
1425
  }
1352
1426
  this.screenStream = screenStream;
1353
1427
  try {
1354
- if (!this.send({ type: "start_screen_share" })) {
1355
- throw new Error("Failed to signal start_screen_share");
1356
- }
1357
1428
  await this.screenSender.replaceTrack(screenTrack);
1358
1429
  this.screenTransceiver.direction = "sendonly";
1359
1430
  const offer = await this.publisher.createOffer();
@@ -1365,6 +1436,7 @@ var SFUClient = class {
1365
1436
  sdp: offer.sdp
1366
1437
  }
1367
1438
  });
1439
+ this.send({ type: "start_screen_share" });
1368
1440
  screenTrack.onended = () => {
1369
1441
  void this.stopScreenShare();
1370
1442
  };
@@ -1425,9 +1497,14 @@ var SFUClient = class {
1425
1497
  }
1426
1498
  }
1427
1499
  muteAudio(muted) {
1500
+ this.desiredAudioMuted = muted;
1428
1501
  this.localStream?.getAudioTracks().forEach((track) => {
1429
1502
  track.enabled = !muted;
1430
1503
  });
1504
+ this.send({
1505
+ type: "set_media_state",
1506
+ payload: { audio_muted: muted, video_muted: this.desiredVideoMuted }
1507
+ });
1431
1508
  const trackId = this.localTrackIds.get("audio");
1432
1509
  if (trackId) {
1433
1510
  this.send({
@@ -1440,9 +1517,14 @@ var SFUClient = class {
1440
1517
  }
1441
1518
  }
1442
1519
  muteVideo(muted) {
1520
+ this.desiredVideoMuted = muted;
1443
1521
  this.localStream?.getVideoTracks().forEach((track) => {
1444
1522
  track.enabled = !muted;
1445
1523
  });
1524
+ this.send({
1525
+ type: "set_media_state",
1526
+ payload: { audio_muted: this.desiredAudioMuted, video_muted: muted }
1527
+ });
1446
1528
  const trackId = this.localTrackIds.get("camera");
1447
1529
  if (trackId) {
1448
1530
  this.send({
@@ -1454,6 +1536,16 @@ var SFUClient = class {
1454
1536
  });
1455
1537
  }
1456
1538
  }
1539
+ sendChatMessage(input) {
1540
+ return this.send({
1541
+ type: "send_chat",
1542
+ payload: {
1543
+ message: input.message,
1544
+ reply_to: input.reply_to ?? null,
1545
+ target: input.target ?? null
1546
+ }
1547
+ });
1548
+ }
1457
1549
  async startRecording() {
1458
1550
  if (!this.send({ type: "start_recording" })) {
1459
1551
  throw new Error("SFU signaling is not connected");
@@ -1921,6 +2013,41 @@ var VideoSDKCore = class {
1921
2013
  console.warn(`[SFU] Reconnecting, attempt ${attempt}`);
1922
2014
  },
1923
2015
  // ADMISSION CONTROL
2016
+ onRoomUpdate: (payload) => {
2017
+ const participants = payload?.participants || [];
2018
+ const local = participants.find((p) => p?.participant_id === this.myId);
2019
+ if (local) {
2020
+ const media = this.state.localParticipant?.media;
2021
+ this.state.updateLocalParticipant({
2022
+ id: this.myId,
2023
+ name: this.participantName,
2024
+ media: {
2025
+ micEnabled: !Boolean(local.audio_muted),
2026
+ camEnabled: !Boolean(local.video_muted),
2027
+ isScreenSharing: Boolean(local.screen_sharing),
2028
+ screenStream: this.screenStream,
2029
+ screenTrack: this.screenStream?.getVideoTracks()[0]
2030
+ }
2031
+ });
2032
+ if (this.localStream) {
2033
+ this.localStream.getAudioTracks().forEach((track) => {
2034
+ track.enabled = !Boolean(local.audio_muted);
2035
+ });
2036
+ this.localStream.getVideoTracks().forEach((track) => {
2037
+ track.enabled = !Boolean(local.video_muted);
2038
+ });
2039
+ }
2040
+ if (local.screen_sharing) this.state.setPresenterId(this.myId);
2041
+ else if (this.state.presenterId === this.myId) this.state.setPresenterId(null);
2042
+ }
2043
+ const presenter = participants.find((p) => p?.screen_sharing);
2044
+ if (presenter) this.state.setPresenterId(presenter.participant_id);
2045
+ else if (!local?.screen_sharing) this.state.setPresenterId(null);
2046
+ for (const message of payload?.chat_history || []) {
2047
+ this.state.addChatMessage(message);
2048
+ }
2049
+ this.events.onRoomUpdate?.(payload);
2050
+ },
1924
2051
  onJoinPending: (info) => {
1925
2052
  console.log("[SFU] Join request pending", info);
1926
2053
  },
@@ -1955,6 +2082,19 @@ var VideoSDKCore = class {
1955
2082
  false
1956
2083
  );
1957
2084
  },
2085
+ onChatMessage: (message) => {
2086
+ const msg = {
2087
+ id: message.id,
2088
+ text: message.text,
2089
+ sender_id: message.sender_id,
2090
+ sender_name: message.sender_name,
2091
+ timestamp: Number(message.timestamp),
2092
+ reply_to: message.reply_to ?? null,
2093
+ target: message.target ?? null
2094
+ };
2095
+ this.state.addChatMessage(msg);
2096
+ this.events.onChatMessage?.(msg);
2097
+ },
1958
2098
  onJoinRequested: (request) => {
1959
2099
  const requestId = request?.request_id || request?.requestId;
1960
2100
  const participantId = request?.user_id || request?.participantId;
@@ -2023,6 +2163,17 @@ var VideoSDKCore = class {
2023
2163
  this.events.onUserLeft?.(pid);
2024
2164
  },
2025
2165
  // TRACKS
2166
+ onParticipantMediaState: (participantId, audioMuted, videoMuted) => {
2167
+ if (participantId === this.myId) return;
2168
+ const participant = this.state.getParticipant(participantId);
2169
+ if (!participant) return;
2170
+ this.state.updateParticipantMedia(participantId, {
2171
+ micEnabled: !audioMuted,
2172
+ camEnabled: !videoMuted
2173
+ });
2174
+ this.events.onMuteStateChanged?.(participantId, "audio", audioMuted);
2175
+ this.events.onMuteStateChanged?.(participantId, "video", videoMuted);
2176
+ },
2026
2177
  onTrackPublished: (track) => {
2027
2178
  console.log("[SFU] Track published", track);
2028
2179
  },
@@ -2092,6 +2243,9 @@ var VideoSDKCore = class {
2092
2243
  // SCREEN SHARE
2093
2244
  onScreenShareStarted: (participantId, _track) => {
2094
2245
  console.log("[SFU] Screen share started", participantId);
2246
+ if (participantId === this.myId && !this.screenStream) {
2247
+ this.screenStream = this.sfuClient?.getScreenStream() ?? null;
2248
+ }
2095
2249
  this.state.setPresenterId(participantId);
2096
2250
  if (participantId === this.myId) {
2097
2251
  this.state.updateLocalParticipant({
@@ -2372,18 +2526,13 @@ var VideoSDKCore = class {
2372
2526
  console.warn("SFU Client not connected");
2373
2527
  return;
2374
2528
  }
2375
- const senderName = this.state.localParticipant?.name || "Anonymous";
2376
- const msg = {
2377
- id: crypto.randomUUID(),
2378
- sender_id: this.myId,
2379
- sender_name: senderName,
2380
- text: payload.message.trim(),
2381
- timestamp: Date.now(),
2529
+ const message = payload.message.trim();
2530
+ if (!message) return;
2531
+ this.sfuClient.sendChatMessage({
2532
+ message,
2382
2533
  reply_to: payload.reply_to ?? null,
2383
2534
  target: payload.target ?? null
2384
- };
2385
- this.state.addChatMessage(msg);
2386
- this.events.onChatMessage?.(msg);
2535
+ });
2387
2536
  }
2388
2537
  // CLEANUP
2389
2538
  cleanupRemoteParticipant(participantId) {