@cometchat/calls-sdk-react-native 5.0.0-beta.1 → 5.0.0-beta.11

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
@@ -4,7 +4,8 @@ import { create } from "zustand";
4
4
  import { combine, persist, subscribeWithSelector } from "zustand/middleware";
5
5
  import { shallow } from "zustand/shallow";
6
6
  import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
7
- import JitsiMeetJS from "lib-jitsi-meet";
7
+ import JitsiMeetJS from "@cometchat/calls-lib-webrtc";
8
+ import AsyncStorage from "@react-native-async-storage/async-storage";
8
9
  import { ActivityIndicator, Animated, AppState, DeviceEventEmitter, Dimensions, FlatList, Image, Modal, NativeEventEmitter, NativeModules, PanResponder, Platform, Pressable, ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, TouchableWithoutFeedback, View } from "react-native";
9
10
  import { RTCView, permissions } from "react-native-webrtc";
10
11
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -44,17 +45,24 @@ var EventBus = class {
44
45
  }
45
46
  }
46
47
  }
47
- subscribe(actionType, listener) {
48
+ subscribe(actionType, listener, options) {
49
+ if (options?.signal?.aborted) {
50
+ return () => {};
51
+ }
48
52
  if (!this.actionListenersMap.get(actionType)) {
49
53
  this.actionListenersMap.set(actionType, []);
50
54
  }
51
55
  this.actionListenersMap.get(actionType)?.push(listener);
52
- return () => {
56
+ const unsubscribe = () => {
53
57
  const listenersList = this.actionListenersMap.get(actionType);
54
58
  if (listenersList) {
55
59
  this.actionListenersMap.set(actionType, listenersList.filter((l) => l !== listener));
56
60
  }
57
61
  };
62
+ if (options?.signal) {
63
+ options.signal.addEventListener("abort", unsubscribe, { once: true });
64
+ }
65
+ return unsubscribe;
58
66
  }
59
67
  };
60
68
  const eventBus = new EventBus();
@@ -96,7 +104,10 @@ const VIDEO_PAUSE_AUTHORITY = {
96
104
  BACKGROUND: 1 << 1,
97
105
  SCREEN_SHARE: 1 << 2
98
106
  };
99
- const CONFERENCE_COMMANDS = { userInfo: "user-info" };
107
+ const CONFERENCE_COMMANDS = {
108
+ userInfo: "user-info",
109
+ initialConfig: "initial_config"
110
+ };
100
111
  const VIDEO_QUALITY_LEVELS = {
101
112
  ULTRA: 2160,
102
113
  HIGH: 720,
@@ -104,10 +115,12 @@ const VIDEO_QUALITY_LEVELS = {
104
115
  LOW: 180,
105
116
  NONE: 0
106
117
  };
107
- const PLATFORM = {
118
+ const SDK_PLATFORM = {
108
119
  WEB: "web",
109
120
  ANDROID: "android",
110
- IOS: "ios"
121
+ IOS: "ios",
122
+ REACT_NATIVE_ANDROID: "react-native-android",
123
+ REACT_NATIVE_IOS: "react-native-ios"
111
124
  };
112
125
  const EVENT_LISTENER_METHODS = {
113
126
  SessionStatusListener: {
@@ -222,7 +235,7 @@ function calculateTileLayout(containerWidth, containerHeight, numberOfTiles) {
222
235
  const tileArea = totalArea / numberOfTiles;
223
236
  const minArea = MIN_TILE_WIDTH * MIN_TILE_WIDTH * MIN_ASPECT_RATIO;
224
237
  if (tileArea < minArea) {
225
- const columnCount$1 = Math.floor(containerWidth / MIN_TILE_WIDTH);
238
+ const columnCount$1 = Math.max(2, Math.floor(containerWidth / MIN_TILE_WIDTH));
226
239
  const rowCount$1 = Math.ceil(numberOfTiles / columnCount$1);
227
240
  const totalHorizontalGap$1 = columnCount$1 * GRID_GAP;
228
241
  const tileWidth$1 = (containerWidth - totalHorizontalGap$1) / columnCount$1;
@@ -405,12 +418,37 @@ function isDeviceEqual(device1, device2) {
405
418
  function getDefaultDevice(devices) {
406
419
  return devices.find((device) => device.deviceId === "default") || devices[0];
407
420
  }
421
+ /**
422
+ * Returns a promise that resolves when the given Zustand store
423
+ * satisfies the provided predicate. Resolves immediately if the
424
+ * condition is already met. Includes a timeout to avoid hanging
425
+ * forever (defaults to 5 000 ms).
426
+ */
427
+ function waitForStoreState(store, predicate, timeoutMs = 5e3) {
428
+ return new Promise((resolve, reject) => {
429
+ if (predicate(store.getState())) {
430
+ resolve();
431
+ return;
432
+ }
433
+ const timer = setTimeout(() => {
434
+ unsubscribe();
435
+ reject(new Error("waitForStoreState timed out"));
436
+ }, timeoutMs);
437
+ const unsubscribe = store.subscribe((state) => {
438
+ if (predicate(state)) {
439
+ clearTimeout(timer);
440
+ unsubscribe();
441
+ resolve();
442
+ }
443
+ });
444
+ });
445
+ }
408
446
 
409
447
  //#endregion
410
448
  //#region calls-sdk-core/utils/try-catch.ts
411
- async function tryCatch(promise) {
449
+ async function tryCatch(promise, timeoutMs) {
412
450
  try {
413
- const data = await promise;
451
+ const data = timeoutMs != null ? await Promise.race([promise, new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), timeoutMs))]) : await promise;
414
452
  return {
415
453
  data,
416
454
  error: null
@@ -454,76 +492,163 @@ function debounce(func, delay) {
454
492
  //#endregion
455
493
  //#region calls-sdk-core/utils/session-methods-core.ts
456
494
  var SessionMethodsCore = class {
495
+ /**
496
+ * Mutes the local user's audio during the call.
497
+ */
457
498
  static muteAudio() {
458
499
  muteAudioTrack();
459
500
  }
501
+ /**
502
+ * Unmutes the local user's audio during the call.
503
+ */
460
504
  static unmuteAudio() {
461
505
  unMuteAudioTrack();
462
506
  }
507
+ /**
508
+ * Toggles the local user's audio mute state.
509
+ * If audio is muted, it will be unmuted, and vice versa.
510
+ */
511
+ static toggleAudio() {
512
+ toggleAudioTrack();
513
+ }
514
+ /**
515
+ * Pauses the local user's video stream.
516
+ */
463
517
  static pauseVideo() {
464
518
  pauseVideoTrack();
465
519
  }
520
+ /**
521
+ * Resumes the local user's video stream.
522
+ */
466
523
  static resumeVideo() {
467
524
  resumeVideoTrack();
468
525
  }
526
+ /**
527
+ * Toggles the local user's video stream.
528
+ * If video is paused, it will be resumed, and vice versa.
529
+ */
530
+ static toggleVideo() {
531
+ toggleVideoTrack();
532
+ }
533
+ /**
534
+ * Local user leaves the current session.
535
+ */
469
536
  static leaveSession() {
470
537
  leaveSession();
471
538
  }
472
- static startScreenSharing() {
473
- startScreenSharing();
474
- }
475
- static stopScreenSharing() {
476
- stopScreenSharing();
539
+ /**
540
+ * Ends the current session for all participants.
541
+ * This will terminate the conference and disconnect everyone.
542
+ */
543
+ static endSessionForAll() {
544
+ leaveSession({ shouldEndSession: true });
477
545
  }
546
+ /**
547
+ * Raises the user's virtual hand in the call.
548
+ */
478
549
  static raiseHand() {
479
550
  raisedHandLocal();
480
551
  }
552
+ /**
553
+ * Lowers the user's virtual hand in the call.
554
+ */
481
555
  static lowerHand() {
482
556
  lowerHandLocal();
483
557
  }
558
+ /**
559
+ * Toggles the user's virtual hand state.
560
+ * If the hand is raised, it will be lowered, and vice versa.
561
+ */
562
+ static toggleHand() {
563
+ toggleRaiseHand();
564
+ }
565
+ /**
566
+ * Switches between the front and rear camera.
567
+ */
484
568
  static switchCamera() {
485
569
  switchCamera();
486
570
  }
571
+ /**
572
+ * Sets the layout type for the call.
573
+ * @param layout - The type of layout to set (tile, sidebar or spotlight).
574
+ */
487
575
  static setLayout(layout$1) {
488
576
  setLayout(layout$1);
489
577
  }
490
- static startRecording() {}
491
- static stopRecording() {}
492
- static enablePictureInPictureLayout() {
493
- enablePictureInPictureLayout();
578
+ /**
579
+ * Starts recording the call.
580
+ */
581
+ static startRecording() {
582
+ startRecording();
494
583
  }
495
- static disablePictureInPictureLayout() {
496
- disablePictureInPictureLayout();
584
+ /**
585
+ * Stops the ongoing call recording.
586
+ */
587
+ static stopRecording() {
588
+ stopRecording();
589
+ }
590
+ /**
591
+ * Toggles the call recording state.
592
+ * If recording is active, it will be stopped, and vice versa.
593
+ */
594
+ static toggleRecording() {
595
+ toggleRecording();
497
596
  }
597
+ /**
598
+ * Pins a participant's video to focus on them.
599
+ * @param participantId - The ID of the participant to pin.
600
+ * @param type - The type of the participant.
601
+ */
498
602
  static pinParticipant(participantId, type) {
499
603
  pinParticipant(participantId, type);
500
604
  }
605
+ /**
606
+ * Unpins a participant's video.
607
+ */
501
608
  static unpinParticipant() {
502
609
  unpinParticipant();
503
610
  }
611
+ /**
612
+ * Mutes the audio of a specific participant.
613
+ * @param participantId - The ID of the participant to mute.
614
+ */
504
615
  static muteParticipant(participantId) {
505
616
  muteParticipant(participantId);
506
617
  }
618
+ /**
619
+ * Pauses the video stream of a specific participant.
620
+ * @param participantId - The ID of the participant whose video to pause.
621
+ */
507
622
  static pauseParticipantVideo(participantId) {
508
623
  pauseParticipantVideo(participantId);
509
624
  }
625
+ /**
626
+ * Sets the unread message count displayed on the chat button.
627
+ * @param count - The number of unread messages.
628
+ */
510
629
  static setChatButtonUnreadCount(count) {
511
630
  setChatButtonUnreadCount(count);
512
631
  }
513
632
  /**
514
- * @deprecated use startScreenSharing() instead
633
+ * Toggles the visibility of the participant list panel.
634
+ */
635
+ static toggleParticipantList() {
636
+ toggleParticipantList();
637
+ }
638
+ /**
639
+ * Shows the participant list panel.
515
640
  */
516
- static startScreenShare() {
517
- this.startScreenSharing();
641
+ static showParticipantList() {
642
+ showParticipantList();
518
643
  }
519
644
  /**
520
- * @deprecated use stopScreenSharing() instead
645
+ * Hides the participant list panel.
521
646
  */
522
- static stopScreenShare() {
523
- this.stopScreenSharing();
647
+ static hideParticipantList() {
648
+ hideParticipantList();
524
649
  }
525
650
  /**
526
- * @deprecated switchToVideoCall is deprecated and not supported
651
+ * @deprecated switchToVideoCall is deprecated and not supported.
527
652
  */
528
653
  static switchToVideoCall() {
529
654
  console.error("switchToVideoCall method deprecated and not supported.");
@@ -613,6 +738,27 @@ async function createLocalTrack(type, deviceId = null, cameraFacing = CAMERA_FAC
613
738
  }
614
739
  }
615
740
  }
741
+ function createLocalTracks() {
742
+ const enableCompanionMode = useConfigStore.getState().enableCompanionMode;
743
+ if (!enableCompanionMode) {
744
+ const audioInputDeviceId = useConfigStore.getState().audioInputDeviceId ?? useBaseStore.getState().audioInputDevice?.deviceId;
745
+ createLocalTrack("audio", audioInputDeviceId);
746
+ }
747
+ const sessionType = useConfigStore.getState().sessionType;
748
+ if (sessionType === SESSION_TYPE.VIDEO) {
749
+ const videoInputDeviceIdP1 = useConfigStore.getState().videoInputDeviceId;
750
+ const videoInputDeviceIdP2 = useBaseStore.getState().videoInputDevice?.deviceId;
751
+ const initialCameraFacingP1 = useConfigStore.getState().initialCameraFacing;
752
+ const initialCameraFacingP2 = useBaseStore.getState().cameraFacing;
753
+ if (videoInputDeviceIdP1) {
754
+ createLocalTrack("video", videoInputDeviceIdP1);
755
+ } else if (initialCameraFacingP1) {
756
+ createLocalTrack("video", null, initialCameraFacingP2);
757
+ } else {
758
+ createLocalTrack("video", videoInputDeviceIdP2, initialCameraFacingP2);
759
+ }
760
+ }
761
+ }
616
762
  function updateAudioInputDevice(deviceId) {
617
763
  const audioInputDevices = useBaseStore.getState().audioInputDevices.filter((device) => device.deviceId !== "");
618
764
  if (audioInputDevices.length > 0) {
@@ -794,7 +940,7 @@ function sendParticipantListChangedEvent() {
794
940
 
795
941
  //#endregion
796
942
  //#region calls-sdk-core/store/config.ts
797
- const initialState$7 = {
943
+ const initialState$8 = {
798
944
  sessionType: "VIDEO",
799
945
  autoStartRecording: false,
800
946
  layout: LAYOUT.TILE,
@@ -812,7 +958,6 @@ const initialState$7 = {
812
958
  hideLeaveSessionButton: false,
813
959
  hideToggleAudioButton: false,
814
960
  hideParticipantListButton: false,
815
- hideSwitchLayoutButton: false,
816
961
  hideChatButton: true,
817
962
  hideToggleVideoButton: false,
818
963
  hideScreenSharingButton: false,
@@ -825,13 +970,17 @@ const initialState$7 = {
825
970
  startVideoPaused: false,
826
971
  title: "",
827
972
  enableNoiseReduction: false,
973
+ unstable_renderAudioOnly: false,
828
974
  idleTimeoutPeriodBeforePrompt: 6e4,
829
975
  idleTimeoutPeriodAfterPrompt: 18e4,
830
976
  enableSpotlightDrag: true,
831
977
  enableSpotlightSwap: true,
832
- showFrameRate: false
978
+ showFrameRate: false,
979
+ enableCompanionMode: false,
980
+ isPeerCall: false,
981
+ enableNotifications: true
833
982
  };
834
- const useConfigStore = create()(subscribeWithSelector(combine(initialState$7, (set) => ({ reset: () => set(initialState$7) }))));
983
+ const useConfigStore = create()(subscribeWithSelector(combine(initialState$8, (set) => ({ reset: () => set(initialState$8) }))));
835
984
  const setConfig = (config) => {
836
985
  useConfigStore.setState(config);
837
986
  };
@@ -849,18 +998,24 @@ const initialLocalParticipant = {
849
998
  type: "human",
850
999
  uid: "unknown"
851
1000
  };
852
- const initialState$6 = {
1001
+ const initialState$7 = {
853
1002
  dominantSpeakers: [],
854
1003
  localParticipant: initialLocalParticipant,
855
1004
  participants: [],
856
1005
  virtualParticipants: []
857
1006
  };
858
- const useParticipantStore = create()(subscribeWithSelector(combine(initialState$6, (set, get$1) => ({
1007
+ const useParticipantStore = create()(subscribeWithSelector(combine(initialState$7, (set, get$1) => ({
859
1008
  addParticipant: (participant) => {
860
- set((state) => ({ participants: [...state.participants, participant] }));
1009
+ set((state) => ({ participants: state.participants.some((p) => p.pid === participant.pid) ? state.participants.map((p) => p.pid === participant.pid ? {
1010
+ ...p,
1011
+ ...participant
1012
+ } : p) : [...state.participants, participant] }));
861
1013
  },
862
1014
  addVirtualParticipant: (participant) => {
863
- set((state) => ({ virtualParticipants: [...state.virtualParticipants, participant] }));
1015
+ set((state) => ({ virtualParticipants: state.virtualParticipants.some((p) => p.pid === participant.pid && p.type === participant.type) ? state.virtualParticipants.map((p) => p.pid === participant.pid && p.type === participant.type ? {
1016
+ ...p,
1017
+ ...participant
1018
+ } : p) : [...state.virtualParticipants, participant] }));
864
1019
  },
865
1020
  clearParticipants: () => set({
866
1021
  participants: [],
@@ -930,7 +1085,7 @@ const useParticipantStore = create()(subscribeWithSelector(combine(initialState$
930
1085
  }
931
1086
  return get$1().participants.find((p) => p.pid === pid);
932
1087
  },
933
- reset: () => set(initialState$6)
1088
+ reset: () => set(initialState$7)
934
1089
  }))));
935
1090
  function useAllParticipants() {
936
1091
  const localParticipant = useParticipantStore((state) => state.localParticipant);
@@ -1062,9 +1217,35 @@ useParticipantStore.subscribe((state) => state.participants.filter((p) => p.uid)
1062
1217
  }
1063
1218
  });
1064
1219
 
1220
+ //#endregion
1221
+ //#region calls-sdk-core/store/utils/send-initial-config.ts
1222
+ function sendInitialConfig() {
1223
+ const conference = useConferenceStore.getState().conference;
1224
+ if (!conference?.room) {
1225
+ return;
1226
+ }
1227
+ const config = useConfigStore.getState();
1228
+ const localParticipant = useParticipantStore.getState().localParticipant;
1229
+ const attributes = {
1230
+ session_id: config.sessionId,
1231
+ is_audio_only: String(config.sessionType === "VOICE"),
1232
+ is_audio_muted: String(config.startAudioMuted),
1233
+ is_video_muted: String(config.startVideoPaused),
1234
+ start_recording_on_call_start: String(config.autoStartRecording),
1235
+ uid: localParticipant.uid,
1236
+ platform: useBaseStore.getState().sdkPlatform,
1237
+ platform_version: useBaseStore.getState().platformVersion,
1238
+ user_agent: useBaseStore.getState().userAgent,
1239
+ meeting_id: conference.room?.xmpp?.getJid?.() ?? config.sessionId,
1240
+ mode: config.layout,
1241
+ commit_id: "fef38834"
1242
+ };
1243
+ conference.room.addOrReplaceInPresence(CONFERENCE_COMMANDS.initialConfig, { attributes });
1244
+ }
1245
+
1065
1246
  //#endregion
1066
1247
  //#region calls-sdk-core/store/conference.ts
1067
- const initialState$5 = {
1248
+ const initialState$6 = {
1068
1249
  conference: null,
1069
1250
  conferenceJoined: false,
1070
1251
  conferenceJoinedAt: null,
@@ -1079,7 +1260,7 @@ const initialState$5 = {
1079
1260
  },
1080
1261
  isRecording: false
1081
1262
  };
1082
- const useConferenceStore = create()(subscribeWithSelector(combine(initialState$5, (set) => ({
1263
+ const useConferenceStore = create()(subscribeWithSelector(combine(initialState$6, (set) => ({
1083
1264
  conferenceWillJoin: (conference) => {
1084
1265
  if (typeof window?.APP !== "undefined") {
1085
1266
  window.APP.conference._room = conference;
@@ -1090,13 +1271,17 @@ const useConferenceStore = create()(subscribeWithSelector(combine(initialState$5
1090
1271
  });
1091
1272
  },
1092
1273
  reset: () => {
1093
- set(initialState$5);
1274
+ set(initialState$6);
1094
1275
  },
1095
1276
  raiseHand: (participantId, timestamp) => {
1096
1277
  set((state) => ({ raiseHandMap: new Map(state.raiseHandMap).set(participantId, timestamp) }));
1097
1278
  sendParticipantEvent(EVENT_LISTENER_METHODS.ParticipantEventsListner.onParticipantHandRaised, participantId);
1098
1279
  },
1099
1280
  lowerHand: (participantId) => {
1281
+ const hasRaisedHand = useConferenceStore.getState().raiseHandMap.has(participantId);
1282
+ if (!hasRaisedHand) {
1283
+ return;
1284
+ }
1100
1285
  set((state) => {
1101
1286
  const raiseHandMap = new Map(state.raiseHandMap);
1102
1287
  raiseHandMap.delete(participantId);
@@ -1107,13 +1292,22 @@ const useConferenceStore = create()(subscribeWithSelector(combine(initialState$5
1107
1292
  leaveConference: async () => {
1108
1293
  const conference = useConferenceStore.getState().conference;
1109
1294
  if (conference) {
1110
- const { error } = await tryCatch(conference.leave());
1295
+ const { error } = await tryCatch(conference.leave(), 500);
1111
1296
  if (error) {
1112
1297
  console.warn("Error leaving conference:", error);
1113
1298
  eventBus.publish({ type: EVENT_LISTENER_METHODS.SessionStatusListener.onSessionLeft });
1114
1299
  }
1115
1300
  }
1116
1301
  },
1302
+ endConference: async () => {
1303
+ const conference = useConferenceStore.getState().conference;
1304
+ if (conference) {
1305
+ const { error } = await tryCatch(conference.end());
1306
+ if (error) {
1307
+ console.warn("Error ending conference:", error);
1308
+ }
1309
+ }
1310
+ },
1117
1311
  stopRecording: async () => {
1118
1312
  const conference = useConferenceStore.getState().conference;
1119
1313
  if (conference) {
@@ -1245,15 +1439,16 @@ const getIsConferenceJoined = () => {
1245
1439
  return conferenceStatus === "joined" || conferenceStatus === "restored";
1246
1440
  };
1247
1441
  useConferenceStore.subscribe((state) => state.conference, (conference) => {
1442
+ sendInitialConfig();
1248
1443
  if (conference) {
1249
1444
  const localParticipant = useParticipantStore.getState().localParticipant;
1250
- conference?.sendCommand(CONFERENCE_COMMANDS.userInfo, { value: JSON.stringify(localParticipant) });
1445
+ conference.sendCommand(CONFERENCE_COMMANDS.userInfo, { value: JSON.stringify(localParticipant) });
1251
1446
  }
1252
1447
  });
1253
1448
 
1254
1449
  //#endregion
1255
1450
  //#region calls-sdk-core/store/tracks.ts
1256
- const initialState$4 = {
1451
+ const initialState$5 = {
1257
1452
  tracks: [],
1258
1453
  permissions: {
1259
1454
  audio: false,
@@ -1261,9 +1456,9 @@ const initialState$4 = {
1261
1456
  initialized: false
1262
1457
  }
1263
1458
  };
1264
- const useTracksStore = create()(subscribeWithSelector(combine(initialState$4, (set, get$1) => ({
1459
+ const useTracksStore = create()(subscribeWithSelector(combine(initialState$5, (set, get$1) => ({
1265
1460
  reset: () => set({
1266
- ...initialState$4,
1461
+ ...initialState$5,
1267
1462
  permissions: get$1().permissions
1268
1463
  }),
1269
1464
  addTrack: (originalTrack) => set((state) => {
@@ -1285,6 +1480,12 @@ const useTracksStore = create()(subscribeWithSelector(combine(initialState$4, (s
1285
1480
  muted: originalTrack.isMuted() ? 1 : 0,
1286
1481
  originalTrack
1287
1482
  };
1483
+ const existingIdx = state.tracks.findIndex((t) => t.pid === participantId && t.mediaType === track.mediaType && t.local === isLocal);
1484
+ if (existingIdx !== -1) {
1485
+ const tracks = [...state.tracks];
1486
+ tracks[existingIdx] = track;
1487
+ return { tracks };
1488
+ }
1288
1489
  return { tracks: [...state.tracks, track] };
1289
1490
  }),
1290
1491
  removeTrack: (originalTrack) => set((state) => ({ tracks: state.tracks.filter((track) => track.originalTrack !== originalTrack) })),
@@ -1486,8 +1687,13 @@ useTracksStore.subscribe((state) => state.tracks.find((t) => t.mediaType === MED
1486
1687
  }
1487
1688
  if (track) {
1488
1689
  const deviceId = track.getDeviceId();
1489
- const device = useBaseStore.getState().audioInputDevices.find((d) => d.deviceId === deviceId);
1490
- updateAudioInputDeviceState(device, true);
1690
+ waitForStoreState(useBaseStore, (state) => state.audioInputDevices.length > 0).then(() => {
1691
+ const audioInputDevices = useBaseStore.getState().audioInputDevices;
1692
+ const device = audioInputDevices.find((d) => d.deviceId === deviceId);
1693
+ updateAudioInputDeviceState(device, true);
1694
+ }).catch(() => {
1695
+ updateAudioInputDeviceState(undefined, true);
1696
+ });
1491
1697
  }
1492
1698
  });
1493
1699
  useTracksStore.subscribe((state) => state.tracks.find((t) => t.mediaType === MEDIA_TYPE.VIDEO && t.local)?.originalTrack, (track, prevTrack) => {
@@ -1634,7 +1840,7 @@ function updateConfig(config) {
1634
1840
 
1635
1841
  //#endregion
1636
1842
  //#region calls-sdk-core/store/base.ts
1637
- const initialState$3 = {
1843
+ const initialState$4 = {
1638
1844
  audioModeMenuVisible: false,
1639
1845
  audioModes: [],
1640
1846
  audioInputDevices: [],
@@ -1669,20 +1875,23 @@ const initialState$3 = {
1669
1875
  desktopSharingFrameRate: 5,
1670
1876
  chatButtonUnreadCount: 0,
1671
1877
  enableNoiseReduction: true,
1672
- sdkPlatform: PLATFORM.WEB,
1673
- webOSName: "unknown",
1878
+ sdkPlatform: SDK_PLATFORM.WEB,
1674
1879
  isMobileBrowser: false,
1675
1880
  visibleParticipants: {
1676
1881
  startIndex: 0,
1677
1882
  endIndex: 0
1883
+ },
1884
+ sidebarVisibleParticipants: {
1885
+ startIndex: 0,
1886
+ endIndex: 0
1678
1887
  }
1679
1888
  };
1680
- const useBaseStore = create()(subscribeWithSelector(persist(combine(initialState$3, (set, get$1) => ({
1889
+ const useBaseStore = create()(subscribeWithSelector(persist(combine(initialState$4, (set, get$1) => ({
1681
1890
  addSubscriptions: (subscriptions) => {
1682
1891
  useBaseStore.setState((state) => ({ subscriptions: [...state.subscriptions, ...subscriptions] }));
1683
1892
  },
1684
1893
  reset: () => set((state) => ({
1685
- ...initialState$3,
1894
+ ...initialState$4,
1686
1895
  audioInputDevice: state.audioInputDevice,
1687
1896
  audioOutputDevice: state.audioOutputDevice,
1688
1897
  videoInputDevice: state.videoInputDevice,
@@ -1693,11 +1902,11 @@ const useBaseStore = create()(subscribeWithSelector(persist(combine(initialState
1693
1902
  toggleParticipantListVisible: () => set((state) => ({ participantListVisible: !state.participantListVisible })),
1694
1903
  incrementConnectionRetryCount: () => set((state) => ({ connectionRetryCount: state.connectionRetryCount + 1 })),
1695
1904
  isMobileSDK: () => {
1696
- const isMobileSDK = get$1().sdkPlatform === "android" || get$1().sdkPlatform === "ios";
1905
+ const isMobileSDK = get$1().sdkPlatform !== "web";
1697
1906
  return isMobileSDK;
1698
1907
  },
1699
1908
  isMobile: () => {
1700
- const isMobileSDK = get$1().sdkPlatform === "android" || get$1().sdkPlatform === "ios";
1909
+ const isMobileSDK = get$1().sdkPlatform !== "web";
1701
1910
  const isMobileBrowser = get$1().isMobileBrowser;
1702
1911
  return isMobileSDK || isMobileBrowser;
1703
1912
  },
@@ -1743,6 +1952,7 @@ const useBaseStore = create()(subscribeWithSelector(persist(combine(initialState
1743
1952
  const toggleParticipantListVisible = useBaseStore.getState().toggleParticipantListVisible;
1744
1953
  const hideParticipantList = () => useBaseStore.setState({ participantListVisible: false });
1745
1954
  const showParticipantList = () => useBaseStore.setState({ participantListVisible: true });
1955
+ const toggleParticipantList = () => useBaseStore.setState((state) => ({ participantListVisible: !state.participantListVisible }));
1746
1956
  const toggleMoreMenuVisible = useBaseStore.getState().toggleMoreMenuVisible;
1747
1957
  const toggleAudioModeMenuVisible = () => {
1748
1958
  useBaseStore.setState((state) => ({ audioModeMenuVisible: !state.audioModeMenuVisible }));
@@ -1791,6 +2001,9 @@ const toggleEnableNoiseReduction = () => {
1791
2001
  const setChatButtonUnreadCount = (count) => {
1792
2002
  useBaseStore.setState({ chatButtonUnreadCount: count });
1793
2003
  };
2004
+ const setAudioMode = (mode) => {
2005
+ useBaseStore.setState({ selectedAudioModeType: mode });
2006
+ };
1794
2007
  const getLayout = () => {
1795
2008
  return useBaseStore.getState().layout;
1796
2009
  };
@@ -1913,13 +2126,13 @@ initBaseStoreSubscriptions();
1913
2126
 
1914
2127
  //#endregion
1915
2128
  //#region calls-sdk-core/store/connection.ts
1916
- const initialState$2 = {
2129
+ const initialState$3 = {
1917
2130
  connection: undefined,
1918
2131
  connectionStatus: "initial",
1919
2132
  error: undefined,
1920
2133
  timeEstablished: undefined
1921
2134
  };
1922
- const useConnectionStore = create()(subscribeWithSelector(combine(initialState$2, (set) => ({
2135
+ const useConnectionStore = create()(subscribeWithSelector(combine(initialState$3, (set) => ({
1923
2136
  connectionEstablished: (connection) => set({
1924
2137
  connection,
1925
2138
  connectionStatus: "connected",
@@ -1952,17 +2165,88 @@ const useConnectionStore = create()(subscribeWithSelector(combine(initialState$2
1952
2165
  eventBus.stopEmitting();
1953
2166
  await tryCatch(connection.disconnect());
1954
2167
  eventBus.publish({ type: EVENT_LISTENER_METHODS.SessionStatusListener.onConnectionClosed }, true);
1955
- set(initialState$2);
2168
+ set(initialState$3);
1956
2169
  }
1957
2170
  },
1958
- reset: () => set(initialState$2)
2171
+ reset: () => set(initialState$3)
1959
2172
  }))));
2173
+ function waitForConnection() {
2174
+ const { connectionStatus } = useConnectionStore.getState();
2175
+ if (connectionStatus === "connected") return Promise.resolve();
2176
+ return new Promise((resolve, reject) => {
2177
+ const timeout = setTimeout(() => {
2178
+ unsub();
2179
+ reject(new Error("Connection timed out after 3 seconds"));
2180
+ }, 3e3);
2181
+ const unsub = useConnectionStore.subscribe((s) => s.connectionStatus, (status) => {
2182
+ if (status === "connected") {
2183
+ clearTimeout(timeout);
2184
+ unsub();
2185
+ resolve();
2186
+ } else if (status === "error") {
2187
+ clearTimeout(timeout);
2188
+ unsub();
2189
+ reject(useConnectionStore.getState().error);
2190
+ }
2191
+ });
2192
+ });
2193
+ }
2194
+
2195
+ //#endregion
2196
+ //#region calls-sdk-core/store/toast.ts
2197
+ const MAX_TOASTS = 3;
2198
+ const initialState$2 = { toasts: [] };
2199
+ let counter = 0;
2200
+ const useToastStore = create((set) => ({
2201
+ ...initialState$2,
2202
+ addToast: (toast) => {
2203
+ if (!getIsConferenceJoined()) {
2204
+ return "";
2205
+ }
2206
+ if (!useConfigStore.getState().enableNotifications) {
2207
+ return "";
2208
+ }
2209
+ const id = `toast-${++counter}-${Date.now()}`;
2210
+ const newToast = {
2211
+ ...toast,
2212
+ id,
2213
+ createdAt: Date.now()
2214
+ };
2215
+ set((state) => {
2216
+ const updated = [...state.toasts, newToast];
2217
+ if (updated.length > MAX_TOASTS) {
2218
+ return { toasts: updated.slice(-MAX_TOASTS) };
2219
+ }
2220
+ return { toasts: updated };
2221
+ });
2222
+ return id;
2223
+ },
2224
+ removeToast: (id) => {
2225
+ set((state) => ({ toasts: state.toasts.filter((t) => t.id !== id) }));
2226
+ },
2227
+ clearAll: () => {
2228
+ set(initialState$2);
2229
+ },
2230
+ reset: () => {
2231
+ set(initialState$2);
2232
+ }
2233
+ }));
2234
+ function showToast(params) {
2235
+ return useToastStore.getState().addToast({
2236
+ message: params.message,
2237
+ type: params.type ?? "info",
2238
+ duration: params.duration ?? 3e3,
2239
+ position: params.position ?? "bottom",
2240
+ action: params.action
2241
+ });
2242
+ }
1960
2243
 
1961
2244
  //#endregion
1962
2245
  //#region calls-sdk-core/store/utils/hooks.ts
1963
2246
  const useHideMuteAudioButton = () => {
1964
2247
  const hideMuteAudioButton = useConfigStore((state) => state.hideToggleAudioButton);
1965
- return hideMuteAudioButton;
2248
+ const enableCompanionMode = useConfigStore((state) => state.enableCompanionMode);
2249
+ return hideMuteAudioButton || enableCompanionMode;
1966
2250
  };
1967
2251
  const useHideToggleVideoButton = () => {
1968
2252
  const hideToggleVideoButton = useConfigStore((state) => state.hideToggleVideoButton);
@@ -1984,7 +2268,7 @@ const useHideScreenSharingButton = () => {
1984
2268
  const useHideSwitchCameraButton = () => {
1985
2269
  const videoTrack = getLocalTrack(MEDIA_TYPE.VIDEO);
1986
2270
  const hideSwitchCameraButton = useConfigStore((state) => state.hideSwitchCameraButton);
1987
- return hideSwitchCameraButton || !videoTrack;
2271
+ return hideSwitchCameraButton || !videoTrack?.originalTrack;
1988
2272
  };
1989
2273
  const useHideChatButton = () => {
1990
2274
  const hideChatButton = useConfigStore((state) => state.hideChatButton);
@@ -2029,8 +2313,21 @@ const getMainParticipant = () => {
2029
2313
  const useIsReconnecting = () => {
2030
2314
  const connectionStatus = useConnectionStore((state) => state.connectionStatus);
2031
2315
  const conferenceStatus = useConferenceStore((state) => state.conferenceStatus);
2032
- const reconnecting = connectionStatus === "connected" && conferenceStatus === "interrupted";
2033
- return reconnecting;
2316
+ const isP2P = useConferenceStore((state) => state.p2p);
2317
+ const [isOnline, setIsOnline] = useState(true);
2318
+ useEffect(() => {
2319
+ if (typeof window === "undefined") return;
2320
+ const controller = new AbortController();
2321
+ const { signal } = controller;
2322
+ window.addEventListener("online", () => setIsOnline(true), { signal });
2323
+ window.addEventListener("offline", () => setIsOnline(false), { signal });
2324
+ return () => controller.abort();
2325
+ }, []);
2326
+ const interrupted = connectionStatus === "connected" && conferenceStatus === "interrupted";
2327
+ if (isP2P && interrupted && isOnline) {
2328
+ return false;
2329
+ }
2330
+ return interrupted;
2034
2331
  };
2035
2332
  const useHideRecordingButton = () => {
2036
2333
  const hideRecordingButton = useConfigStore((state) => state.hideRecordingButton);
@@ -2108,6 +2405,11 @@ const useIsVideoInputSelectionSupported = () => {
2108
2405
  const isVideoInputSelectionSupported = hasVideoPermission && videoInputDevices.length > 0;
2109
2406
  return isVideoInputSelectionSupported;
2110
2407
  };
2408
+ const useShouldMirrorLocalVideo = () => {
2409
+ const mirrorLocalVideo = useBaseStore((state) => state.mirrorLocalVideo);
2410
+ const cameraFacing = useBaseStore((state) => state.cameraFacing);
2411
+ return cameraFacing === "user" && mirrorLocalVideo;
2412
+ };
2111
2413
 
2112
2414
  //#endregion
2113
2415
  //#region calls-sdk-core/store/utils/switch-camera.ts
@@ -2156,8 +2458,10 @@ function setReceiverConstraintsInternal() {
2156
2458
  }
2157
2459
  });
2158
2460
  } else if (layout$1 === "SIDEBAR") {
2159
- const participants = useParticipantStore.getState().participants;
2160
- participants.forEach((participant) => {
2461
+ const { startIndex, endIndex } = useBaseStore.getState().sidebarVisibleParticipants;
2462
+ const allParticipants = getAllParticipants();
2463
+ const visibleSidebarParticipants = allParticipants.slice(startIndex, endIndex);
2464
+ visibleSidebarParticipants.forEach((participant) => {
2161
2465
  const videoSources$1 = participant.sources?.get("video");
2162
2466
  if (videoSources$1) {
2163
2467
  Array.from(videoSources$1.keys()).forEach((source) => {
@@ -2183,6 +2487,9 @@ const setReceiverConstraints = debounce(setReceiverConstraintsInternal, 200);
2183
2487
  useBaseStore.subscribe((state) => state.visibleParticipants, () => {
2184
2488
  setReceiverConstraints();
2185
2489
  });
2490
+ useBaseStore.subscribe((state) => state.sidebarVisibleParticipants, () => {
2491
+ setReceiverConstraints();
2492
+ });
2186
2493
  useBaseStore.subscribe((state) => state.layout, () => {
2187
2494
  setReceiverConstraints();
2188
2495
  });
@@ -2280,8 +2587,12 @@ var ConferenceListener = class {
2280
2587
  track.removeAllListeners(JitsiMeetJS.events.track.NO_DATA_FROM_SOURCE);
2281
2588
  }
2282
2589
  onConferenceJoinInProgress() {}
2283
- onConferenceFailed(_conference, error, message) {
2284
- console.error("Conference failed:", error, message);
2590
+ onConferenceFailed(errorName, error, message) {
2591
+ if (errorName === JitsiMeetJS.errors.conference.CONFERENCE_DESTROYED) {
2592
+ leaveSession({ forceLeave: true });
2593
+ return;
2594
+ }
2595
+ console.error("Conference failed:", errorName, error, message);
2285
2596
  useConferenceStore.setState({
2286
2597
  conferenceStatus: "error",
2287
2598
  conferenceJoined: false,
@@ -2316,6 +2627,7 @@ var ConferenceListener = class {
2316
2627
  }
2317
2628
  onConnectionEstablished() {
2318
2629
  useConferenceStore.setState({ conferenceStatus: "joined" });
2630
+ setTimeout(setReceiverConstraints, 500);
2319
2631
  }
2320
2632
  onConnectionInterrupted() {
2321
2633
  useConferenceStore.setState({ conferenceStatus: "interrupted" });
@@ -2327,17 +2639,19 @@ var ConferenceListener = class {
2327
2639
  useConferenceStore.setState({ p2p });
2328
2640
  }
2329
2641
  onTrackMuteChanged(track, participantThatMutedUs) {
2330
- if (participantThatMutedUs) {
2331
- useTracksStore.getState().updateTrack(track, { muted: track.isMuted() ? 1 : 0 });
2332
- const displayName = participantThatMutedUs.getDisplayName();
2333
- if (displayName) {
2334
- eventBus.publish({
2335
- type: INTERNAL_EVENTS.notification,
2336
- payload: {
2337
- type: "info",
2338
- message: `${displayName} has muted you.`
2339
- }
2340
- });
2642
+ if (track.isLocal()) {
2643
+ useTracksStore.getState().updateLocalTrack(track.getType(), { muted: track.isMuted() ? 1 : 0 });
2644
+ if (participantThatMutedUs) {
2645
+ const displayName = participantThatMutedUs.getDisplayName();
2646
+ if (displayName) {
2647
+ eventBus.publish({
2648
+ type: INTERNAL_EVENTS.notification,
2649
+ payload: {
2650
+ type: "info",
2651
+ message: `${displayName} has muted you.`
2652
+ }
2653
+ });
2654
+ }
2341
2655
  }
2342
2656
  }
2343
2657
  }
@@ -2392,6 +2706,24 @@ var ConferenceListener = class {
2392
2706
  useParticipantStore.getState().updateParticipant(participantId, { role: newRole });
2393
2707
  }
2394
2708
  }
2709
+ onTrackUnmuteRejected(track) {
2710
+ if (!track.isLocal()) {
2711
+ return;
2712
+ }
2713
+ const mediaType = track.getType();
2714
+ track.dispose().catch(() => {});
2715
+ useTracksStore.getState().updateLocalTrack(mediaType, {
2716
+ originalTrack: undefined,
2717
+ muted: 1
2718
+ });
2719
+ eventBus.publish({
2720
+ type: INTERNAL_EVENTS.notification,
2721
+ payload: {
2722
+ type: "info",
2723
+ message: `Your ${mediaType} unmute was rejected.`
2724
+ }
2725
+ });
2726
+ }
2395
2727
  onTalkWhileMuted() {}
2396
2728
  onConferenceError(error) {
2397
2729
  console.error("Conference error:", error);
@@ -2470,6 +2802,7 @@ function addConferenceListeners(conference) {
2470
2802
  conference.on(JitsiMeetJS.events.conference.PARTICIPANT_PROPERTY_CHANGED, conferenceListener.onParticipantPropertyChanged);
2471
2803
  conference.on(JitsiMeetJS.events.conference.USER_ROLE_CHANGED, conferenceListener.onUserRoleChanged);
2472
2804
  conference.on(JitsiMeetJS.events.conference.TALK_WHILE_MUTED, conferenceListener.onTalkWhileMuted);
2805
+ conference.on(JitsiMeetJS.events.conference.TRACK_UNMUTE_REJECTED, conferenceListener.onTrackUnmuteRejected);
2473
2806
  conference.on(JitsiMeetJS.events.conference.TRACK_AUDIO_LEVEL_CHANGED, conferenceListener.onTrackAudioLevelChanged);
2474
2807
  conference.addCommandListener(CONFERENCE_COMMANDS.userInfo, (data, id) => {
2475
2808
  const vData = v.safeParse(UserInfoCommandSchema, safeParseJson(data.value));
@@ -2481,17 +2814,23 @@ function addConferenceListeners(conference) {
2481
2814
  }
2482
2815
  });
2483
2816
  }
2484
- async function createConference(connection, roomName) {
2817
+ async function _createConference() {
2818
+ const sessionId = useConfigStore.getState().sessionId;
2819
+ const connection = useConnectionStore.getState().connection;
2485
2820
  if (!connection) {
2486
2821
  throw new Error("No connection available");
2487
2822
  }
2823
+ const connectionStatus = useConnectionStore.getState().connectionStatus;
2824
+ if (connectionStatus !== "connected") {
2825
+ await waitForConnection();
2826
+ }
2488
2827
  const existingConference = useConferenceStore.getState().conference;
2489
2828
  if (existingConference) {
2490
2829
  console.log("Conference already exists, skipping creation");
2491
2830
  return;
2492
2831
  }
2493
2832
  const connectionConfig = useConnectionStore.getState().connectionConfig;
2494
- const conference = connection.initJitsiConference(roomName, connectionConfig);
2833
+ const conference = connection.initJitsiConference(sessionId, connectionConfig);
2495
2834
  const localAudioTrack = getLocalTrack(MEDIA_TYPE.AUDIO)?.originalTrack;
2496
2835
  const localVideoTrack = getLocalTrack(MEDIA_TYPE.VIDEO)?.originalTrack;
2497
2836
  if (localAudioTrack) {
@@ -2506,6 +2845,20 @@ async function createConference(connection, roomName) {
2506
2845
  conference.setDisplayName(useParticipantStore.getState().localParticipant.name);
2507
2846
  conference.join();
2508
2847
  }
2848
+ async function createConference() {
2849
+ const conference = useConferenceStore.getState().conference;
2850
+ if (!conference) {
2851
+ const result = await tryCatch(_createConference());
2852
+ if (result.error) {
2853
+ console.error("Error creating conference", result.error);
2854
+ useConferenceStore.setState({
2855
+ conferenceStatus: "error",
2856
+ conferenceJoined: false,
2857
+ conferenceError: result.error.message
2858
+ });
2859
+ }
2860
+ }
2861
+ }
2509
2862
  function muteParticipant(participantId) {
2510
2863
  const conference = useConferenceStore.getState().conference;
2511
2864
  conference?.muteParticipant(participantId, "audio");
@@ -2517,7 +2870,12 @@ function pauseParticipantVideo(participantId) {
2517
2870
 
2518
2871
  //#endregion
2519
2872
  //#region calls-sdk-core/handlers/connection.ts
2520
- function connect(roomName) {
2873
+ async function connect(autoJoinConference = true) {
2874
+ const existingConnection = useConnectionStore.getState().connection;
2875
+ if (existingConnection) {
2876
+ createConference();
2877
+ return;
2878
+ }
2521
2879
  const options = useConnectionStore.getState().connectionConfig;
2522
2880
  const jwt = useConnectionStore.getState().jwt;
2523
2881
  const iAmRecorder = useConfigStore.getState().iAmRecorder;
@@ -2533,15 +2891,8 @@ function connect(roomName) {
2533
2891
  async function onConnectionEstablished() {
2534
2892
  useConnectionStore.getState().connectionEstablished(connection);
2535
2893
  eventBus.publish({ type: INTERNAL_EVENTS.onConnectionEstablished });
2536
- const result = await tryCatch(createConference(connection, roomName));
2537
- if (result.error) {
2538
- console.error("Error creating conference", result.error);
2539
- useConferenceStore.setState({
2540
- conferenceStatus: "error",
2541
- conferenceJoined: false,
2542
- conferenceError: result.error.message
2543
- });
2544
- }
2894
+ if (!autoJoinConference) return;
2895
+ createConference();
2545
2896
  }
2546
2897
  function onConnectionFailed(err, message, ...args) {
2547
2898
  unsubscribe();
@@ -2708,6 +3059,7 @@ function resetAllStores() {
2708
3059
  useConfigStore.getState().reset();
2709
3060
  useConnectionStore.getState().reset();
2710
3061
  useParticipantStore.getState().reset();
3062
+ useToastStore.getState().reset();
2711
3063
  useTracksStore.getState().reset();
2712
3064
  }
2713
3065
 
@@ -2739,6 +3091,7 @@ var Mutex = class {
2739
3091
  //#region calls-sdk-core/handlers/init.ts
2740
3092
  function initializeLib() {
2741
3093
  JitsiMeetJS.init();
3094
+ JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.ERROR);
2742
3095
  console.log("JitsiMeetJS initialized successfully.");
2743
3096
  }
2744
3097
 
@@ -2747,6 +3100,7 @@ function initializeLib() {
2747
3100
  let isSessionStarted = false;
2748
3101
  let reconnectTimeoutId = null;
2749
3102
  const RECONNECT_DEBOUNCE_DELAY = 3e3;
3103
+ initializeLib();
2750
3104
  function startSession() {
2751
3105
  const sessionId = useConfigStore.getState().sessionId;
2752
3106
  if (!sessionId) {
@@ -2759,29 +3113,13 @@ function startSession() {
2759
3113
  }
2760
3114
  isSessionStarted = true;
2761
3115
  console.log(`Session started in room: ${sessionId}`);
2762
- initializeLib();
2763
- const audioInputDeviceId = useConfigStore.getState().audioInputDeviceId ?? useBaseStore.getState().audioInputDevice?.deviceId;
2764
- createLocalTrack("audio", audioInputDeviceId);
2765
- const sessionType = useConfigStore.getState().sessionType;
2766
- if (sessionType === SESSION_TYPE.VIDEO) {
2767
- const videoInputDeviceIdP1 = useConfigStore.getState().videoInputDeviceId;
2768
- const videoInputDeviceIdP2 = useBaseStore.getState().videoInputDevice?.deviceId;
2769
- const initialCameraFacingP1 = useConfigStore.getState().initialCameraFacing;
2770
- const initialCameraFacingP2 = useBaseStore.getState().cameraFacing;
2771
- if (videoInputDeviceIdP1) {
2772
- createLocalTrack("video", videoInputDeviceIdP1);
2773
- } else if (initialCameraFacingP1) {
2774
- createLocalTrack("video", null, initialCameraFacingP2);
2775
- } else {
2776
- createLocalTrack("video", videoInputDeviceIdP2, initialCameraFacingP2);
2777
- }
2778
- }
3116
+ createLocalTracks();
2779
3117
  const audioOutputDeviceId = useConfigStore.getState().audioOutputDeviceId ?? useBaseStore.getState().audioOutputDevice?.deviceId;
2780
3118
  if (audioOutputDeviceId) {
2781
3119
  updateAudioOutputDevice(audioOutputDeviceId);
2782
3120
  }
2783
3121
  eventBus.startEmitting();
2784
- const test = tryCatchSync(() => connect(sessionId));
3122
+ const test = tryCatchSync(() => connect());
2785
3123
  if (test.error) {
2786
3124
  console.error("Error connecting to session:", test.error);
2787
3125
  useConnectionStore.getState().connectionFailed(test.error.message);
@@ -2798,8 +3136,14 @@ async function _leaveSession() {
2798
3136
  await useConnectionStore.getState().disconnect();
2799
3137
  }
2800
3138
  const sessionMutex = new Mutex();
2801
- function leaveSession() {
3139
+ function leaveSession(options = {}) {
2802
3140
  return sessionMutex.run(async () => {
3141
+ const isPeerCall = useConfigStore.getState().isPeerCall;
3142
+ const shouldEnd = options.shouldEndSession || isPeerCall && !options.forceLeave;
3143
+ if (shouldEnd) {
3144
+ useConferenceStore.getState().endConference();
3145
+ return;
3146
+ }
2803
3147
  useBaseStore.getState().clearIdealTimeoutTimer();
2804
3148
  cancelPendingReconnect();
2805
3149
  await _leaveSession();
@@ -3267,6 +3611,10 @@ eventBus.subscribe(INTERNAL_EVENTS.lifecycle.componentDidMount, () => {
3267
3611
  });
3268
3612
  });
3269
3613
  useBaseStore.subscribe((state) => state.appState, (appState) => {
3614
+ const sessionType = useConfigStore.getState().sessionType;
3615
+ if (sessionType !== SESSION_TYPE.VIDEO) {
3616
+ return;
3617
+ }
3270
3618
  if (appState === "active") {
3271
3619
  resumeVideoTrack(VIDEO_PAUSE_AUTHORITY.BACKGROUND);
3272
3620
  } else {
@@ -3707,6 +4055,13 @@ const commonStyles = StyleSheet.create({
3707
4055
  borderDefault: { borderColor: colors.border.default },
3708
4056
  borderDark: { borderColor: colors.border.dark },
3709
4057
  borderHighlight: { borderColor: colors.border.highlight },
4058
+ absoluteFill: {
4059
+ position: "absolute",
4060
+ top: 0,
4061
+ bottom: 0,
4062
+ left: 0,
4063
+ right: 0
4064
+ },
3710
4065
  flex1: { flex: 1 },
3711
4066
  flexRow: { flexDirection: "row" },
3712
4067
  flexColumn: { flexDirection: "column" },
@@ -3896,10 +4251,16 @@ const PopupMenu = ({ visible, onClose, options, anchorLayout }) => {
3896
4251
  return /* @__PURE__ */ jsx(Modal, {
3897
4252
  transparent: true,
3898
4253
  animationType: "none",
4254
+ supportedOrientations: [
4255
+ "portrait",
4256
+ "landscape-left",
4257
+ "landscape-right"
4258
+ ],
3899
4259
  onRequestClose: onClose,
3900
4260
  children: /* @__PURE__ */ jsx(Pressable, {
3901
4261
  style: styles$27.backdrop,
3902
4262
  onPress: onClose,
4263
+ testID: "cometchat-popup-menu-backdrop",
3903
4264
  children: /* @__PURE__ */ jsx(Animated.View, {
3904
4265
  style: [styles$27.menu, {
3905
4266
  [isBelowMiddle ? "bottom" : "top"]: isBelowMiddle ? callContainerDimension.height - (y - 4) : y + height + 4,
@@ -3920,6 +4281,7 @@ const PopupMenu = ({ visible, onClose, options, anchorLayout }) => {
3920
4281
  },
3921
4282
  activeOpacity: option.selected ? DISABLED_OPTION_OPACITY : .2,
3922
4283
  style: [styles$27.menuItem, option.selected ? styles$27.menuItemSelected : {}],
4284
+ testID: `cometchat-popup-menu-option-${index}`,
3923
4285
  children: [option.iconName && /* @__PURE__ */ jsx(Icon_native_default, {
3924
4286
  name: option.iconName,
3925
4287
  size: 24,
@@ -4081,6 +4443,7 @@ const MoreOptionButton = ({ ruid }) => {
4081
4443
  ref: buttonRef,
4082
4444
  style: styles$4.moreButton,
4083
4445
  onPress: showMenu,
4446
+ testID: "cometchat-participant-more-options-button",
4084
4447
  children: /* @__PURE__ */ jsx(Icon_native_default, {
4085
4448
  name: "more",
4086
4449
  size: 20,
@@ -4220,7 +4583,11 @@ const VideoNative = ({ videoTrack, style, objectFit = "cover", mirror = false, m
4220
4583
  return null;
4221
4584
  }
4222
4585
  return /* @__PURE__ */ jsx(Animated.View, {
4223
- style: [styles$25.videoContainer, { opacity: fadeAnim }],
4586
+ style: [
4587
+ commonStyles.absoluteFill,
4588
+ styles$25.videoContainer,
4589
+ { opacity: fadeAnim }
4590
+ ],
4224
4591
  children: /* @__PURE__ */ jsx(RTCView, {
4225
4592
  streamURL: streamURL.toURL(),
4226
4593
  style: [styles$25.video, style],
@@ -4233,8 +4600,7 @@ const VideoNative = ({ videoTrack, style, objectFit = "cover", mirror = false, m
4233
4600
  const styles$25 = StyleSheet.create({
4234
4601
  videoContainer: {
4235
4602
  height: "100%",
4236
- width: "100%",
4237
- ...StyleSheet.absoluteFillObject
4603
+ width: "100%"
4238
4604
  },
4239
4605
  video: {
4240
4606
  width: "100%",
@@ -4250,6 +4616,7 @@ const Tile = ({ participant, style, zOrder, showLabel = true, disablePress = fal
4250
4616
  const videoTrack = useTrackByParticipantId(pid, type === "screen-share" ? MEDIA_TYPE.SCREENSHARE : MEDIA_TYPE.VIDEO)?.originalTrack;
4251
4617
  const videoMuted = useTrackMuted(type === "screen-share" ? MEDIA_TYPE.SCREENSHARE : MEDIA_TYPE.VIDEO, pid);
4252
4618
  const enableParticipantContextMenu = useEnableParticipantContextMenu();
4619
+ const shouldMirror = useShouldMirrorLocalVideo();
4253
4620
  const [size, fontSize] = React.useMemo(() => {
4254
4621
  const flatStyle = StyleSheet.flatten(style);
4255
4622
  const width$1 = flatStyle?.width;
@@ -4267,6 +4634,7 @@ const Tile = ({ participant, style, zOrder, showLabel = true, disablePress = fal
4267
4634
  activeOpacity: 1,
4268
4635
  disabled: disablePress,
4269
4636
  style: [styles$24.callScreen, style],
4637
+ testID: `cometchat-tile-${pid}`,
4270
4638
  children: [
4271
4639
  /* @__PURE__ */ jsx(View, {
4272
4640
  style: styles$24.tileAvatar,
@@ -4282,7 +4650,7 @@ const Tile = ({ participant, style, zOrder, showLabel = true, disablePress = fal
4282
4650
  objectFit: type === "screen-share" ? "contain" : "cover",
4283
4651
  muted: videoMuted,
4284
4652
  zOrder,
4285
- mirror: isLocal
4653
+ mirror: isLocal && type !== "screen-share" && shouldMirror
4286
4654
  }),
4287
4655
  showLabel && /* @__PURE__ */ jsx(Label_native_default, { participant }),
4288
4656
  enableParticipantContextMenu && /* @__PURE__ */ jsx(View, {
@@ -4341,6 +4709,7 @@ const GroupAvatarTile = ({ startIndex = 4, style }) => {
4341
4709
  const overflowCount = Math.max(0, participantCount - startIndex - 3);
4342
4710
  const visible = participants.slice(startIndex, startIndex + (overflowCount === 1 ? 4 : 3));
4343
4711
  return /* @__PURE__ */ jsxs(TouchableOpacity, {
4712
+ testID: "cometchat-group-avatar-tile",
4344
4713
  style: [styles$23.container, style],
4345
4714
  onPress: toggleParticipantListVisible,
4346
4715
  children: [/* @__PURE__ */ jsx(View, {
@@ -4448,6 +4817,7 @@ function SidebarLayout() {
4448
4817
  const mainParticipant = useMainParticipant();
4449
4818
  const participants = allParticipants.length > 1 ? [mainParticipant].concat(allParticipants) : [mainParticipant];
4450
4819
  return /* @__PURE__ */ jsxs(View, {
4820
+ testID: "cometchat-sidebar-layout",
4451
4821
  style: styles$22.container,
4452
4822
  children: [
4453
4823
  /* @__PURE__ */ jsx(Tile_native_default, {
@@ -4584,23 +4954,26 @@ const Spotlight = () => {
4584
4954
  if (otherParticipant) {
4585
4955
  spotlightParticipants.push(otherParticipant);
4586
4956
  }
4587
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Tile_native_default, {
4588
- participant: spotlightParticipants[0],
4589
- disablePress: true
4590
- }, spotlightParticipants[0].ruid), spotlightParticipants[1] && /* @__PURE__ */ jsx(Pan, {
4591
- disableDrag: !enableSpotlightDrag,
4592
- layout: {
4593
- width: mainAreaDimension.width,
4594
- height: mainAreaDimension.height
4595
- },
4596
- children: /* @__PURE__ */ jsx(Tile_native_default, {
4597
- showLabel: false,
4598
- disablePress: !enableSpotlightSwap,
4599
- participant: spotlightParticipants[1],
4600
- style: styles$20.panTile,
4601
- zOrder: 1
4602
- }, spotlightParticipants[1].ruid)
4603
- })] });
4957
+ return /* @__PURE__ */ jsxs(View, {
4958
+ testID: "cometchat-spotlight-layout",
4959
+ children: [/* @__PURE__ */ jsx(Tile_native_default, {
4960
+ participant: spotlightParticipants[0],
4961
+ disablePress: true
4962
+ }, spotlightParticipants[0].ruid), spotlightParticipants[1] && /* @__PURE__ */ jsx(Pan, {
4963
+ disableDrag: !enableSpotlightDrag,
4964
+ layout: {
4965
+ width: mainAreaDimension.width,
4966
+ height: mainAreaDimension.height
4967
+ },
4968
+ children: /* @__PURE__ */ jsx(Tile_native_default, {
4969
+ showLabel: false,
4970
+ disablePress: !enableSpotlightSwap,
4971
+ participant: spotlightParticipants[1],
4972
+ style: styles$20.panTile,
4973
+ zOrder: 1
4974
+ }, spotlightParticipants[1].ruid)
4975
+ })]
4976
+ });
4604
4977
  };
4605
4978
  const styles$20 = StyleSheet.create({ panTile: {
4606
4979
  borderColor: "#1A1A1A",
@@ -4633,6 +5006,7 @@ function TileLayout() {
4633
5006
  participantCount: isPIPLayoutEnabled ? 1 : participants.length
4634
5007
  });
4635
5008
  return /* @__PURE__ */ jsx(FlatList, {
5009
+ testID: "cometchat-tile-layout",
4636
5010
  data: isPIPLayoutEnabled ? [mainParticipant] : participants,
4637
5011
  renderItem: ({ item }) => /* @__PURE__ */ jsx(Tile_native_default, {
4638
5012
  participant: item,
@@ -4700,7 +5074,11 @@ function ProcessingOverlay({ message, visible = true, animated = false }) {
4700
5074
  return null;
4701
5075
  }
4702
5076
  return /* @__PURE__ */ jsxs(Animated.View, {
4703
- style: [styles$18.overlay, { opacity }],
5077
+ style: [
5078
+ commonStyles.absoluteFill,
5079
+ styles$18.overlay,
5080
+ { opacity }
5081
+ ],
4704
5082
  children: [/* @__PURE__ */ jsx(ActivityIndicator, {
4705
5083
  size: "large",
4706
5084
  color: "#fff"
@@ -4712,7 +5090,6 @@ function ProcessingOverlay({ message, visible = true, animated = false }) {
4712
5090
  }
4713
5091
  const styles$18 = StyleSheet.create({
4714
5092
  overlay: {
4715
- ...StyleSheet.absoluteFillObject,
4716
5093
  justifyContent: "center",
4717
5094
  alignItems: "center",
4718
5095
  backgroundColor: "#1A1A1A",
@@ -4890,7 +5267,6 @@ function showConfirmationDialog(title, description, confirmText, cancelText, onC
4890
5267
  const { width } = Dimensions.get("window");
4891
5268
  const styles$15 = StyleSheet.create({
4892
5269
  backdrop: {
4893
- ...StyleSheet.absoluteFillObject,
4894
5270
  backgroundColor: "rgba(0, 0, 0, 0.8)",
4895
5271
  justifyContent: "center",
4896
5272
  alignItems: "center",
@@ -4982,10 +5358,16 @@ function ConfirmationDialog() {
4982
5358
  visible,
4983
5359
  transparent: true,
4984
5360
  animationType: "fade",
5361
+ supportedOrientations: [
5362
+ "portrait",
5363
+ "landscape-left",
5364
+ "landscape-right"
5365
+ ],
4985
5366
  onRequestClose: handleBackdropPress,
4986
5367
  children: /* @__PURE__ */ jsx(Pressable, {
4987
- style: styles$15.backdrop,
5368
+ style: [commonStyles.absoluteFill, styles$15.backdrop],
4988
5369
  onPress: handleBackdropPress,
5370
+ testID: "cometchat-confirmation-dialog-backdrop",
4989
5371
  children: /* @__PURE__ */ jsx(View, {
4990
5372
  style: styles$15.dialog,
4991
5373
  children: /* @__PURE__ */ jsxs(View, {
@@ -5028,7 +5410,7 @@ function ConfirmationDialog() {
5028
5410
  //#endregion
5029
5411
  //#region src/ui/bottom-sheet/BottomSheet.native.tsx
5030
5412
  const SCREEN_HEIGHT = Dimensions.get("window").height;
5031
- const BottomSheet = ({ children, isVisible, onClose, maxHeight = SCREEN_HEIGHT * .4 }) => {
5413
+ const BottomSheet = ({ children, isVisible, onClose, maxHeight = SCREEN_HEIGHT * .4, testID }) => {
5032
5414
  const visibleTranslateY = SCREEN_HEIGHT - maxHeight;
5033
5415
  const hiddenTranslateY = useRef(SCREEN_HEIGHT).current;
5034
5416
  const animatedValue = useRef(new Animated.Value(hiddenTranslateY)).current;
@@ -5054,8 +5436,10 @@ const BottomSheet = ({ children, isVisible, onClose, maxHeight = SCREEN_HEIGHT *
5054
5436
  const bottomSheetAnimation = { transform: [{ translateY: animatedValue }] };
5055
5437
  return /* @__PURE__ */ jsxs(Fragment, { children: [isVisible && /* @__PURE__ */ jsx(TouchableWithoutFeedback, {
5056
5438
  onPress: onClose,
5057
- children: /* @__PURE__ */ jsx(View, { style: styles$14.backdrop })
5439
+ testID: "cometchat-bottom-sheet-backdrop",
5440
+ children: /* @__PURE__ */ jsx(View, { style: [commonStyles.absoluteFill, styles$14.backdrop] })
5058
5441
  }), /* @__PURE__ */ jsxs(Animated.View, {
5442
+ testID,
5059
5443
  style: [
5060
5444
  styles$14.bottomSheet,
5061
5445
  bottomSheetAnimation,
@@ -5069,7 +5453,6 @@ const BottomSheet = ({ children, isVisible, onClose, maxHeight = SCREEN_HEIGHT *
5069
5453
  };
5070
5454
  const styles$14 = StyleSheet.create({
5071
5455
  backdrop: {
5072
- ...StyleSheet.absoluteFillObject,
5073
5456
  backgroundColor: "rgba(0,0,0,0.5)",
5074
5457
  zIndex: 1
5075
5458
  },
@@ -5113,8 +5496,9 @@ var BottomSheet_native_default = BottomSheet;
5113
5496
 
5114
5497
  //#endregion
5115
5498
  //#region src/ui/control-pane/MenuItem.native.tsx
5116
- const MenuItem = ({ iconName, label, onPress, selected = false }) => {
5499
+ const MenuItem = ({ iconName, label, onPress, selected = false, testID }) => {
5117
5500
  return /* @__PURE__ */ jsxs(TouchableOpacity, {
5501
+ testID,
5118
5502
  onPress: () => {
5119
5503
  hideAllBottomSheets();
5120
5504
  onPress();
@@ -5167,6 +5551,7 @@ const AudioModesMenu = ({ isVisible, onClose }) => {
5167
5551
  style: [commonStyles.bodyRegular, styles$12.noItemsText],
5168
5552
  children: "No audio modes available"
5169
5553
  }), audioModes.map((mode, index) => /* @__PURE__ */ jsx(MenuItem_native_default, {
5554
+ testID: `cometchat-menu-item-audio-${mode.type.toLowerCase()}`,
5170
5555
  iconName: AUDIO_MODE_TYPE_ICON_MAP[mode.type],
5171
5556
  label: mode.type,
5172
5557
  selected: mode.selected,
@@ -5218,6 +5603,7 @@ const AudioModeButton = () => {
5218
5603
  style: controlPaneStyles.controlButton,
5219
5604
  onPress: toggleAudioModeMenuVisible,
5220
5605
  activeOpacity: .7,
5606
+ testID: "cometchat-audio-mode-button",
5221
5607
  children: /* @__PURE__ */ jsx(Icon_native_default, {
5222
5608
  name: "speaker-fill",
5223
5609
  fill: "#FFF",
@@ -5243,6 +5629,7 @@ const AudioControl = () => {
5243
5629
  style: [controlPaneStyles.controlButton, muted && controlPaneStyles.toggledButton],
5244
5630
  onPress,
5245
5631
  activeOpacity: .7,
5632
+ testID: "cometchat-audio-toggle-button",
5246
5633
  children: /* @__PURE__ */ jsx(Icon_native_default, {
5247
5634
  name: muted ? "mic-off-fill" : "mic-fill",
5248
5635
  fill: muted ? "#9F3032" : "#FFF",
@@ -5268,6 +5655,7 @@ const VideoControl = () => {
5268
5655
  style: [controlPaneStyles.controlButton, videoMuted && controlPaneStyles.toggledButton],
5269
5656
  onPress,
5270
5657
  activeOpacity: .7,
5658
+ testID: "cometchat-video-toggle-button",
5271
5659
  children: /* @__PURE__ */ jsx(Icon_native_default, {
5272
5660
  name: videoMuted ? "video-off-fill" : "video-fill",
5273
5661
  fill: videoMuted ? "#9F3032" : "#FFF",
@@ -5292,6 +5680,7 @@ const LeaveSessionButton = () => {
5292
5680
  style: [controlPaneStyles.controlButton, controlPaneStyles.leaveSessionButton],
5293
5681
  onPress,
5294
5682
  activeOpacity: .7,
5683
+ testID: "cometchat-leave-session-button",
5295
5684
  children: /* @__PURE__ */ jsx(Icon_native_default, {
5296
5685
  name: "call-end",
5297
5686
  fill: "#FFF",
@@ -5308,6 +5697,7 @@ const MoreMenuButton = () => {
5308
5697
  style: controlPaneStyles.controlButton,
5309
5698
  onPress: toggleMoreMenuVisible,
5310
5699
  activeOpacity: .7,
5700
+ testID: "cometchat-more-menu-button",
5311
5701
  children: /* @__PURE__ */ jsx(Icon_native_default, {
5312
5702
  name: "more",
5313
5703
  fill: "#FFF",
@@ -5326,6 +5716,7 @@ const ControlPane = () => {
5326
5716
  }
5327
5717
  return /* @__PURE__ */ jsxs(View, {
5328
5718
  style: controlPaneStyles.controlPane,
5719
+ testID: "cometchat-control-panel",
5329
5720
  children: [
5330
5721
  /* @__PURE__ */ jsx(Audio_native_default, {}),
5331
5722
  /* @__PURE__ */ jsx(Video_native_default, {}),
@@ -5383,22 +5774,26 @@ const MoreMenu = ({ isVisible, onClose }) => {
5383
5774
  toggleParticipantListVisible();
5384
5775
  }, []);
5385
5776
  return /* @__PURE__ */ jsx(BottomSheet_native_default, {
5777
+ testID: "cometchat-more-menu-bottom-sheet",
5386
5778
  maxHeight: bottomSheetMaxHeight,
5387
5779
  isVisible,
5388
5780
  onClose,
5389
5781
  children: /* @__PURE__ */ jsxs(ScrollView, { children: [
5390
5782
  numberOfVisibleItems === 0 && /* @__PURE__ */ jsx(Text, {
5783
+ testID: "cometchat-more-menu-empty-state",
5391
5784
  style: [commonStyles.bodyRegular, styles$11.noItemsText],
5392
5785
  children: "No options available"
5393
5786
  }),
5394
5787
  !hideScreenSharingButton && /* @__PURE__ */ jsx(ScreenShareButton_default, {}),
5395
5788
  !hideRaiseHandButton && /* @__PURE__ */ jsx(MenuItem_native_default, {
5789
+ testID: "cometchat-menu-item-raise-hand",
5396
5790
  iconName: "raise-hand-fill",
5397
5791
  label: raiseHandTimestamp ? "Lower Hand" : "Raise Hand",
5398
5792
  onPress: onRaiseHandPress,
5399
5793
  selected: Boolean(raiseHandTimestamp)
5400
5794
  }),
5401
5795
  !hideRecordingButton && /* @__PURE__ */ jsx(MenuItem_native_default, {
5796
+ testID: isRecording ? "cometchat-menu-item-stop-recording" : "cometchat-menu-item-start-recording",
5402
5797
  iconName: isRecording ? "record-stop-fill" : "record-fill",
5403
5798
  label: isRecording ? "Stop Recording" : "Start Recording",
5404
5799
  onPress: () => {
@@ -5409,6 +5804,7 @@ const MoreMenu = ({ isVisible, onClose }) => {
5409
5804
  }
5410
5805
  }),
5411
5806
  !hideParticipantListButton && /* @__PURE__ */ jsx(MenuItem_native_default, {
5807
+ testID: "cometchat-menu-item-participants",
5412
5808
  iconName: "participants",
5413
5809
  label: "Participants",
5414
5810
  onPress: onParticipantListPress
@@ -5450,14 +5846,15 @@ const FullScreenDialog = ({ children, visible = false }) => {
5450
5846
  return null;
5451
5847
  }
5452
5848
  return /* @__PURE__ */ jsx(Animated.View, {
5453
- style: [styles$10.container, { opacity: fadeAnim }],
5849
+ style: [
5850
+ commonStyles.absoluteFill,
5851
+ styles$10.container,
5852
+ { opacity: fadeAnim }
5853
+ ],
5454
5854
  children
5455
5855
  });
5456
5856
  };
5457
- const styles$10 = StyleSheet.create({ container: {
5458
- ...StyleSheet.absoluteFillObject,
5459
- zIndex: 1e3
5460
- } });
5857
+ const styles$10 = StyleSheet.create({ container: { zIndex: 1e3 } });
5461
5858
  var FullScreenDialog_native_default = FullScreenDialog;
5462
5859
 
5463
5860
  //#endregion
@@ -5535,6 +5932,7 @@ const ChangeLayout = () => {
5535
5932
  eventBus.publish({ type: EVENT_LISTENER_METHODS.ButtonClickListener.onChangeLayoutButtonClicked });
5536
5933
  showMenu();
5537
5934
  },
5935
+ testID: "cometchat-change-layout-button",
5538
5936
  children: /* @__PURE__ */ jsx(Icon_native_default, {
5539
5937
  name: "tile-fill",
5540
5938
  fill: "#FFFFFF"
@@ -5587,6 +5985,7 @@ const ChatButton = () => {
5587
5985
  return /* @__PURE__ */ jsxs(TouchableOpacity, {
5588
5986
  style: [styles$9.iconButton],
5589
5987
  onPress,
5988
+ testID: "cometchat-chat-button",
5590
5989
  children: [/* @__PURE__ */ jsx(Icon_native_default, {
5591
5990
  name: "chat",
5592
5991
  fill: "#FFFFFF"
@@ -5643,6 +6042,7 @@ const SwitchCamera = () => {
5643
6042
  return null;
5644
6043
  }
5645
6044
  return /* @__PURE__ */ jsx(TouchableOpacity, {
6045
+ testID: "cometchat-switch-camera-button",
5646
6046
  disabled,
5647
6047
  style: [styles$8.iconButton, disabled && styles$8.iconButtonDisabled],
5648
6048
  onPress,
@@ -5675,6 +6075,7 @@ const SessionTimer = () => {
5675
6075
  return null;
5676
6076
  }
5677
6077
  return /* @__PURE__ */ jsx(Text, {
6078
+ testID: "cometchat-session-timer",
5678
6079
  style: [commonStyles.caption1Regular, styles$7.meetingTime],
5679
6080
  children: miliSecondsToMMSS(conferenceElapsedTime)
5680
6081
  });
@@ -5687,6 +6088,7 @@ const Header = () => {
5687
6088
  }
5688
6089
  return /* @__PURE__ */ jsxs(View, {
5689
6090
  style: styles$7.headerContainer,
6091
+ testID: "cometchat-header-panel",
5690
6092
  children: [/* @__PURE__ */ jsxs(View, {
5691
6093
  style: styles$7.meetingInfo,
5692
6094
  children: [title && /* @__PURE__ */ jsx(Text, {
@@ -5786,69 +6188,80 @@ const IdealTimeoutModal = ({ style = {} }) => {
5786
6188
  transparent: true,
5787
6189
  visible: idleTimeoutModalVisible,
5788
6190
  animationType: "none",
6191
+ supportedOrientations: [
6192
+ "portrait",
6193
+ "landscape-left",
6194
+ "landscape-right"
6195
+ ],
5789
6196
  statusBarTranslucent: true,
5790
6197
  children: /* @__PURE__ */ jsx(TouchableWithoutFeedback, {
5791
6198
  onPress: handleOverlayPress,
6199
+ testID: "cometchat-idle-timeout-overlay",
5792
6200
  children: /* @__PURE__ */ jsx(Animated.View, {
5793
6201
  style: [styles$6.overlay, { opacity: fadeAnim }],
5794
- children: /* @__PURE__ */ jsx(TouchableWithoutFeedback, { children: /* @__PURE__ */ jsx(Animated.View, {
5795
- style: [
5796
- styles$6.modal,
5797
- style,
5798
- {
5799
- opacity: fadeAnim,
5800
- transform: [{ scale: scaleAnim }]
5801
- }
5802
- ],
5803
- children: /* @__PURE__ */ jsxs(View, {
5804
- style: styles$6.content,
5805
- children: [
5806
- /* @__PURE__ */ jsx(View, {
5807
- style: styles$6.timerIcon,
5808
- children: /* @__PURE__ */ jsx(Text, {
5809
- style: [commonStyles.heading3Bold, styles$6.timerText],
5810
- children: formattedTime
5811
- })
5812
- }),
5813
- /* @__PURE__ */ jsxs(View, {
5814
- style: styles$6.textContent,
5815
- children: [/* @__PURE__ */ jsx(Text, {
5816
- style: [commonStyles.heading2Medium, styles$6.title],
5817
- children: "Are you still there?"
5818
- }), /* @__PURE__ */ jsxs(Text, {
5819
- style: [commonStyles.bodyRegular, styles$6.subtitle],
5820
- children: [
5821
- "You are the only one here, so this call will end in less than ",
5822
- ceilMinutes,
5823
- " minute",
5824
- ceilMinutes > 1 ? "s" : "",
5825
- ". Do you want to stay in this call?"
5826
- ]
5827
- })]
5828
- }),
5829
- /* @__PURE__ */ jsxs(View, {
5830
- style: styles$6.actions,
5831
- children: [/* @__PURE__ */ jsx(TouchableOpacity, {
5832
- style: [styles$6.button, styles$6.buttonSecondary],
5833
- onPress: onStayInCall,
5834
- activeOpacity: .8,
6202
+ children: /* @__PURE__ */ jsx(TouchableWithoutFeedback, {
6203
+ testID: "cometchat-idle-timeout-modal",
6204
+ children: /* @__PURE__ */ jsx(Animated.View, {
6205
+ style: [
6206
+ styles$6.modal,
6207
+ style,
6208
+ {
6209
+ opacity: fadeAnim,
6210
+ transform: [{ scale: scaleAnim }]
6211
+ }
6212
+ ],
6213
+ children: /* @__PURE__ */ jsxs(View, {
6214
+ style: styles$6.content,
6215
+ children: [
6216
+ /* @__PURE__ */ jsx(View, {
6217
+ style: styles$6.timerIcon,
5835
6218
  children: /* @__PURE__ */ jsx(Text, {
5836
- style: [commonStyles.bodyMedium, styles$6.buttonSecondaryText],
5837
- children: "Stay on the call"
6219
+ style: [commonStyles.heading3Bold, styles$6.timerText],
6220
+ children: formattedTime
5838
6221
  })
5839
- }), /* @__PURE__ */ jsx(TouchableOpacity, {
5840
- style: [styles$6.button, styles$6.buttonPrimary],
5841
- onPress: leaveSession,
5842
- activeOpacity: .8,
5843
- children: /* @__PURE__ */ jsx(Text, {
5844
- style: [commonStyles.bodyMedium, styles$6.buttonPrimaryText],
5845
- children: "Leave now"
5846
- })
5847
- })]
5848
- })
5849
- ]
6222
+ }),
6223
+ /* @__PURE__ */ jsxs(View, {
6224
+ style: styles$6.textContent,
6225
+ children: [/* @__PURE__ */ jsx(Text, {
6226
+ style: [commonStyles.heading2Medium, styles$6.title],
6227
+ children: "Are you still there?"
6228
+ }), /* @__PURE__ */ jsxs(Text, {
6229
+ style: [commonStyles.bodyRegular, styles$6.subtitle],
6230
+ children: [
6231
+ "You are the only one here, so this call will end in less than ",
6232
+ ceilMinutes,
6233
+ " minute",
6234
+ ceilMinutes > 1 ? "s" : "",
6235
+ ". Do you want to stay in this call?"
6236
+ ]
6237
+ })]
6238
+ }),
6239
+ /* @__PURE__ */ jsxs(View, {
6240
+ style: styles$6.actions,
6241
+ children: [/* @__PURE__ */ jsx(TouchableOpacity, {
6242
+ style: [styles$6.button, styles$6.buttonSecondary],
6243
+ onPress: onStayInCall,
6244
+ activeOpacity: .8,
6245
+ testID: "cometchat-idle-timeout-stay-button",
6246
+ children: /* @__PURE__ */ jsx(Text, {
6247
+ style: [commonStyles.bodyMedium, styles$6.buttonSecondaryText],
6248
+ children: "Stay on the call"
6249
+ })
6250
+ }), /* @__PURE__ */ jsx(TouchableOpacity, {
6251
+ style: [styles$6.button, styles$6.buttonPrimary],
6252
+ onPress: () => leaveSession(),
6253
+ activeOpacity: .8,
6254
+ testID: "cometchat-idle-timeout-leave-button",
6255
+ children: /* @__PURE__ */ jsx(Text, {
6256
+ style: [commonStyles.bodyMedium, styles$6.buttonPrimaryText],
6257
+ children: "Leave now"
6258
+ })
6259
+ })]
6260
+ })
6261
+ ]
6262
+ })
5850
6263
  })
5851
- }) })
6264
+ })
5852
6265
  })
5853
6266
  })
5854
6267
  });
@@ -5867,6 +6280,7 @@ const styles$6 = StyleSheet.create({
5867
6280
  borderWidth: 1,
5868
6281
  borderColor: "#383838",
5869
6282
  width: "100%",
6283
+ maxWidth: 372,
5870
6284
  paddingTop: 32,
5871
6285
  paddingHorizontal: 20,
5872
6286
  paddingBottom: 20,
@@ -5945,6 +6359,7 @@ const ShareInviteButton = () => {
5945
6359
  style: styles$5.shareButtonContainer,
5946
6360
  children: /* @__PURE__ */ jsxs(TouchableOpacity, {
5947
6361
  style: styles$5.shareButton,
6362
+ testID: "cometchat-share-invite-button",
5948
6363
  onPress: () => {
5949
6364
  eventBus.publish({ type: "onShareInviteButtonClicked" });
5950
6365
  },
@@ -6081,6 +6496,7 @@ const ParticipantList = () => {
6081
6496
  }), /* @__PURE__ */ jsx(TouchableOpacity, {
6082
6497
  onPress: toggleParticipantListVisible,
6083
6498
  accessibilityLabel: "Close participants list",
6499
+ testID: "cometchat-participant-list-close-button",
6084
6500
  children: /* @__PURE__ */ jsx(Icon_native_default, {
6085
6501
  name: "close",
6086
6502
  size: 24,
@@ -6102,7 +6518,8 @@ const ParticipantList = () => {
6102
6518
  style: styles$3.searchInput,
6103
6519
  value: searchTerm,
6104
6520
  onChangeText: setSearchTerm,
6105
- placeholderTextColor: "#858585"
6521
+ placeholderTextColor: "#858585",
6522
+ testID: "cometchat-participant-search-input"
6106
6523
  })]
6107
6524
  })
6108
6525
  }),
@@ -6243,45 +6660,6 @@ const styles$2 = StyleSheet.create({
6243
6660
  }
6244
6661
  });
6245
6662
 
6246
- //#endregion
6247
- //#region src/ui/toast/toast-state.ts
6248
- const MAX_TOASTS = 3;
6249
- let counter = 0;
6250
- const useToastStore = create((set) => ({
6251
- toasts: [],
6252
- addToast: (toast) => {
6253
- const id = `toast-${++counter}-${Date.now()}`;
6254
- const newToast = {
6255
- ...toast,
6256
- id,
6257
- createdAt: Date.now()
6258
- };
6259
- set((state) => {
6260
- const updated = [...state.toasts, newToast];
6261
- if (updated.length > MAX_TOASTS) {
6262
- return { toasts: updated.slice(-MAX_TOASTS) };
6263
- }
6264
- return { toasts: updated };
6265
- });
6266
- return id;
6267
- },
6268
- removeToast: (id) => {
6269
- set((state) => ({ toasts: state.toasts.filter((t) => t.id !== id) }));
6270
- },
6271
- clearAll: () => {
6272
- set({ toasts: [] });
6273
- }
6274
- }));
6275
- function showToast(params) {
6276
- return useToastStore.getState().addToast({
6277
- message: params.message,
6278
- type: params.type ?? "info",
6279
- duration: params.duration ?? 3e3,
6280
- position: params.position ?? "bottom",
6281
- action: params.action
6282
- });
6283
- }
6284
-
6285
6663
  //#endregion
6286
6664
  //#region src/ui/toast/Toast.native.tsx
6287
6665
  const SLIDE_IN_DURATION = 300;
@@ -6356,6 +6734,7 @@ function ToastItemView({ toast, onDismiss }) {
6356
6734
  }), toast.action && /* @__PURE__ */ jsx(Pressable, {
6357
6735
  style: styles$2.actionButton,
6358
6736
  onPress: handleActionPress,
6737
+ testID: "cometchat-toast-action-button",
6359
6738
  children: /* @__PURE__ */ jsx(Text, {
6360
6739
  style: styles$2.actionText,
6361
6740
  children: toast.action.label
@@ -6408,15 +6787,20 @@ function CallUI(props) {
6408
6787
  const isConferenceJoined = useIsConferenceJoined();
6409
6788
  useLayoutEffect(() => {
6410
6789
  eventBus.publish({ type: INTERNAL_EVENTS.lifecycle.componentDidMount });
6411
- updateConfig(props.callSettings);
6790
+ updateConfig(props.sessionSettings);
6412
6791
  return () => {
6413
6792
  eventBus.publish({ type: INTERNAL_EVENTS.lifecycle.componentWillUnmount }, true);
6414
6793
  };
6415
- }, [props.callSettings]);
6794
+ }, [props.sessionSettings]);
6416
6795
  useEffect(() => {
6417
- useBaseStore.setState({ sdkPlatform: Platform.OS });
6796
+ if (props.sessionSettings.sdkPlatform) {
6797
+ useBaseStore.setState({ sdkPlatform: props.sessionSettings.sdkPlatform });
6798
+ } else {
6799
+ useBaseStore.setState({ sdkPlatform: Platform.OS === "ios" ? "react-native-ios" : "react-native-android" });
6800
+ }
6801
+ useBaseStore.setState({ platformVersion: Platform.Version });
6418
6802
  startSession();
6419
- }, []);
6803
+ }, [props.sessionSettings.sdkPlatform]);
6420
6804
  useEffect(() => {
6421
6805
  const sub = AudioModeEventEmitter.addListener(AudioModeModule_default.DEVICE_CHANGE_EVENT, (e) => {
6422
6806
  console.log("AudioMode.DEVICE_CHANGE_EVENT", e);
@@ -6429,13 +6813,13 @@ function CallUI(props) {
6429
6813
  useEffect(() => {
6430
6814
  if (Platform.OS === "android") {
6431
6815
  AudioModeModule_default.setMode(type === SESSION_TYPE.VOICE ? AudioModeModule_default.AUDIO_CALL : AudioModeModule_default.VIDEO_CALL);
6432
- if (props.callSettings.audioMode) {
6433
- AudioModeModule_default.setAudioDevice(props.callSettings.audioMode);
6816
+ if (props.sessionSettings.audioMode) {
6817
+ AudioModeModule_default.setAudioDevice(props.sessionSettings.audioMode);
6434
6818
  }
6435
6819
  } else if (Platform.OS === "ios") {
6436
6820
  AudioModeModule_default.updateDeviceList();
6437
6821
  }
6438
- }, [props.callSettings.audioMode, type]);
6822
+ }, [props.sessionSettings.audioMode, type]);
6439
6823
  if (isPIPLayoutEnabled) {
6440
6824
  return /* @__PURE__ */ jsx(PiPTile_default, {});
6441
6825
  }
@@ -6482,13 +6866,6 @@ const styles$1 = StyleSheet.create({
6482
6866
  alignItems: "center",
6483
6867
  gap: GRID_GAP,
6484
6868
  paddingHorizontal: 20
6485
- },
6486
- loadingContainer: {
6487
- ...StyleSheet.absoluteFillObject,
6488
- justifyContent: "center",
6489
- alignItems: "center",
6490
- backgroundColor: "#000",
6491
- zIndex: 1e3
6492
6869
  }
6493
6870
  });
6494
6871
  var index_native_default = CallUI;
@@ -6564,10 +6941,10 @@ const convertLegacyCallSettingsToV5Props = (callSettings) => {
6564
6941
  if (cs.defaultAudioMode === "BLUETOOTH" || cs.defaultAudioMode === "EARPIECE" || cs.defaultAudioMode === "HEADPHONES" || cs.defaultAudioMode === "SPEAKER") {
6565
6942
  v5Props.audioMode = cs.defaultAudioMode;
6566
6943
  }
6567
- if (cs.mode === "SPOTLIGHT") {
6568
- v5Props.layout = "SPOTLIGHT";
6569
- } else {
6570
- v5Props.layout = "SIDEBAR";
6944
+ if (typeof cs.layout === "string") {
6945
+ v5Props.layout = cs.layout;
6946
+ } else if (cs.mode === "SPOTLIGHT" || cs.mode === "SIDEBAR") {
6947
+ v5Props.layout = cs.mode;
6571
6948
  }
6572
6949
  if (cs.idleTimeoutPeriod) {
6573
6950
  v5Props.idleTimeoutPeriodAfterPrompt = 6e4;
@@ -6727,10 +7104,11 @@ async function callVerifyTokenAPI({ appId, region, calltoken, baseURL }) {
6727
7104
  }
6728
7105
 
6729
7106
  //#endregion
6730
- //#region src/AppRN.tsx
6731
- function App(props) {
7107
+ //#region src/AppReactNativeSDK.tsx
7108
+ function AppReactNativeSDK(props) {
6732
7109
  const [internalSettings, setInternalSettings] = React.useState(null);
6733
7110
  const [infoMessage, setInfoMessage] = React.useState(null);
7111
+ const sessionSettings = props.sessionSettings ?? props.callSettings ?? {};
6734
7112
  useEffect(() => {
6735
7113
  return eventBus.subscribe("onConnectionClosed", () => {
6736
7114
  setTimeout(() => {
@@ -6740,7 +7118,7 @@ function App(props) {
6740
7118
  }, []);
6741
7119
  useEffect(() => {
6742
7120
  const listeners = [];
6743
- const cs = props.callSettings ?? {};
7121
+ const cs = sessionSettings ?? {};
6744
7122
  if (cs.listener?.onUserJoined) {
6745
7123
  listeners.push(CometChatCalls.addEventListener("onParticipantJoined", cs.listener.onUserJoined));
6746
7124
  }
@@ -6778,7 +7156,7 @@ function App(props) {
6778
7156
  listener();
6779
7157
  });
6780
7158
  };
6781
- }, [props.callSettings]);
7159
+ }, [sessionSettings]);
6782
7160
  useEffect(() => {
6783
7161
  callVerifyTokenAPI({
6784
7162
  appId: CometChatCalls.appSettings?.appId || "",
@@ -6807,14 +7185,12 @@ function App(props) {
6807
7185
  visible: true
6808
7186
  });
6809
7187
  }
6810
- return /* @__PURE__ */ jsx(index_native_default, { callSettings: {
6811
- ...props.callSettings,
6812
- ...convertLegacyCallSettingsToV5Props(props?.callSettings ?? {}),
7188
+ return /* @__PURE__ */ jsx(index_native_default, { sessionSettings: {
7189
+ ...sessionSettings,
7190
+ ...convertLegacyCallSettingsToV5Props(sessionSettings ?? {}),
6813
7191
  internalSettings
6814
7192
  } });
6815
7193
  }
6816
- var AppRN_default = App;
6817
- const AppComponent = App;
6818
7194
 
6819
7195
  //#endregion
6820
7196
  //#region src/v4/Constants.ts
@@ -10547,7 +10923,13 @@ var CometChatCalls = class extends SessionMethodsCore {
10547
10923
  static OngoingCallListener = OngoingCallListener;
10548
10924
  static CallSettingsBuilder = CallSettingsBuilder;
10549
10925
  static CallAppSettingsBuilder = CallAppSettingsBuilder;
10550
- static Component = AppComponent;
10926
+ static Component = AppReactNativeSDK;
10927
+ /**
10928
+ * Initializes the CometChat Calls SDK with the provided app settings.
10929
+ * Must be called before any other SDK methods.
10930
+ * @param appSettings - The application settings for configuring the SDK.
10931
+ * @returns An object indicating success or failure with error details.
10932
+ */
10551
10933
  static async init(appSettings) {
10552
10934
  const parsedAppSettings = v.safeParse(CallAppSettingsSchema, appSettings);
10553
10935
  if (!parsedAppSettings.success) {
@@ -10563,7 +10945,7 @@ var CometChatCalls = class extends SessionMethodsCore {
10563
10945
  }
10564
10946
  this.appSettings = parsedAppSettings.output;
10565
10947
  this.isInitialized = true;
10566
- const savedUser = this.getSavedUser();
10948
+ const savedUser = await this.getSavedUser();
10567
10949
  if (savedUser) {
10568
10950
  let parsedUser;
10569
10951
  if (typeof savedUser === "string") {
@@ -10582,6 +10964,14 @@ var CometChatCalls = class extends SessionMethodsCore {
10582
10964
  error: null
10583
10965
  };
10584
10966
  }
10967
+ /**
10968
+ * Logs in a user with their UID and an optional auth key.
10969
+ * If no auth key is provided, the one from app settings is used.
10970
+ * @param uid - The unique identifier of the user.
10971
+ * @param authKey - The authentication key. Falls back to the key provided in app settings.
10972
+ * @returns A Promise that resolves to the logged-in User object.
10973
+ * @throws {CometChatException} If login fails or validation errors occur.
10974
+ */
10585
10975
  static async login(uid, authKey) {
10586
10976
  try {
10587
10977
  if (this.loginInProgress) {
@@ -10624,12 +11014,11 @@ var CometChatCalls = class extends SessionMethodsCore {
10624
11014
  if (this.loggedInUser && this.loggedInUser.uid !== uid) {
10625
11015
  await this.logoutInternal();
10626
11016
  }
10627
- console.log("Logging in user with UID:", uid);
10628
11017
  const authToken = await this.loginWithUID(uid, resolvedAuthKey);
10629
11018
  const user = await this.authenticateWithToken(authToken);
10630
11019
  this.loginInProgress = false;
10631
11020
  this.loggedInUser = user;
10632
- this.saveUser(user);
11021
+ await this.saveUser(user);
10633
11022
  this.notifyLoginSuccess(user);
10634
11023
  return user;
10635
11024
  } catch (error) {
@@ -10639,6 +11028,12 @@ var CometChatCalls = class extends SessionMethodsCore {
10639
11028
  throw cometChatError;
10640
11029
  }
10641
11030
  }
11031
+ /**
11032
+ * Logs in a user directly with an auth token.
11033
+ * @param authToken - The authentication token for the user.
11034
+ * @returns A Promise that resolves to the logged-in User object.
11035
+ * @throws {CometChatException} If login fails or the token is invalid.
11036
+ */
10642
11037
  static async loginWithAuthToken(authToken) {
10643
11038
  try {
10644
11039
  if (this.loginInProgress) {
@@ -10671,7 +11066,7 @@ var CometChatCalls = class extends SessionMethodsCore {
10671
11066
  const user = await this.authenticateWithToken(authToken);
10672
11067
  this.loginInProgress = false;
10673
11068
  this.loggedInUser = user;
10674
- this.saveUser(user);
11069
+ await this.saveUser(user);
10675
11070
  this.notifyLoginSuccess(user);
10676
11071
  return user;
10677
11072
  } catch (error) {
@@ -10681,6 +11076,11 @@ var CometChatCalls = class extends SessionMethodsCore {
10681
11076
  throw cometChatError;
10682
11077
  }
10683
11078
  }
11079
+ /**
11080
+ * Logs out the currently logged-in user and clears local session data.
11081
+ * @returns A Promise that resolves to a success message string.
11082
+ * @throws {CometChatException} If no user is logged in or logout fails.
11083
+ */
10684
11084
  static async logout() {
10685
11085
  try {
10686
11086
  if (!this.loggedInUser) {
@@ -10698,24 +11098,53 @@ var CometChatCalls = class extends SessionMethodsCore {
10698
11098
  throw cometChatError;
10699
11099
  }
10700
11100
  }
11101
+ /**
11102
+ * Retrieves the currently logged-in user.
11103
+ * @returns The logged-in User object, or null if no user is logged in.
11104
+ */
10701
11105
  static getLoggedInUser() {
10702
11106
  if (this.loggedInUser && typeof this.loggedInUser === "string") {
10703
11107
  this.loggedInUser = JSON.parse(this.loggedInUser);
10704
11108
  }
10705
11109
  return this.loggedInUser;
10706
11110
  }
11111
+ /**
11112
+ * Retrieves the auth token of the currently logged-in user.
11113
+ * @returns The auth token string, or null if no user is logged in.
11114
+ */
10707
11115
  static getUserAuthToken() {
10708
11116
  return this.loggedInUser?.authToken || null;
10709
11117
  }
11118
+ /**
11119
+ * Checks whether a user is currently logged in.
11120
+ * @returns True if a user is logged in with a valid auth token, false otherwise.
11121
+ */
10710
11122
  static isUserLoggedIn() {
10711
11123
  return this.loggedInUser !== null && this.loggedInUser.authToken !== undefined;
10712
11124
  }
11125
+ /**
11126
+ * Registers a login listener to receive login/logout lifecycle callbacks.
11127
+ * @param listenerId - A unique identifier for the listener.
11128
+ * @param listener - The listener object with callback methods.
11129
+ */
10713
11130
  static addLoginListener(listenerId, listener) {
10714
11131
  this.loginListeners.set(listenerId, listener);
10715
11132
  }
11133
+ /**
11134
+ * Removes a previously registered login listener.
11135
+ * @param listenerId - The unique identifier of the listener to remove.
11136
+ */
10716
11137
  static removeLoginListener(listenerId) {
10717
11138
  this.loginListeners.delete(listenerId);
10718
11139
  }
11140
+ /**
11141
+ * Generates a call token for the given session.
11142
+ * Uses the provided auth token or falls back to the logged-in user's token.
11143
+ * @param sessionId - The session ID to generate a token for.
11144
+ * @param authToken - Optional auth token. If omitted, the logged-in user's token is used.
11145
+ * @returns A Promise that resolves to an object containing the generated token.
11146
+ * @throws {CometChatException} If the session ID is missing, no auth token is available, or the SDK is not initialized.
11147
+ */
10719
11148
  static async generateToken(sessionId, authToken) {
10720
11149
  try {
10721
11150
  if (!sessionId || sessionId.trim() === "") {
@@ -10770,7 +11199,7 @@ var CometChatCalls = class extends SessionMethodsCore {
10770
11199
  appId
10771
11200
  },
10772
11201
  body: {
10773
- platform: "web",
11202
+ platform: "react-native",
10774
11203
  deviceId: this.generateDeviceId()
10775
11204
  }
10776
11205
  });
@@ -10791,7 +11220,7 @@ var CometChatCalls = class extends SessionMethodsCore {
10791
11220
  appId
10792
11221
  },
10793
11222
  body: {
10794
- platform: "web",
11223
+ platform: "react-native",
10795
11224
  deviceId: this.generateDeviceId()
10796
11225
  }
10797
11226
  });
@@ -10831,7 +11260,7 @@ var CometChatCalls = class extends SessionMethodsCore {
10831
11260
  }
10832
11261
  }
10833
11262
  this.loggedInUser = null;
10834
- this.clearSavedUser();
11263
+ await this.clearSavedUser();
10835
11264
  }
10836
11265
  static async callGenerateTokenAPI(sessionId, authToken) {
10837
11266
  const appId = this.appSettings?.appId || "";
@@ -10859,9 +11288,11 @@ var CometChatCalls = class extends SessionMethodsCore {
10859
11288
  baseURL: this.getBaseURL()
10860
11289
  });
10861
11290
  }
10862
- static saveUser(user) {
11291
+ static getStorageKey() {
11292
+ return `${this.appSettings?.appId}:common_store/user`;
11293
+ }
11294
+ static async saveUser(user) {
10863
11295
  try {
10864
- const key = `${this.appSettings?.appId}:common_store/user`;
10865
11296
  const userWithDefaults = {
10866
11297
  hasBlockedMe: false,
10867
11298
  blockedByMe: false,
@@ -10870,31 +11301,28 @@ var CometChatCalls = class extends SessionMethodsCore {
10870
11301
  role: user.role || "default",
10871
11302
  wsChannel: user.wsChannel || { identity: `[${this.appSettings?.appId}]${user.uid}` }
10872
11303
  };
10873
- localStorage.setItem(key, JSON.stringify(userWithDefaults));
11304
+ await AsyncStorage.setItem(this.getStorageKey(), JSON.stringify(userWithDefaults));
10874
11305
  } catch (error) {
10875
- console.warn("Failed to save user to localStorage:", error);
11306
+ console.warn("Failed to save user to AsyncStorage:", error);
10876
11307
  }
10877
11308
  }
10878
- static getSavedUser() {
11309
+ static async getSavedUser() {
10879
11310
  try {
10880
- const key = `${this.appSettings?.appId}:common_store/user`;
10881
- const savedUser = localStorage.getItem(key);
10882
- return savedUser ? savedUser : null;
11311
+ return await AsyncStorage.getItem(this.getStorageKey());
10883
11312
  } catch (error) {
10884
- console.warn("Failed to get saved user from localStorage:", error);
11313
+ console.warn("Failed to get saved user from AsyncStorage:", error);
10885
11314
  return null;
10886
11315
  }
10887
11316
  }
10888
- static clearSavedUser() {
11317
+ static async clearSavedUser() {
10889
11318
  try {
10890
- const key = `${this.appSettings?.appId}:common_store/user`;
10891
- localStorage.removeItem(key);
11319
+ await AsyncStorage.removeItem(this.getStorageKey());
10892
11320
  } catch (error) {
10893
- console.warn("Failed to clear saved user from localStorage:", error);
11321
+ console.warn("Failed to clear saved user from AsyncStorage:", error);
10894
11322
  }
10895
11323
  }
10896
11324
  static generateDeviceId() {
10897
- return "web_" + Math.random().toString(36).substr(2, 9);
11325
+ return "rn_" + Math.random().toString(36).substring(2, 11);
10898
11326
  }
10899
11327
  static createError(error) {
10900
11328
  if (error.errorCode && error.errorDescription) {
@@ -10949,11 +11377,36 @@ var CometChatCalls = class extends SessionMethodsCore {
10949
11377
  }
10950
11378
  });
10951
11379
  }
10952
- static addEventListener(eventType, listener) {
10953
- return eventBus.subscribe(eventType, listener);
11380
+ /**
11381
+ * Adds an event listener for SDK events.
11382
+ * @param eventType - The type of event to listen for.
11383
+ * @param listener - The callback function to invoke when the event fires.
11384
+ * @param options - Optional configuration including an AbortSignal for automatic cleanup.
11385
+ * @returns An unsubscribe function to remove the listener.
11386
+ */
11387
+ static addEventListener(eventType, listener, options) {
11388
+ return eventBus.subscribe(eventType, listener, options);
11389
+ }
11390
+ /**
11391
+ * Sets the audio output mode (mobile only).
11392
+ * @param mode - The audio mode to set (e.g., 'SPEAKER', 'EARPIECE', 'BLUETOOTH', 'HEADPHONES').
11393
+ */
11394
+ static setAudioMode(mode) {
11395
+ setAudioMode(mode);
11396
+ }
11397
+ /**
11398
+ * Enables Picture-in-Picture (PIP) layout during the call.
11399
+ */
11400
+ static enablePictureInPictureLayout() {
11401
+ enablePictureInPictureLayout();
11402
+ }
11403
+ /**
11404
+ * Disables Picture-in-Picture (PIP) layout.
11405
+ */
11406
+ static disablePictureInPictureLayout() {
11407
+ disablePictureInPictureLayout();
10954
11408
  }
10955
11409
  };
10956
11410
 
10957
11411
  //#endregion
10958
- export { CometChatCalls };
10959
- //# sourceMappingURL=index.mjs.map
11412
+ export { CometChatCalls };