@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.js CHANGED
@@ -403,6 +403,8 @@ var SFUClient = class {
403
403
  this.intentionalClose = false;
404
404
  this.leaveAcknowledged = false;
405
405
  this.joiningWithResume = false;
406
+ this.desiredAudioMuted = false;
407
+ this.desiredVideoMuted = false;
406
408
  this.reconnectTimer = null;
407
409
  this.reconnectAttempt = 0;
408
410
  this.maxReconnectAttempts = 8;
@@ -428,7 +430,6 @@ var SFUClient = class {
428
430
  this.trackToSource = /* @__PURE__ */ new Map();
429
431
  this.localTrackIds = /* @__PURE__ */ new Map();
430
432
  this.subscriberStatsTimer = null;
431
- this.lastRecordingId = null;
432
433
  this.heartbeatTimer = null;
433
434
  this.apiBase = options.apiBase || "https://api.example.com/api";
434
435
  this.wsBase = options.wsBase || "wss://api.example.com/ws";
@@ -441,8 +442,6 @@ var SFUClient = class {
441
442
  });
442
443
  this.onParticipantPresence = options.onParticipantPresence || (() => {
443
444
  });
444
- this.onParticipantMediaState = options.onParticipantMediaState || (() => {
445
- });
446
445
  this.onTrackPublished = options.onTrackPublished || (() => {
447
446
  });
448
447
  this.onTrackUnpublished = options.onTrackUnpublished || (() => {
@@ -474,6 +473,12 @@ var SFUClient = class {
474
473
  this.onRecordingStopped = options.onRecordingStopped || (() => {
475
474
  });
476
475
  this.onError = options.onError || console.error;
476
+ this.onChatMessage = options.onChatMessage || (() => {
477
+ });
478
+ this.onRoomUpdate = options.onRoomUpdate || (() => {
479
+ });
480
+ this.onParticipantMediaState = options.onParticipantMediaState || (() => {
481
+ });
477
482
  this.autoSubscribe = options.autoSubscribe !== false;
478
483
  }
479
484
  startHeartbeat() {
@@ -593,11 +598,11 @@ var SFUClient = class {
593
598
  this.room = nextRoom;
594
599
  this.joinOptions = {
595
600
  displayName: options.displayName,
596
- avatarUrl: options.avatarUrl,
597
- audioMuted: options.audioMuted,
598
- videoMuted: options.videoMuted
601
+ avatarUrl: options.avatarUrl
599
602
  };
600
603
  if (options.participantId) this.participantId = options.participantId;
604
+ if (typeof options.audioMuted === "boolean") this.desiredAudioMuted = options.audioMuted;
605
+ if (typeof options.videoMuted === "boolean") this.desiredVideoMuted = options.videoMuted;
601
606
  if (!this.resumeToken) {
602
607
  this.resumeToken = this.loadResumeToken(this.room);
603
608
  }
@@ -665,8 +670,8 @@ var SFUClient = class {
665
670
  resume_token: this.resumeToken,
666
671
  display_name: options.displayName,
667
672
  avatar_url: options.avatarUrl,
668
- audio_muted: options.audioMuted ?? this.joinOptions.audioMuted ?? false,
669
- video_muted: options.videoMuted ?? this.joinOptions.videoMuted ?? false
673
+ audio_muted: this.desiredAudioMuted,
674
+ video_muted: this.desiredVideoMuted
670
675
  };
671
676
  if (this.pendingJoinRequestId) {
672
677
  this.send({
@@ -780,9 +785,7 @@ var SFUClient = class {
780
785
  room_code: this.room,
781
786
  resume_token: this.resumeToken,
782
787
  display_name: this.joinOptions.displayName,
783
- avatar_url: this.joinOptions.avatarUrl,
784
- audio_muted: this.joinOptions.audioMuted ?? false,
785
- video_muted: this.joinOptions.videoMuted ?? false
788
+ avatar_url: this.joinOptions.avatarUrl
786
789
  }
787
790
  });
788
791
  }
@@ -843,6 +846,16 @@ var SFUClient = class {
843
846
  this.onParticipantLeft(participantId);
844
847
  break;
845
848
  }
849
+ case "participant_media_state": {
850
+ const participantId = payload.participant_id;
851
+ if (!participantId) break;
852
+ this.onParticipantMediaState(
853
+ participantId,
854
+ Boolean(payload.audio_muted),
855
+ Boolean(payload.video_muted)
856
+ );
857
+ break;
858
+ }
846
859
  case "participant_presence":
847
860
  case "participant_presence_changed": {
848
861
  const participantId = payload.participant_id;
@@ -923,7 +936,6 @@ var SFUClient = class {
923
936
  break;
924
937
  }
925
938
  case "recording_started": {
926
- this.lastRecordingId = payload.recording_id || this.lastRecordingId;
927
939
  this.onRecordingStarted({
928
940
  recording_id: payload.recording_id,
929
941
  started_at: payload.started_at
@@ -931,7 +943,6 @@ var SFUClient = class {
931
943
  break;
932
944
  }
933
945
  case "recording_stopped": {
934
- this.lastRecordingId = null;
935
946
  this.onRecordingStopped({
936
947
  recording_id: payload.recording_id,
937
948
  stopped_at: payload.stopped_at
@@ -968,26 +979,34 @@ var SFUClient = class {
968
979
  break;
969
980
  }
970
981
  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
- });
982
+ const participants = payload.participants || [];
983
+ this.onRoomStateReconciled(payload);
984
+ this.onRoomUpdate(payload);
985
+ for (const participant of participants) {
986
+ const pid = participant?.participant_id;
987
+ if (!pid) continue;
988
+ if (pid !== this.currentParticipantId) {
989
+ this.participants.set(pid, participant);
990
+ this.onParticipantPresence(pid, String(participant.presence || "connected"));
991
+ this.onParticipantMediaState(pid, Boolean(participant.audio_muted), Boolean(participant.video_muted));
984
992
  }
985
993
  }
986
- this.onParticipantMediaState?.(
987
- participantId,
988
- Boolean(payload.audio_muted),
989
- Boolean(payload.video_muted)
990
- );
994
+ for (const [pid] of this.participants) {
995
+ if (!participants.some((p) => p?.participant_id === pid)) {
996
+ this.participants.delete(pid);
997
+ this.removeRemoteParticipant(pid);
998
+ this.onParticipantLeft(pid);
999
+ }
1000
+ }
1001
+ for (const track of payload.tracks || []) this.rememberTrack(track);
1002
+ if (Array.isArray(payload.chat_history)) {
1003
+ for (const message2 of payload.chat_history) this.onChatMessage(message2);
1004
+ }
1005
+ break;
1006
+ }
1007
+ case "chat_message": {
1008
+ const msg = payload.message || payload;
1009
+ if (msg?.id) this.onChatMessage(msg);
991
1010
  break;
992
1011
  }
993
1012
  case "error": {
@@ -1012,88 +1031,12 @@ var SFUClient = class {
1012
1031
  console.debug("[SFU] Ignoring unknown server message", message);
1013
1032
  }
1014
1033
  }
1015
- async reconcileRoomState(payload) {
1016
- const nextParticipants = /* @__PURE__ */ new Map();
1017
- for (const participant of payload.participants || []) {
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) {
1035
- this.onParticipantJoined(participant);
1036
- }
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
- );
1055
- }
1056
- }
1057
- const nextTracks = /* @__PURE__ */ new Map();
1058
- for (const track of payload.tracks || []) {
1059
- if (track?.track_id) {
1060
- nextTracks.set(track.track_id, track);
1061
- this.rememberTrack(track);
1062
- }
1063
- }
1064
- for (const [trackId, oldTrack] of [...this.trackMetadata.entries()]) {
1065
- if (!nextTracks.has(trackId)) {
1066
- this.trackMetadata.delete(trackId);
1067
- this.trackToParticipant.delete(trackId);
1068
- this.trackToSource.delete(trackId);
1069
- this.subscribedTracks.delete(trackId);
1070
- if (oldTrack.participant_id !== this.currentParticipantId) {
1071
- this.onTrackUnpublished(oldTrack);
1072
- }
1073
- }
1074
- }
1075
- if (this.autoSubscribe) {
1076
- const missing = [...nextTracks.values()].filter(
1077
- (track) => track.participant_id !== this.currentParticipantId && !this.subscribedTracks.has(track.track_id)
1078
- ).map((track) => track.track_id);
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
- });
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
- });
1034
+ onRoomStateReconciled(payload) {
1035
+ const participants = payload.participants || [];
1036
+ const local = participants.find((p) => p?.participant_id === this.currentParticipantId);
1037
+ if (local) {
1038
+ this.desiredAudioMuted = Boolean(local.audio_muted);
1039
+ this.desiredVideoMuted = Boolean(local.video_muted);
1097
1040
  }
1098
1041
  }
1099
1042
  rememberTrack(track) {
@@ -1131,8 +1074,21 @@ var SFUClient = class {
1131
1074
  for (const participant of this.participants.values()) {
1132
1075
  if (participant.participant_id !== this.currentParticipantId) {
1133
1076
  this.onParticipantJoined(participant);
1077
+ this.onParticipantPresence(
1078
+ participant.participant_id,
1079
+ String(participant.presence || "connected")
1080
+ );
1081
+ this.onParticipantMediaState(
1082
+ participant.participant_id,
1083
+ Boolean(participant.audio_muted),
1084
+ Boolean(participant.video_muted)
1085
+ );
1134
1086
  }
1135
1087
  }
1088
+ if (Array.isArray(payload.chat_history)) {
1089
+ for (const message of payload.chat_history) this.onChatMessage(message);
1090
+ }
1091
+ this.onRoomUpdate(payload);
1136
1092
  if (this.localStream) {
1137
1093
  await this.publishExistingTracks();
1138
1094
  }
@@ -1505,9 +1461,6 @@ var SFUClient = class {
1505
1461
  }
1506
1462
  this.screenStream = screenStream;
1507
1463
  try {
1508
- if (!this.send({ type: "start_screen_share" })) {
1509
- throw new Error("Failed to signal start_screen_share");
1510
- }
1511
1464
  await this.screenSender.replaceTrack(screenTrack);
1512
1465
  this.screenTransceiver.direction = "sendonly";
1513
1466
  const offer = await this.publisher.createOffer();
@@ -1519,6 +1472,7 @@ var SFUClient = class {
1519
1472
  sdp: offer.sdp
1520
1473
  }
1521
1474
  });
1475
+ this.send({ type: "start_screen_share" });
1522
1476
  screenTrack.onended = () => {
1523
1477
  void this.stopScreenShare();
1524
1478
  };
@@ -1579,26 +1533,52 @@ var SFUClient = class {
1579
1533
  }
1580
1534
  }
1581
1535
  muteAudio(muted) {
1536
+ this.desiredAudioMuted = muted;
1582
1537
  this.localStream?.getAudioTracks().forEach((track) => {
1583
1538
  track.enabled = !muted;
1584
1539
  });
1585
1540
  this.send({
1586
1541
  type: "set_media_state",
1587
- payload: {
1588
- audio_muted: muted,
1589
- video_muted: !(this.localStream?.getVideoTracks()[0]?.enabled ?? true)
1590
- }
1542
+ payload: { audio_muted: muted, video_muted: this.desiredVideoMuted }
1591
1543
  });
1544
+ const trackId = this.localTrackIds.get("audio");
1545
+ if (trackId) {
1546
+ this.send({
1547
+ type: "track_state",
1548
+ payload: {
1549
+ track_id: trackId,
1550
+ muted
1551
+ }
1552
+ });
1553
+ }
1592
1554
  }
1593
1555
  muteVideo(muted) {
1556
+ this.desiredVideoMuted = muted;
1594
1557
  this.localStream?.getVideoTracks().forEach((track) => {
1595
1558
  track.enabled = !muted;
1596
1559
  });
1597
1560
  this.send({
1598
1561
  type: "set_media_state",
1562
+ payload: { audio_muted: this.desiredAudioMuted, video_muted: muted }
1563
+ });
1564
+ const trackId = this.localTrackIds.get("camera");
1565
+ if (trackId) {
1566
+ this.send({
1567
+ type: "track_state",
1568
+ payload: {
1569
+ track_id: trackId,
1570
+ muted
1571
+ }
1572
+ });
1573
+ }
1574
+ }
1575
+ sendChatMessage(input) {
1576
+ return this.send({
1577
+ type: "send_chat",
1599
1578
  payload: {
1600
- audio_muted: !(this.localStream?.getAudioTracks()[0]?.enabled ?? true),
1601
- video_muted: muted
1579
+ message: input.message,
1580
+ reply_to: input.reply_to ?? null,
1581
+ target: input.target ?? null
1602
1582
  }
1603
1583
  });
1604
1584
  }
@@ -2059,11 +2039,7 @@ var VideoSDKCore = class {
2059
2039
  console.log(`[SFU] ${resumed ? "Resumed" : "Joined"} room`, payload);
2060
2040
  this.state.updateLocalParticipant({
2061
2041
  id: this.myId,
2062
- name: this.participantName,
2063
- media: {
2064
- micEnabled: this.localStream?.getAudioTracks()[0]?.enabled ?? true,
2065
- camEnabled: this.localStream?.getVideoTracks()[0]?.enabled ?? true
2066
- }
2042
+ name: this.participantName
2067
2043
  });
2068
2044
  },
2069
2045
  onDisconnected: () => {
@@ -2073,13 +2049,43 @@ var VideoSDKCore = class {
2073
2049
  console.warn(`[SFU] Reconnecting, attempt ${attempt}`);
2074
2050
  },
2075
2051
  // ADMISSION CONTROL
2052
+ onRoomUpdate: (payload) => {
2053
+ const participants = payload?.participants || [];
2054
+ const local = participants.find((p) => p?.participant_id === this.myId);
2055
+ if (local) {
2056
+ const media = this.state.localParticipant?.media;
2057
+ this.state.updateLocalParticipant({
2058
+ id: this.myId,
2059
+ name: this.participantName,
2060
+ media: {
2061
+ micEnabled: !Boolean(local.audio_muted),
2062
+ camEnabled: !Boolean(local.video_muted),
2063
+ isScreenSharing: Boolean(local.screen_sharing),
2064
+ screenStream: this.screenStream,
2065
+ screenTrack: this.screenStream?.getVideoTracks()[0]
2066
+ }
2067
+ });
2068
+ if (this.localStream) {
2069
+ this.localStream.getAudioTracks().forEach((track) => {
2070
+ track.enabled = !Boolean(local.audio_muted);
2071
+ });
2072
+ this.localStream.getVideoTracks().forEach((track) => {
2073
+ track.enabled = !Boolean(local.video_muted);
2074
+ });
2075
+ }
2076
+ if (local.screen_sharing) this.state.setPresenterId(this.myId);
2077
+ else if (this.state.presenterId === this.myId) this.state.setPresenterId(null);
2078
+ }
2079
+ const presenter = participants.find((p) => p?.screen_sharing);
2080
+ if (presenter) this.state.setPresenterId(presenter.participant_id);
2081
+ else if (!local?.screen_sharing) this.state.setPresenterId(null);
2082
+ for (const message of payload?.chat_history || []) {
2083
+ this.state.addChatMessage(message);
2084
+ }
2085
+ this.events.onRoomUpdate?.(payload);
2086
+ },
2076
2087
  onJoinPending: (info) => {
2077
2088
  console.log("[SFU] Join request pending", info);
2078
- this.events.onEntryRequested?.({
2079
- requestId: info.request_id,
2080
- participantId: this.myId,
2081
- name: this.participantName
2082
- });
2083
2089
  },
2084
2090
  onJoinApproved: (requestId) => {
2085
2091
  console.log("[SFU] Join approved", requestId);
@@ -2112,6 +2118,19 @@ var VideoSDKCore = class {
2112
2118
  false
2113
2119
  );
2114
2120
  },
2121
+ onChatMessage: (message) => {
2122
+ const msg = {
2123
+ id: message.id,
2124
+ text: message.text,
2125
+ sender_id: message.sender_id,
2126
+ sender_name: message.sender_name,
2127
+ timestamp: Number(message.timestamp),
2128
+ reply_to: message.reply_to ?? null,
2129
+ target: message.target ?? null
2130
+ };
2131
+ this.state.addChatMessage(msg);
2132
+ this.events.onChatMessage?.(msg);
2133
+ },
2115
2134
  onJoinRequested: (request) => {
2116
2135
  const requestId = request?.request_id || request?.requestId;
2117
2136
  const participantId = request?.user_id || request?.participantId;
@@ -2132,44 +2151,22 @@ var VideoSDKCore = class {
2132
2151
  return;
2133
2152
  }
2134
2153
  const pname = participant.user_metadata?.name || participant.display_name || "Participant";
2135
- const participantMedia = participant.media || {};
2136
- const participantState = {
2154
+ this.state.addParticipant({
2137
2155
  id: pid,
2138
2156
  name: pname,
2139
- connectionState: participant.presence === "reconnecting" ? "reconnecting" : "connected",
2157
+ connectionState: "connected",
2140
2158
  media: {
2141
- micEnabled: !Boolean(participant.audio_muted),
2142
- camEnabled: !Boolean(participant.video_muted),
2143
- isScreenSharing: Boolean(participant.screen_sharing),
2144
- screenStream: participantMedia.screenStream ?? null,
2159
+ micEnabled: true,
2160
+ camEnabled: true,
2161
+ isScreenSharing: false,
2145
2162
  stream: null
2146
2163
  }
2147
- };
2148
- if (!this.state.addParticipant(participantState)) {
2149
- this.state.updateParticipantMedia(pid, participantState.media);
2150
- }
2164
+ });
2151
2165
  this.events.onUserJoined?.({
2152
2166
  id: pid,
2153
2167
  name: pname
2154
2168
  });
2155
2169
  },
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
- },
2173
2170
  onParticipantPresence: (participantId, presence) => {
2174
2171
  if (participantId === this.myId) {
2175
2172
  return;
@@ -2202,6 +2199,17 @@ var VideoSDKCore = class {
2202
2199
  this.events.onUserLeft?.(pid);
2203
2200
  },
2204
2201
  // TRACKS
2202
+ onParticipantMediaState: (participantId, audioMuted, videoMuted) => {
2203
+ if (participantId === this.myId) return;
2204
+ const participant = this.state.getParticipant(participantId);
2205
+ if (!participant) return;
2206
+ this.state.updateParticipantMedia(participantId, {
2207
+ micEnabled: !audioMuted,
2208
+ camEnabled: !videoMuted
2209
+ });
2210
+ this.events.onMuteStateChanged?.(participantId, "audio", audioMuted);
2211
+ this.events.onMuteStateChanged?.(participantId, "video", videoMuted);
2212
+ },
2205
2213
  onTrackPublished: (track) => {
2206
2214
  console.log("[SFU] Track published", track);
2207
2215
  },
@@ -2271,6 +2279,9 @@ var VideoSDKCore = class {
2271
2279
  // SCREEN SHARE
2272
2280
  onScreenShareStarted: (participantId, _track) => {
2273
2281
  console.log("[SFU] Screen share started", participantId);
2282
+ if (participantId === this.myId && !this.screenStream) {
2283
+ this.screenStream = this.sfuClient?.getScreenStream() ?? null;
2284
+ }
2274
2285
  this.state.setPresenterId(participantId);
2275
2286
  if (participantId === this.myId) {
2276
2287
  this.state.updateLocalParticipant({
@@ -2333,9 +2344,7 @@ var VideoSDKCore = class {
2333
2344
  client.setLocalStream(this.localStream);
2334
2345
  await client.getToken(roomId, {
2335
2346
  displayName: name,
2336
- participantId: this.myId,
2337
- audioMuted: !this.localStream?.getAudioTracks()[0]?.enabled,
2338
- videoMuted: !this.localStream?.getVideoTracks()[0]?.enabled
2347
+ participantId: this.myId
2339
2348
  });
2340
2349
  }
2341
2350
  // SFU STATE
@@ -2499,7 +2508,6 @@ var VideoSDKCore = class {
2499
2508
  screenTrack: this.screenStream.getVideoTracks()[0]
2500
2509
  }
2501
2510
  });
2502
- this.state.setPresenterId(this.myId);
2503
2511
  return this.screenStream;
2504
2512
  } catch (err) {
2505
2513
  this.emitError(
@@ -2521,9 +2529,6 @@ var VideoSDKCore = class {
2521
2529
  this.screenStream.getTracks().forEach((track) => track.stop());
2522
2530
  this.screenStream = null;
2523
2531
  this.isScreenSharing = false;
2524
- if (this.state.presenterId === this.myId) {
2525
- this.state.setPresenterId(null);
2526
- }
2527
2532
  this.state.updateLocalParticipant({
2528
2533
  media: {
2529
2534
  isScreenSharing: false,
@@ -2557,18 +2562,13 @@ var VideoSDKCore = class {
2557
2562
  console.warn("SFU Client not connected");
2558
2563
  return;
2559
2564
  }
2560
- const senderName = this.state.localParticipant?.name || "Anonymous";
2561
- const msg = {
2562
- id: crypto.randomUUID(),
2563
- sender_id: this.myId,
2564
- sender_name: senderName,
2565
- text: payload.message.trim(),
2566
- timestamp: Date.now(),
2565
+ const message = payload.message.trim();
2566
+ if (!message) return;
2567
+ this.sfuClient.sendChatMessage({
2568
+ message,
2567
2569
  reply_to: payload.reply_to ?? null,
2568
2570
  target: payload.target ?? null
2569
- };
2570
- this.state.addChatMessage(msg);
2571
- this.events.onChatMessage?.(msg);
2571
+ });
2572
2572
  }
2573
2573
  // CLEANUP
2574
2574
  cleanupRemoteParticipant(participantId) {