@defcomm/meetingsdk 0.2.5 → 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;
@@ -392,7 +394,6 @@ var SFUClient = class {
392
394
  this.trackToSource = /* @__PURE__ */ new Map();
393
395
  this.localTrackIds = /* @__PURE__ */ new Map();
394
396
  this.subscriberStatsTimer = null;
395
- this.lastRecordingId = null;
396
397
  this.heartbeatTimer = null;
397
398
  this.apiBase = options.apiBase || "https://api.example.com/api";
398
399
  this.wsBase = options.wsBase || "wss://api.example.com/ws";
@@ -405,8 +406,6 @@ var SFUClient = class {
405
406
  });
406
407
  this.onParticipantPresence = options.onParticipantPresence || (() => {
407
408
  });
408
- this.onParticipantMediaState = options.onParticipantMediaState || (() => {
409
- });
410
409
  this.onTrackPublished = options.onTrackPublished || (() => {
411
410
  });
412
411
  this.onTrackUnpublished = options.onTrackUnpublished || (() => {
@@ -438,6 +437,12 @@ var SFUClient = class {
438
437
  this.onRecordingStopped = options.onRecordingStopped || (() => {
439
438
  });
440
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
+ });
441
446
  this.autoSubscribe = options.autoSubscribe !== false;
442
447
  }
