@defcomm/meetingsdk 0.2.7 → 0.2.9

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
@@ -22,6 +22,7 @@ type Events = {
22
22
  onUserLeft?: (id: string) => void;
23
23
  onMeetingLeft?: () => void;
24
24
  onChatMessage?: (msg: ChatMessage) => void;
25
+ onReaction?: (reaction: Reaction) => void;
25
26
  onScreenShareStarted?: (peerId: string, stream: MediaStream) => void;
26
27
  onScreenShareStopped?: (peerId: string) => void;
27
28
  onMuteStateChanged?: (peerId: string, kind: "audio" | "video", muted: boolean) => void;
@@ -39,6 +40,13 @@ type Events = {
39
40
  stopped_at: string;
40
41
  }) => void;
41
42
  };
43
+ type Reaction = {
44
+ id: string;
45
+ participant_id: string;
46
+ sender_name?: string | null;
47
+ emoji: string;
48
+ timestamp: string | number;
49
+ };
42
50
  type ChatMessage = {
43
51
  id: string;
44
52
  text: string;
@@ -211,7 +219,7 @@ declare class VideoSDKCore {
211
219
  rejectJoinRequest(requestId: string, reason?: string): Promise<void>;
212
220
  toggleMic(): void;
213
221
  toggleCam(): void;
214
- startScreenShare(): Promise<MediaStream>;
222
+ startScreenShare(): Promise<MediaStream | null>;
215
223
  stopScreenShare(): void;
216
224
  startRecording(): Promise<void>;
217
225
  stopRecording(): Promise<void>;
@@ -219,6 +227,7 @@ declare class VideoSDKCore {
219
227
  recording: RecordingInfo | null;
220
228
  }>;
221
229
  sendChatMessage(payload: ChatInput): void;
230
+ sendReaction(reaction: string): void;
222
231
  private cleanupRemoteParticipant;
223
232
  disconnect(): Promise<void>;
224
233
  private performDisconnect;
@@ -285,6 +294,7 @@ interface SFUClientOptions {
285
294
  }) => void;
286
295
  onError?: (error: any) => void;
287
296
  onChatMessage?: (message: any) => void;
297
+ onReaction?: (reaction: any) => void;
288
298
  onRoomUpdate?: (data: any) => void;
289
299
  onParticipantMediaState?: (participantId: string, audioMuted: boolean, videoMuted: boolean) => void;
290
300
  autoSubscribe?: boolean;
@@ -307,6 +317,7 @@ declare class SFUClient {
307
317
  private iceServers;
308
318
  private requestId;
309
319
  private publisherRevision;
320
+ private screenShareGeneration;
310
321
  private currentParticipantId;
311
322
  private token;
312
323
  private room;
@@ -362,6 +373,7 @@ declare class SFUClient {
362
373
  private onRecordingStopped;
363
374
  private onError;
364
375
  private onChatMessage;
376
+ private onReaction;
365
377
  private onRoomUpdate;
366
378
  private onParticipantMediaState;
367
379
  private autoSubscribe;
@@ -424,7 +436,7 @@ declare class SFUClient {
424
436
  video?: boolean | undefined;
425
437
  }): Promise<MediaStream>;
426
438
  private publishExistingTracks;
427
- shareScreen(): Promise<MediaStream>;
439
+ shareScreen(): Promise<MediaStream | null>;
428
440
  stopScreenShare(): Promise<void>;
429
441
  muteAudio(muted: boolean): void;
430
442
  muteVideo(muted: boolean): void;
@@ -440,6 +452,7 @@ declare class SFUClient {
440
452
  rejectJoinRequest(requestId: string, reason?: string): Promise<void>;
441
453
  private isSocketOpen;
442
454
  private send;
455
+ sendReaction(reaction: string): void;
443
456
  private removeRemoteParticipant;
444
457
  leave(): Promise<void>;
445
458
  private closePeerConnectionsOnly;
@@ -466,9 +479,10 @@ type MeetingContextValue = {
466
479
  deleteRoom: (roomCode: string) => Promise<any>;
467
480
  toggleMic: () => void;
468
481
  toggleCam: () => void;
469
- startScreenShare: () => Promise<MediaStream>;
482
+ startScreenShare: () => Promise<MediaStream | null>;
470
483
  stopScreenShare: () => void;
471
484
  sendMessage: (input: ChatInput) => void;
485
+ sendReaction: (emoji: string) => void;
472
486
  room: {
473
487
  id: string | null;
474
488
  name: string | null;
@@ -491,6 +505,7 @@ type MeetingContextValue = {
491
505
  onTrackStateChanged: (cb: (track: any) => void) => () => void;
492
506
  onRecordingStarted: (cb: (recording: Recording) => void) => () => void;
493
507
  onRecordingStopped: (cb: (recording: any) => void) => () => void;
508
+ onReaction: (cb: (reaction: Reaction) => void) => () => void;
494
509
  onActiveSpeakersChanged: (cb: (participantIds: string[]) => void) => () => void;
495
510
  };
496
511
  declare const MeetingProvider: ({ config, children, }: {
@@ -507,6 +522,7 @@ declare const useMeeting: (handlers?: {
507
522
  onTrackStateChanged?: (track: any) => void;
508
523
  onRecordingStarted?: (recording: any) => void;
509
524
  onRecordingStopped?: (recording: any) => void;
525
+ onReaction?: (reaction: any) => void;
510
526
  onActiveSpeakersChanged?: (participantIds: string[]) => void;
511
527
  }) => {
512
528
  join: (config?: Omit<MeetingConfig, "participantId">) => Promise<void>;
@@ -521,9 +537,10 @@ declare const useMeeting: (handlers?: {
521
537
  deleteRoom: (roomCode: string) => Promise<any>;
522
538
  toggleMic: () => void;
523
539
  toggleCam: () => void;
524
- startScreenShare: () => Promise<MediaStream>;
540
+ startScreenShare: () => Promise<MediaStream | null>;
525
541
  stopScreenShare: () => void;
526
542
  sendMessage: (input: ChatInput) => void;
543
+ sendReaction: (emoji: string) => void;
527
544
  room: {
528
545
  id: string | null;
529
546
  name: string | null;
@@ -549,6 +566,7 @@ declare const useMeeting: (handlers?: {
549
566
  onTrackStateChanged: (cb: (track: any) => void) => () => void;
550
567
  onRecordingStarted: (cb: (recording: Recording) => void) => () => void;
551
568
  onRecordingStopped: (cb: (recording: any) => void) => () => void;
569
+ onReaction: (cb: (reaction: Reaction) => void) => () => void;
552
570
  onActiveSpeakersChanged: (cb: (participantIds: string[]) => void) => () => void;
553
571
  };
554
572
 
@@ -629,4 +647,4 @@ declare class ActiveSpeakerDetector {
629
647
  destroy(): Promise<void>;
630
648
  }
631
649
 
632
- export { ActiveSpeakerDetector, type ChatInput, type MeetingConfig, MeetingProvider, MeetingState, type Participant, type ParticipantInfo, type RecordingInfo, SFUClient, type SFUConnectionState, type TrackInfo, VideoSDKCore, useLocalParticipant, useMeeting, useMeetingContext, useMeetingPreview, useParticipants, useRemoteMedia };
650
+ export { ActiveSpeakerDetector, type ChatInput, type MeetingConfig, MeetingProvider, MeetingState, type Participant, type ParticipantInfo, type Reaction, type RecordingInfo, SFUClient, type SFUConnectionState, type TrackInfo, VideoSDKCore, useLocalParticipant, useMeeting, useMeetingContext, useMeetingPreview, useParticipants, useRemoteMedia };
package/dist/index.d.ts CHANGED
@@ -22,6 +22,7 @@ type Events = {
22
22
  onUserLeft?: (id: string) => void;
23
23
  onMeetingLeft?: () => void;
24
24
  onChatMessage?: (msg: ChatMessage) => void;
25
+ onReaction?: (reaction: Reaction) => void;
25
26
  onScreenShareStarted?: (peerId: string, stream: MediaStream) => void;
26
27
  onScreenShareStopped?: (peerId: string) => void;
27
28
  onMuteStateChanged?: (peerId: string, kind: "audio" | "video", muted: boolean) => void;
@@ -39,6 +40,13 @@ type Events = {
39
40
  stopped_at: string;
40
41
  }) => void;
41
42
  };
43
+ type Reaction = {
44
+ id: string;
45
+ participant_id: string;
46
+ sender_name?: string | null;
47
+ emoji: string;
48
+ timestamp: string | number;
49
+ };
42
50
  type ChatMessage = {
43
51
  id: string;
44
52
  text: string;
@@ -211,7 +219,7 @@ declare class VideoSDKCore {
211
219
  rejectJoinRequest(requestId: string, reason?: string): Promise<void>;
212
220
  toggleMic(): void;
213
221
  toggleCam(): void;
214
- startScreenShare(): Promise<MediaStream>;
222
+ startScreenShare(): Promise<MediaStream | null>;
215
223
  stopScreenShare(): void;
216
224
  startRecording(): Promise<void>;
217
225
  stopRecording(): Promise<void>;
@@ -219,6 +227,7 @@ declare class VideoSDKCore {
219
227
  recording: RecordingInfo | null;
220
228
  }>;
221
229
  sendChatMessage(payload: ChatInput): void;
230
+ sendReaction(reaction: string): void;
222
231
  private cleanupRemoteParticipant;
223
232
  disconnect(): Promise<void>;
224
233
  private performDisconnect;
@@ -285,6 +294,7 @@ interface SFUClientOptions {
285
294
  }) => void;
286
295
  onError?: (error: any) => void;
287
296
  onChatMessage?: (message: any) => void;
297
+ onReaction?: (reaction: any) => void;
288
298
  onRoomUpdate?: (data: any) => void;
289
299
  onParticipantMediaState?: (participantId: string, audioMuted: boolean, videoMuted: boolean) => void;
290
300
  autoSubscribe?: boolean;
@@ -307,6 +317,7 @@ declare class SFUClient {
307
317
  private iceServers;
308
318
  private requestId;
309
319
  private publisherRevision;
320
+ private screenShareGeneration;
310
321
  private currentParticipantId;
311
322
  private token;
312
323
  private room;
@@ -362,6 +373,7 @@ declare class SFUClient {
362
373
  private onRecordingStopped;
363
374
  private onError;
364
375
  private onChatMessage;
376
+ private onReaction;
365
377
  private onRoomUpdate;
366
378
  private onParticipantMediaState;
367
379
  private autoSubscribe;
@@ -424,7 +436,7 @@ declare class SFUClient {
424
436
  video?: boolean | undefined;
425
437
  }): Promise<MediaStream>;
426
438
  private publishExistingTracks;
427
- shareScreen(): Promise<MediaStream>;
439
+ shareScreen(): Promise<MediaStream | null>;
428
440
  stopScreenShare(): Promise<void>;
429
441
  muteAudio(muted: boolean): void;
430
442
  muteVideo(muted: boolean): void;
@@ -440,6 +452,7 @@ declare class SFUClient {
440
452
  rejectJoinRequest(requestId: string, reason?: string): Promise<void>;
441
453
  private isSocketOpen;
442
454
  private send;
455
+ sendReaction(reaction: string): void;
443
456
  private removeRemoteParticipant;
444
457
  leave(): Promise<void>;
445
458
  private closePeerConnectionsOnly;
@@ -466,9 +479,10 @@ type MeetingContextValue = {
466
479
  deleteRoom: (roomCode: string) => Promise<any>;
467
480
  toggleMic: () => void;
468
481
  toggleCam: () => void;
469
- startScreenShare: () => Promise<MediaStream>;
482
+ startScreenShare: () => Promise<MediaStream | null>;
470
483
  stopScreenShare: () => void;
471
484
  sendMessage: (input: ChatInput) => void;
485
+ sendReaction: (emoji: string) => void;
472
486
  room: {
473
487
  id: string | null;
474
488
  name: string | null;
@@ -491,6 +505,7 @@ type MeetingContextValue = {
491
505
  onTrackStateChanged: (cb: (track: any) => void) => () => void;
492
506
  onRecordingStarted: (cb: (recording: Recording) => void) => () => void;
493
507
  onRecordingStopped: (cb: (recording: any) => void) => () => void;
508
+ onReaction: (cb: (reaction: Reaction) => void) => () => void;
494
509
  onActiveSpeakersChanged: (cb: (participantIds: string[]) => void) => () => void;
495
510
  };
496
511
  declare const MeetingProvider: ({ config, children, }: {
@@ -507,6 +522,7 @@ declare const useMeeting: (handlers?: {
507
522
  onTrackStateChanged?: (track: any) => void;
508
523
  onRecordingStarted?: (recording: any) => void;
509
524
  onRecordingStopped?: (recording: any) => void;
525
+ onReaction?: (reaction: any) => void;
510
526
  onActiveSpeakersChanged?: (participantIds: string[]) => void;
511
527
  }) => {
512
528
  join: (config?: Omit<MeetingConfig, "participantId">) => Promise<void>;
@@ -521,9 +537,10 @@ declare const useMeeting: (handlers?: {
521
537
  deleteRoom: (roomCode: string) => Promise<any>;
522
538
  toggleMic: () => void;
523
539
  toggleCam: () => void;
524
- startScreenShare: () => Promise<MediaStream>;
540
+ startScreenShare: () => Promise<MediaStream | null>;
525
541
  stopScreenShare: () => void;
526
542
  sendMessage: (input: ChatInput) => void;
543
+ sendReaction: (emoji: string) => void;
527
544
  room: {
528
545
  id: string | null;
529
546
  name: string | null;
@@ -549,6 +566,7 @@ declare const useMeeting: (handlers?: {
549
566
  onTrackStateChanged: (cb: (track: any) => void) => () => void;
550
567
  onRecordingStarted: (cb: (recording: Recording) => void) => () => void;
551
568
  onRecordingStopped: (cb: (recording: any) => void) => () => void;
569
+ onReaction: (cb: (reaction: Reaction) => void) => () => void;
552
570
  onActiveSpeakersChanged: (cb: (participantIds: string[]) => void) => () => void;
553
571
  };
554
572
 
@@ -629,4 +647,4 @@ declare class ActiveSpeakerDetector {
629
647
  destroy(): Promise<void>;
630
648
  }
631
649
 
632
- export { ActiveSpeakerDetector, type ChatInput, type MeetingConfig, MeetingProvider, MeetingState, type Participant, type ParticipantInfo, type RecordingInfo, SFUClient, type SFUConnectionState, type TrackInfo, VideoSDKCore, useLocalParticipant, useMeeting, useMeetingContext, useMeetingPreview, useParticipants, useRemoteMedia };
650
+ export { ActiveSpeakerDetector, type ChatInput, type MeetingConfig, MeetingProvider, MeetingState, type Participant, type ParticipantInfo, type Reaction, type RecordingInfo, SFUClient, type SFUConnectionState, type TrackInfo, VideoSDKCore, useLocalParticipant, useMeeting, useMeetingContext, useMeetingPreview, useParticipants, useRemoteMedia };
package/dist/index.js CHANGED
@@ -393,6 +393,7 @@ var SFUClient = class {
393
393
  this.iceServers = [];
394
394
  this.requestId = 0;
395
395
  this.publisherRevision = 0;
396
+ this.screenShareGeneration = 0;
396
397
  this.currentParticipantId = null;
397
398
  this.token = null;
398
399
  this.room = null;
@@ -475,6 +476,8 @@ var SFUClient = class {
475
476
  this.onError = options.onError || console.error;
476
477
  this.onChatMessage = options.onChatMessage || (() => {
477
478
  });
479
+ this.onReaction = options.onReaction || (() => {
480
+ });
478
481
  this.onRoomUpdate = options.onRoomUpdate || (() => {
479
482
  });
480
483
  this.onParticipantMediaState = options.onParticipantMediaState || (() => {
@@ -601,8 +604,10 @@ var SFUClient = class {
601
604
  avatarUrl: options.avatarUrl
602
605
  };
603
606
  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;
607
+ if (typeof options.audioMuted === "boolean")
608
+ this.desiredAudioMuted = options.audioMuted;
609
+ if (typeof options.videoMuted === "boolean")
610
+ this.desiredVideoMuted = options.videoMuted;
606
611
  if (!this.resumeToken) {
607
612
  this.resumeToken = this.loadResumeToken(this.room);
608
613
  }
@@ -987,8 +992,15 @@ var SFUClient = class {
987
992
  if (!pid) continue;
988
993
  if (pid !== this.currentParticipantId) {
989
994
  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));
995
+ this.onParticipantPresence(
996
+ pid,
997
+ String(participant.presence || "connected")
998
+ );
999
+ this.onParticipantMediaState(
1000
+ pid,
1001
+ Boolean(participant.audio_muted),
1002
+ Boolean(participant.video_muted)
1003
+ );
992
1004
  }
993
1005
  }
994
1006
  for (const [pid] of this.participants) {
@@ -1000,7 +1012,8 @@ var SFUClient = class {
1000
1012
  }
1001
1013
  for (const track of payload.tracks || []) this.rememberTrack(track);
1002
1014
  if (Array.isArray(payload.chat_history)) {
1003
- for (const message2 of payload.chat_history) this.onChatMessage(message2);
1015
+ for (const message2 of payload.chat_history)
1016
+ this.onChatMessage(message2);
1004
1017
  }
1005
1018
  break;
1006
1019
  }
@@ -1009,6 +1022,11 @@ var SFUClient = class {
1009
1022
  if (msg?.id) this.onChatMessage(msg);
1010
1023
  break;
1011
1024
  }
1025
+ case "reaction": {
1026
+ const reaction = payload.reaction || payload;
1027
+ if (reaction?.id && reaction?.emoji) this.onReaction(reaction);
1028
+ break;
1029
+ }
1012
1030
  case "error": {
1013
1031
  const message2 = payload.message || "SFU signaling error";
1014
1032
  if (this.state === "reconnecting" && message2.includes("participant session is stale")) {
@@ -1033,7 +1051,9 @@ var SFUClient = class {
1033
1051
  }
1034
1052
  onRoomStateReconciled(payload) {
1035
1053
  const participants = payload.participants || [];
1036
- const local = participants.find((p) => p?.participant_id === this.currentParticipantId);
1054
+ const local = participants.find(
1055
+ (p) => p?.participant_id === this.currentParticipantId
1056
+ );
1037
1057
  if (local) {
1038
1058
  this.desiredAudioMuted = Boolean(local.audio_muted);
1039
1059
  this.desiredVideoMuted = Boolean(local.video_muted);
@@ -1441,15 +1461,20 @@ var SFUClient = class {
1441
1461
  }
1442
1462
  }
1443
1463
  async shareScreen() {
1444
- if (!this.publisher) {
1464
+ if (!this.publisher)
1445
1465
  throw new Error("Publisher PeerConnection is not initialized");
1446
- }
1447
- if (!this.screenTransceiver || !this.screenSender) {
1466
+ if (!this.screenTransceiver || !this.screenSender)
1448
1467
  throw new Error("Screen transceiver is not initialized");
1468
+ const existing = this.screenStream;
1469
+ const existingTrack = this.screenSender.track;
1470
+ if (existing || existingTrack && existingTrack.readyState === "live") {
1471
+ return existing;
1449
1472
  }
1450
- if (this.screenStream) {
1451
- return this.screenStream;
1473
+ try {
1474
+ await this.screenSender.replaceTrack(null);
1475
+ } catch {
1452
1476
  }
1477
+ this.screenTransceiver.direction = "inactive";
1453
1478
  const screenStream = await navigator.mediaDevices.getDisplayMedia({
1454
1479
  video: true,
1455
1480
  audio: false
@@ -1459,29 +1484,22 @@ var SFUClient = class {
1459
1484
  screenStream.getTracks().forEach((track) => track.stop());
1460
1485
  throw new Error("Screen share returned no video track");
1461
1486
  }
1487
+ const generation = ++this.screenShareGeneration;
1462
1488
  this.screenStream = screenStream;
1463
1489
  try {
1464
1490
  await this.screenSender.replaceTrack(screenTrack);
1465
1491
  this.screenTransceiver.direction = "sendonly";
1492
+ this.send({ type: "start_screen_share" });
1466
1493
  const offer = await this.publisher.createOffer();
1467
1494
  await this.publisher.setLocalDescription(offer);
1468
1495
  this.send({
1469
1496
  type: "publish_offer",
1470
- payload: {
1471
- revision: ++this.publisherRevision,
1472
- sdp: offer.sdp
1473
- }
1497
+ payload: { revision: ++this.publisherRevision, sdp: offer.sdp }
1474
1498
  });
1475
- this.send({ type: "start_screen_share" });
1476
1499
  screenTrack.onended = () => {
1500
+ if (generation !== this.screenShareGeneration) return;
1477
1501
  void this.stopScreenShare();
1478
1502
  };
1479
- console.info("[publisher] Screen track attached", {
1480
- screenTrackId: screenTrack.id,
1481
- cameraTrackId: this.videoSender?.track?.id,
1482
- screenMid: this.screenTransceiver.mid,
1483
- cameraMid: this.cameraTransceiver?.mid
1484
- });
1485
1503
  return this.screenStream;
1486
1504
  } catch (error) {
1487
1505
  try {
@@ -1499,38 +1517,34 @@ var SFUClient = class {
1499
1517
  }
1500
1518
  }
1501
1519
  async stopScreenShare() {
1502
- if (!this.screenStream && !this.screenSender?.track) return;
1503
- const screenTrack = this.screenSender?.track || null;
1504
- if (screenTrack) screenTrack.onended = null;
1520
+ const stream = this.screenStream;
1521
+ const senderTrack = this.screenSender?.track || null;
1522
+ if (!stream && !senderTrack) return;
1523
+ ++this.screenShareGeneration;
1524
+ if (senderTrack) senderTrack.onended = null;
1505
1525
  try {
1506
1526
  await this.screenSender?.replaceTrack(null);
1507
1527
  } catch (error) {
1508
1528
  this.onError(error);
1509
1529
  }
1510
- if (this.screenTransceiver) {
1511
- this.screenTransceiver.direction = "inactive";
1512
- }
1513
- this.send({ type: "stop_screen_share" });
1530
+ if (this.screenTransceiver) this.screenTransceiver.direction = "inactive";
1514
1531
  try {
1515
1532
  const offer = await this.publisher?.createOffer();
1516
1533
  if (offer && this.publisher) {
1517
1534
  await this.publisher.setLocalDescription(offer);
1518
1535
  this.send({
1519
1536
  type: "publish_offer",
1520
- payload: {
1521
- revision: ++this.publisherRevision,
1522
- sdp: offer.sdp
1523
- }
1537
+ payload: { revision: ++this.publisherRevision, sdp: offer.sdp }
1524
1538
  });
1525
1539
  }
1526
1540
  } catch (error) {
1527
1541
  this.onError(error);
1528
1542
  }
1529
- this.screenStream?.getTracks().forEach((track) => track.stop());
1543
+ this.send({ type: "stop_screen_share" });
1544
+ stream?.getTracks().forEach((track) => track.stop());
1530
1545
  this.screenStream = null;
1531
- if (this.currentParticipantId) {
1546
+ if (this.currentParticipantId)
1532
1547
  this.onScreenShareStopped(this.currentParticipantId);
1533
- }
1534
1548
  }
1535
1549
  muteAudio(muted) {
1536
1550
  this.desiredAudioMuted = muted;
@@ -1633,6 +1647,14 @@ var SFUClient = class {
1633
1647
  );
1634
1648
  return true;
1635
1649
  }
1650
+ sendReaction(reaction) {
1651
+ this.send({
1652
+ type: "send_reaction",
1653
+ payload: {
1654
+ reaction
1655
+ }
1656
+ });
1657
+ }
1636
1658
  removeRemoteParticipant(participantId) {
1637
1659
  const stream = this.remoteStreams.get(participantId);
1638
1660
  if (stream) {
@@ -2051,7 +2073,9 @@ var VideoSDKCore = class {
2051
2073
  // ADMISSION CONTROL
2052
2074
  onRoomUpdate: (payload) => {
2053
2075
  const participants = payload?.participants || [];
2054
- const local = participants.find((p) => p?.participant_id === this.myId);
2076
+ const local = participants.find(
2077
+ (p) => p?.participant_id === this.myId
2078
+ );
2055
2079
  if (local) {
2056
2080
  const media = this.state.localParticipant?.media;
2057
2081
  this.state.updateLocalParticipant({
@@ -2074,7 +2098,8 @@ var VideoSDKCore = class {
2074
2098
  });
2075
2099
  }
2076
2100
  if (local.screen_sharing) this.state.setPresenterId(this.myId);
2077
- else if (this.state.presenterId === this.myId) this.state.setPresenterId(null);
2101
+ else if (this.state.presenterId === this.myId)
2102
+ this.state.setPresenterId(null);
2078
2103
  }
2079
2104
  const presenter = participants.find((p) => p?.screen_sharing);
2080
2105
  if (presenter) this.state.setPresenterId(presenter.participant_id);
@@ -2505,7 +2530,7 @@ var VideoSDKCore = class {
2505
2530
  media: {
2506
2531
  isScreenSharing: true,
2507
2532
  screenStream: this.screenStream,
2508
- screenTrack: this.screenStream.getVideoTracks()[0]
2533
+ screenTrack: this.screenStream?.getVideoTracks()[0]
2509
2534
  }
2510
2535
  });
2511
2536
  return this.screenStream;
@@ -2570,6 +2595,13 @@ var VideoSDKCore = class {
2570
2595
  target: payload.target ?? null
2571
2596
  });
2572
2597
  }
2598
+ sendReaction(reaction) {
2599
+ if (!this.sfuClient) {
2600
+ console.warn("SFU Client not connected");
2601
+ return;
2602
+ }
2603
+ this.sfuClient.sendReaction(reaction);
2604
+ }
2573
2605
  // CLEANUP
2574
2606
  cleanupRemoteParticipant(participantId) {
2575
2607
  this.activeSpeakerDetector.removeParticipant(participantId);
@@ -2656,6 +2688,7 @@ var MeetingProvider = ({
2656
2688
  const trackStateListeners = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
2657
2689
  const recordingStartedListeners = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
2658
2690
  const recordingStoppedListeners = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
2691
+ const reactionListeners = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
2659
2692
  const activeSpeakerListeners = (0, import_react2.useRef)(
2660
2693
  /* @__PURE__ */ new Set()
2661
2694
  );
@@ -2685,6 +2718,9 @@ var MeetingProvider = ({
2685
2718
  },
2686
2719
  onRecordingStopped: (recording) => {
2687
2720
  recordingStoppedListeners.current.forEach((fn) => fn(recording));
2721
+ },
2722
+ onReaction: (reaction) => {
2723
+ reactionListeners.current.forEach((fn) => fn(reaction));
2688
2724
  }
2689
2725
  }
2690
2726
  );
@@ -2731,6 +2767,7 @@ var MeetingProvider = ({
2731
2767
  startScreenShare: sdk.startScreenShare.bind(sdk),
2732
2768
  stopScreenShare: sdk.stopScreenShare.bind(sdk),
2733
2769
  sendMessage: sdk.sendChatMessage.bind(sdk),
2770
+ sendReaction: sdk.sendReaction.bind(sdk),
2734
2771
  room: sdk.getMeeting(),
2735
2772
  localParticipant,
2736
2773
  participants,
@@ -2787,6 +2824,10 @@ var MeetingProvider = ({
2787
2824
  recordingStoppedListeners.current.add(cb);
2788
2825
  return () => recordingStoppedListeners.current.delete(cb);
2789
2826
  },
2827
+ onReaction: (cb) => {
2828
+ reactionListeners.current.add(cb);
2829
+ return () => reactionListeners.current.delete(cb);
2830
+ },
2790
2831
  onActiveSpeakersChanged: (cb) => {
2791
2832
  activeSpeakerListeners.current.add(cb);
2792
2833
  return () => {
@@ -2883,6 +2924,10 @@ var useMeeting = (handlers) => {
2883
2924
  if (!handlers?.onRecordingStopped) return;
2884
2925
  return ctx.onRecordingStopped(handlers.onRecordingStopped);
2885
2926
  }, [handlers?.onRecordingStopped]);
2927
+ (0, import_react4.useEffect)(() => {
2928
+ if (!handlers?.onReaction) return;
2929
+ return ctx.onReaction(handlers.onReaction);
2930
+ }, [handlers?.onReaction]);
2886
2931
  const { sdk: _, ...publicApi } = ctx;
2887
2932
  return publicApi;
2888
2933
  };