443
448
  startHeartbeat() {
@@ -557,11 +562,11 @@ var SFUClient = class {
557
562
  this.room = nextRoom;
558
563
  this.joinOptions = {
559
564
  displayName: options.displayName,
560
- avatarUrl: options.avatarUrl,
561
- audioMuted: options.audioMuted,
562
- videoMuted: options.videoMuted
565
+ avatarUrl: options.avatarUrl
563
566
  };
564
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;
565
570
  if (!this.resumeToken) {
566
571
  this.resumeToken = this.loadResumeToken(this.room);
567
572
  }
@@ -629,8 +634,8 @@ var SFUClient = class {
629
634
  resume_token: this.resumeToken,
630
635
  display_name: options.displayName,
631
636
  avatar_url: options.avatarUrl,
632
- audio_muted: options.audioMuted ?? this.joinOptions.audioMuted ?? false,
633
- video_muted: options.videoMuted ?? this.joinOptions.videoMuted ?? false
637
+ audio_muted: this.desiredAudioMuted,
638
+ video_muted: this.desiredVideoMuted
634
639
  };
635
640
  if (this.pendingJoinRequestId) {
636
641
  this.send({
@@ -744,9 +749,7 @@ var SFUClient = class {
744
749
  room_code: this.room,
745
750
  resume_token: this.resumeToken,
746
751
  display_name: this.joinOptions.displayName,
747
- avatar_url: this.joinOptions.avatarUrl,
748
- audio_muted: this.joinOptions.audioMuted ?? false,
749
- video_muted: this.joinOptions.videoMuted ?? false
752
+ avatar_url: this.joinOptions.avatarUrl
750
753
  }
751
754
  });
752
755
  }
@@ -807,6 +810,16 @@ var SFUClient = class {
807
810
  this.onParticipantLeft(participantId);
808
811
  break;
809
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
+ }
810
823
  case "participant_presence":
811
824
  case "participant_presence_changed": {
812
825
  const participantId = payload.participant_id;
@@ -887,7 +900,6 @@ var SFUClient = class {
887
900
  break;
888
901
  }
889
902
  case "recording_started": {
890
- this.lastRecordingId = payload.recording_id || this.lastRecordingId;
891
903
  this.onRecordingStarted({
892
904
  recording_id: payload.recording_id,
893
905
  started_at: payload.started_at
@@ -895,7 +907,6 @@ var SFUClient = class {
895
907
  break;
896
908
  }
897
909
  case "recording_stopped": {
898
- this.lastRecordingId = null;
899
910
  this.onRecordingStopped({
900
911
  recording_id: payload.recording_id,
901
912
  stopped_at: payload.stopped_at
@@ -932,26 +943,34 @@ var SFUClient = class {
932
943
  break;
933
944
  }
934
945
  case "room_state": {
935
- await this.reconcileRoomState(payload);
936
- break;
937
- }
938
- case "participant_media_state": {
939
- const participantId = payload.participant_id;
940
- if (participantId) {
941
- const current = this.participants.get(participantId);
942
- if (current) {
943
- this.participants.set(participantId, {
944
- ...current,
945
- audio_muted: Boolean(payload.audio_muted),
946
- video_muted: Boolean(payload.video_muted)
947
- });
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));
948
956
  }
949
957
  }
950
- this.onParticipantMediaState?.(
951
- participantId,
952
- Boolean(payload.audio_muted),
953
- Boolean(payload.video_muted)
954
- );
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);
955
974
  break;
956
975
  }
957
976
  case "error": {
@@ -976,88 +995,12 @@ var SFUClient = class {
976
995
  console.debug("[SFU] Ignoring unknown server message", message);
977
996
  }
978
997
  }
979
- async reconcileRoomState(payload) {
980
- const nextParticipants = /* @__PURE__ */ new Map();
981
- for (const participant of payload.participants || []) {
982
- if (participant?.participant_id) {
983
- nextParticipants.set(participant.participant_id, participant);
984
- }
985
- }
986
- for (const participantId of this.participants.keys()) {
987
- if (participantId !== this.currentParticipantId && !nextParticipants.has(participantId)) {
988
- this.participants.delete(participantId);
989
- this.onParticipantLeft(participantId);
990
- }
991
- }
992
- for (const participant of nextParticipants.values()) {
993
- if (participant.participant_id === this.currentParticipantId) continue;
994
- const previous = this.participants.get(participant.participant_id);
995
- const wasScreenSharing = Boolean(previous?.screen_sharing);
996
- const isScreenSharing = Boolean(participant.screen_sharing);
997
- this.participants.set(participant.participant_id, participant);
998
- if (!previous) {
999
- this.onParticipantJoined(participant);
1000
- }
1001
- if (participant.presence) {
1002
- this.onParticipantPresence(
1003
- participant.participant_id,
1004
- String(participant.presence).toLowerCase()
1005
- );
1006
- }
1007
- this.onParticipantMediaState(
1008
- participant.participant_id,
1009
- Boolean(participant.audio_muted),
1010
- Boolean(participant.video_muted)
1011
- );
1012
- if (!wasScreenSharing && isScreenSharing) {
1013
- this.onScreenShareStarted(participant.participant_id, void 0);
1014
- } else if (wasScreenSharing && !isScreenSharing) {
1015
- this.onScreenShareStopped(
1016
- participant.participant_id,
1017
- participant.screen_track_id || void 0
1018
- );
1019
- }
1020
- }
1021
- const nextTracks = /* @__PURE__ */ new Map();
1022
- for (const track of payload.tracks || []) {
1023
- if (track?.track_id) {
1024
- nextTracks.set(track.track_id, track);
1025
- this.rememberTrack(track);
1026
- }
1027
- }
1028
- for (const [trackId, oldTrack] of [...this.trackMetadata.entries()]) {
1029
- if (!nextTracks.has(trackId)) {
1030
- this.trackMetadata.delete(trackId);
1031
- this.trackToParticipant.delete(trackId);
1032
- this.trackToSource.delete(trackId);
1033
- this.subscribedTracks.delete(trackId);
1034
- if (oldTrack.participant_id !== this.currentParticipantId) {
1035
- this.onTrackUnpublished(oldTrack);
1036
- }
1037
- }
1038
- }
1039
- if (this.autoSubscribe) {
1040
- const missing = [...nextTracks.values()].filter(
1041
- (track) => track.participant_id !== this.currentParticipantId && !this.subscribedTracks.has(track.track_id)
1042
- ).map((track) => track.track_id);
1043
- if (missing.length) await this.subscribeMultiple(missing);
1044
- }
1045
- const nextRecordingId = payload.recording?.recording_id || null;
1046
- if (payload.recording) {
1047
- if (this.lastRecordingId !== nextRecordingId) {
1048
- this.lastRecordingId = nextRecordingId;
1049
- this.onRecordingStarted({
1050
- recording_id: payload.recording.recording_id,
1051
- started_at: payload.recording.started_at
1052
- });
1053
- }
1054
- } else if (this.lastRecordingId) {
1055
- const recordingId = this.lastRecordingId;
1056
- this.lastRecordingId = null;
1057
- this.onRecordingStopped({
1058
- recording_id: recordingId,
1059
- stopped_at: (/* @__PURE__ */ new Date()).toISOString()
1060
- });
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);
1061
1004
  }
1062
1005
  }
1063
1006
  rememberTrack(track) {
@@ -1095,8 +1038,21 @@ var SFUClient = class {
1095
1038
  for (const participant of this.participants.values()) {
1096
1039
  if (participant.participant_id !== this.currentParticipantId) {
1097
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
+ );
1098
1050
  }
1099
1051
  }
1052
+ if (Array.isArray(payload.chat_history)) {
1053
+ for (const message of payload.chat_history) this.onChatMessage(message);
1054
+ }
1055
+ this.onRoomUpdate(payload);
1100
1056
  if (this.localStream) {
1101
1057
  await this.publishExistingTracks();
1102
1058
  }
@@ -1469,9 +1425,6 @@ var SFUClient = class {
1469
1425
  }
1470
1426
  this.screenStream = screenStream;
1471
1427
  try {
1472
- if (!this.send({ type: "start_screen_share" })) {
1473
- throw new Error("Failed to signal start_screen_share");
1474
- }
1475
1428
  await this.screenSender.replaceTrack(screenTrack);
1476
1429
  this.screenTransceiver.direction = "sendonly";
1477
1430
  const offer = await this.publisher.createOffer();
@@ -1483,6 +1436,7 @@ var SFUClient = class {
1483
1436
  sdp: offer.sdp
1484
1437
  }
1485
1438
  });
1439
+ this.send({ type: "start_screen_share" });
1486
1440
  screenTrack.onended = () => {
1487
1441
  void this.stopScreenShare();
1488
1442
  };
@@ -1543,26 +1497,52 @@ var SFUClient = class {
1543
1497
  }
1544
1498
  }
1545
1499
  muteAudio(muted) {
1500
+ this.desiredAudioMuted = muted;
1546
1501
  this.localStream?.getAudioTracks().forEach((track) => {
1547
1502
  track.enabled = !muted;
1548
1503
  });
1549
1504
  this.send({
1550
1505
  type: "set_media_state",
1551
- payload: {
1552
- audio_muted: muted,
1553
- video_muted: !(this.localStream?.getVideoTracks()[0]?.enabled ?? true)
1554
- }
1506
+ payload: { audio_muted: muted, video_muted: this.desiredVideoMuted }
1555
1507
  });
1508
+ const trackId = this.localTrackIds.get("audio");
1509
+ if (trackId) {
1510
+ this.send({
1511
+ type: "track_state",
1512
+ payload: {
1513
+ track_id: trackId,
1514
+ muted
1515
+ }
1516
+ });
1517
+ }
1556
1518
  }
1557
1519
  muteVideo(muted) {
1520
+ this.desiredVideoMuted = muted;
1558
1521
  this.localStream?.getVideoTracks().forEach((track) => {
1559
1522
  track.enabled = !muted;
1560
1523
  });
1561
1524
  this.send({
1562
1525
  type: "set_media_state",
1526
+ payload: { audio_muted: this.desiredAudioMuted, video_muted: muted }
1527
+ });
1528
+ const trackId = this.localTrackIds.get("camera");
1529
+ if (trackId) {
1530
+ this.send({
1531
+ type: "track_state",
1532
+ payload: {
1533
+ track_id: trackId,
1534
+ muted
1535
+ }
1536
+ });
1537
+ }
1538
+ }
1539
+ sendChatMessage(input) {
1540
+ return this.send({
1541
+ type: "send_chat",
1563
1542
  payload: {
1564
- audio_muted: !(this.localStream?.getAudioTracks()[0]?.enabled ?? true),
1565
- video_muted: muted
1543
+ message: input.message,
1544
+ reply_to: input.reply_to ?? null,
1545
+ target: input.target ?? null
1566
1546
  }
1567
1547
  });
1568
1548
  }
@@ -2023,11 +2003,7 @@ var VideoSDKCore = class {
2023
2003
  console.log(`[SFU] ${resumed ? "Resumed" : "Joined"} room`, payload);
2024
2004
  this.state.updateLocalParticipant({
2025
2005
  id: this.myId,
2026
- name: this.participantName,
2027
- media: {
2028
- micEnabled: this.localStream?.getAudioTracks()[0]?.enabled ?? true,
2029
- camEnabled: this.localStream?.getVideoTracks()[0]?.enabled ?? true
2030
- }
2006
+ name: this.participantName
2031
2007
  });
2032
2008
  },
2033
2009
  onDisconnected: () => {
@@ -2037,13 +2013,43 @@ var VideoSDKCore = class {
2037
2013
  console.warn(`[SFU] Reconnecting, attempt ${attempt}`);
2038
2014
  },
2039
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
+ },
2040
2051
  onJoinPending: (info) => {
2041
2052
  console.log("[SFU] Join request pending", info);
2042
- this.events.onEntryRequested?.({
2043
- requestId: info.request_id,
2044
- participantId: this.myId,
2045
- name: this.participantName
2046
- });
2047
2053
  },
2048
2054
  onJoinApproved: (requestId) => {
2049
2055
  console.log("[SFU] Join approved", requestId);
@@ -2076,6 +2082,19 @@ var VideoSDKCore = class {
2076
2082
  false
2077
2083
  );
2078
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
+ },
2079
2098
  onJoinRequested: (request) => {
2080
2099
  const requestId = request?.request_id || request?.requestId;
2081
2100
  const participantId = request?.user_id || request?.participantId;
@@ -2096,44 +2115,22 @@ var VideoSDKCore = class {
2096
2115
  return;
2097
2116
  }
2098
2117
  const pname = participant.user_metadata?.name || participant.display_name || "Participant";
2099
- const participantMedia = participant.media || {};
2100
- const participantState = {
2118
+ this.state.addParticipant({
2101
2119
  id: pid,
2102
2120
  name: pname,
2103
- connectionState: participant.presence === "reconnecting" ? "reconnecting" : "connected",
2121
+ connectionState: "connected",
2104
2122
  media: {
2105
- micEnabled: !Boolean(participant.audio_muted),
2106
- camEnabled: !Boolean(participant.video_muted),
2107
- isScreenSharing: Boolean(participant.screen_sharing),
2108
- screenStream: participantMedia.screenStream ?? null,
2123
+ micEnabled: true,
2124
+ camEnabled: true,
2125
+ isScreenSharing: false,
2109
2126
  stream: null
2110
2127
  }
2111
- };
2112
- if (!this.state.addParticipant(participantState)) {
2113
- this.state.updateParticipantMedia(pid, participantState.media);
2114
- }
2128
+ });
2115
2129
  this.events.onUserJoined?.({
2116
2130
  id: pid,
2117
2131
  name: pname
2118
2132
  });
2119
2133
  },
2120
- onParticipantMediaState: (participantId, audioMuted, videoMuted) => {
2121
- if (participantId === this.myId) {
2122
- this.state.updateLocalParticipant({
2123
- media: {
2124
- micEnabled: !audioMuted,
2125
- camEnabled: !videoMuted
2126
- }
2127
- });
2128
- return;
2129
- }
2130
- this.state.updateParticipantMedia(participantId, {
2131
- micEnabled: !audioMuted,
2132
- camEnabled: !videoMuted
2133
- });
2134
- this.events.onMuteStateChanged?.(participantId, "audio", audioMuted);
2135
- this.events.onMuteStateChanged?.(participantId, "video", videoMuted);
2136
- },
2137
2134
  onParticipantPresence: (participantId, presence) => {
2138
2135
  if (participantId === this.myId) {
2139
2136
  return;
@@ -2166,6 +2163,17 @@ var VideoSDKCore = class {
2166
2163
  this.events.onUserLeft?.(pid);
2167
2164
  },
2168
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
+ },
2169
2177
  onTrackPublished: (track) => {
2170
2178
  console.log("[SFU] Track published", track);
2171
2179
  },
@@ -2235,6 +2243,9 @@ var VideoSDKCore = class {
2235
2243
  // SCREEN SHARE
2236
2244
  onScreenShareStarted: (participantId, _track) => {
2237
2245
  console.log("[SFU] Screen share started", participantId);
2246
+ if (participantId === this.myId && !this.screenStream) {
2247
+ this.screenStream = this.sfuClient?.getScreenStream() ?? null;
2248
+ }
2238
2249
  this.state.setPresenterId(participantId);
2239
2250
  if (participantId === this.myId) {
2240
2251
  this.state.updateLocalParticipant({
@@ -2297,9 +2308,7 @@ var VideoSDKCore = class {
2297
2308
  client.setLocalStream(this.localStream);
2298
2309
  await client.getToken(roomId, {
2299
2310
  displayName: name,
2300
- participantId: this.myId,
2301
- audioMuted: !this.localStream?.getAudioTracks()[0]?.enabled,
2302
- videoMuted: !this.localStream?.getVideoTracks()[0]?.enabled
2311
+ participantId: this.myId
2303
2312
  });
2304
2313
  }
2305
2314
  // SFU STATE
@@ -2463,7 +2472,6 @@ var VideoSDKCore = class {
2463
2472
  screenTrack: this.screenStream.getVideoTracks()[0]
2464
2473
  }
2465
2474
  });
2466
- this.state.setPresenterId(this.myId);
2467
2475
  return this.screenStream;
2468
2476
  } catch (err) {
2469
2477
  this.emitError(
@@ -2485,9 +2493,6 @@ var VideoSDKCore = class {
2485
2493
  this.screenStream.getTracks().forEach((track) => track.stop());
2486
2494
  this.screenStream = null;
2487
2495
  this.isScreenSharing = false;
2488
- if (this.state.presenterId === this.myId) {
2489
- this.state.setPresenterId(null);
2490
- }
2491
2496
  this.state.updateLocalParticipant({
2492
2497
  media: {
2493
2498
  isScreenSharing: false,
@@ -2521,18 +2526,13 @@ var VideoSDKCore = class {
2521
2526
  console.warn("SFU Client not connected");
2522
2527
  return;
2523
2528
  }
2524
- const senderName = this.state.localParticipant?.name || "Anonymous";
2525
- const msg = {
2526
- id: crypto.randomUUID(),
2527
- sender_id: this.myId,
2528
- sender_name: senderName,
2529
- text: payload.message.trim(),
2530
- timestamp: Date.now(),
2529
+ const message = payload.message.trim();
2530
+ if (!message) return;
2531
+ this.sfuClient.sendChatMessage({
2532
+ message,
2531
2533
  reply_to: payload.reply_to ?? null,
2532
2534
  target: payload.target ?? null
2533
- };
2534
- this.state.addChatMessage(msg);
2535
- this.events.onChatMessage?.(msg);
2535
+ });
2536
2536
  }
2537
2537
  // CLEANUP
2538
2538
  cleanupRemoteParticipant(participantId) {