@cometchat/calls-sdk-react-native 5.0.0-beta.2 → 5.0.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -35,6 +35,8 @@ let react = require("react");
35
35
  react = __toESM(react);
36
36
  let lib_jitsi_meet = require("lib-jitsi-meet");
37
37
  lib_jitsi_meet = __toESM(lib_jitsi_meet);
38
+ let __react_native_async_storage_async_storage = require("@react-native-async-storage/async-storage");
39
+ __react_native_async_storage_async_storage = __toESM(__react_native_async_storage_async_storage);
38
40
  let react_native = require("react-native");
39
41
  let react_native_webrtc = require("react-native-webrtc");
40
42
  let react_jsx_runtime = require("react/jsx-runtime");
@@ -75,17 +77,24 @@ var EventBus = class {
75
77
  }
76
78
  }
77
79
  }
78
- subscribe(actionType, listener) {
80
+ subscribe(actionType, listener, options) {
81
+ if (options?.signal?.aborted) {
82
+ return () => {};
83
+ }
79
84
  if (!this.actionListenersMap.get(actionType)) {
80
85
  this.actionListenersMap.set(actionType, []);
81
86
  }
82
87
  this.actionListenersMap.get(actionType)?.push(listener);
83
- return () => {
88
+ const unsubscribe = () => {
84
89
  const listenersList = this.actionListenersMap.get(actionType);
85
90
  if (listenersList) {
86
91
  this.actionListenersMap.set(actionType, listenersList.filter((l) => l !== listener));
87
92
  }
88
93
  };
94
+ if (options?.signal) {
95
+ options.signal.addEventListener("abort", unsubscribe, { once: true });
96
+ }
97
+ return unsubscribe;
89
98
  }
90
99
  };
91
100
  const eventBus = new EventBus();
@@ -135,10 +144,12 @@ const VIDEO_QUALITY_LEVELS = {
135
144
  LOW: 180,
136
145
  NONE: 0
137
146
  };
138
- const PLATFORM = {
147
+ const SDK_PLATFORM = {
139
148
  WEB: "web",
140
149
  ANDROID: "android",
141
- IOS: "ios"
150
+ IOS: "ios",
151
+ REACT_NATIVE_ANDROID: "react-native-android",
152
+ REACT_NATIVE_IOS: "react-native-ios"
142
153
  };
143
154
  const EVENT_LISTENER_METHODS = {
144
155
  SessionStatusListener: {
@@ -253,7 +264,7 @@ function calculateTileLayout(containerWidth, containerHeight, numberOfTiles) {
253
264
  const tileArea = totalArea / numberOfTiles;
254
265
  const minArea = MIN_TILE_WIDTH * MIN_TILE_WIDTH * MIN_ASPECT_RATIO;
255
266
  if (tileArea < minArea) {
256
- const columnCount$1 = Math.floor(containerWidth / MIN_TILE_WIDTH);
267
+ const columnCount$1 = Math.max(2, Math.floor(containerWidth / MIN_TILE_WIDTH));
257
268
  const rowCount$1 = Math.ceil(numberOfTiles / columnCount$1);
258
269
  const totalHorizontalGap$1 = columnCount$1 * GRID_GAP;
259
270
  const tileWidth$1 = (containerWidth - totalHorizontalGap$1) / columnCount$1;
@@ -436,12 +447,37 @@ function isDeviceEqual(device1, device2) {
436
447
  function getDefaultDevice(devices) {
437
448
  return devices.find((device) => device.deviceId === "default") || devices[0];
438
449
  }
450
+ /**
451
+ * Returns a promise that resolves when the given Zustand store
452
+ * satisfies the provided predicate. Resolves immediately if the
453
+ * condition is already met. Includes a timeout to avoid hanging
454
+ * forever (defaults to 5 000 ms).
455
+ */
456
+ function waitForStoreState(store, predicate, timeoutMs = 5e3) {
457
+ return new Promise((resolve, reject) => {
458
+ if (predicate(store.getState())) {
459
+ resolve();
460
+ return;
461
+ }
462
+ const timer = setTimeout(() => {
463
+ unsubscribe();
464
+ reject(new Error("waitForStoreState timed out"));
465
+ }, timeoutMs);
466
+ const unsubscribe = store.subscribe((state) => {
467
+ if (predicate(state)) {
468
+ clearTimeout(timer);
469
+ unsubscribe();
470
+ resolve();
471
+ }
472
+ });
473
+ });
474
+ }
439
475
 
440
476
  //#endregion
441
477
  //#region calls-sdk-core/utils/try-catch.ts
442
- async function tryCatch(promise) {
478
+ async function tryCatch(promise, timeoutMs) {
443
479
  try {
444
- const data = await promise;
480
+ const data = timeoutMs != null ? await Promise.race([promise, new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), timeoutMs))]) : await promise;
445
481
  return {
446
482
  data,
447
483
  error: null
@@ -498,6 +534,13 @@ var SessionMethodsCore = class {
498
534
  unMuteAudioTrack();
499
535
  }
500
536
  /**
537
+ * Toggles the local user's audio mute state.
538
+ * If audio is muted, it will be unmuted, and vice versa.
539
+ */
540
+ static toggleAudio() {
541
+ toggleAudioTrack();
542
+ }
543
+ /**
501
544
  * Pauses the local user's video stream.
502
545
  */
503
546
  static pauseVideo() {
@@ -510,22 +553,17 @@ var SessionMethodsCore = class {
510
553
  resumeVideoTrack();
511
554
  }
512
555
  /**
513
- * Local user leaves the current session.
514
- */
515
- static leaveSession() {
516
- leaveSession();
517
- }
518
- /**
519
- * Starts sharing the user's screen with other participants.
556
+ * Toggles the local user's video stream.
557
+ * If video is paused, it will be resumed, and vice versa.
520
558
  */
521
- static startScreenSharing() {
522
- startScreenSharing();
559
+ static toggleVideo() {
560
+ toggleVideoTrack();
523
561
  }
524
562
  /**
525
- * Stops the ongoing screen sharing session.
563
+ * Local user leaves the current session.
526
564
  */
527
- static stopScreenSharing() {
528
- stopScreenSharing();
565
+ static leaveSession() {
566
+ leaveSession();
529
567
  }
530
568
  /**
531
569
  * Raises the user's virtual hand in the call.
@@ -540,6 +578,13 @@ var SessionMethodsCore = class {
540
578
  lowerHandLocal();
541
579
  }
542
580
  /**
581
+ * Toggles the user's virtual hand state.
582
+ * If the hand is raised, it will be lowered, and vice versa.
583
+ */
584
+ static toggleHand() {
585
+ toggleRaiseHand();
586
+ }
587
+ /**
543
588
  * Switches between the front and rear camera.
544
589
  */
545
590
  static switchCamera() {
@@ -555,22 +600,21 @@ var SessionMethodsCore = class {
555
600
  /**
556
601
  * Starts recording the call.
557
602
  */
558
- static startRecording() {}
603
+ static startRecording() {
604
+ startRecording();
605
+ }
559
606
  /**
560
607
  * Stops the ongoing call recording.
561
608
  */
562
- static stopRecording() {}
563
- /**
564
- * Enables Picture-in-Picture (PIP) layout during the call.
565
- */
566
- static enablePictureInPictureLayout() {
567
- enablePictureInPictureLayout();
609
+ static stopRecording() {
610
+ stopRecording();
568
611
  }
569
612
  /**
570
- * Disables Picture-in-Picture (PIP) layout.
613
+ * Toggles the call recording state.
614
+ * If recording is active, it will be stopped, and vice versa.
571
615
  */
572
- static disablePictureInPictureLayout() {
573
- disablePictureInPictureLayout();
616
+ static toggleRecording() {
617
+ toggleRecording();
574
618
  }
575
619
  /**
576
620
  * Pins a participant's video to focus on them.
@@ -608,16 +652,22 @@ var SessionMethodsCore = class {
608
652
  setChatButtonUnreadCount(count);
609
653
  }
610
654
  /**
611
- * @deprecated use startScreenSharing() instead
655
+ * Toggles the visibility of the participant list panel.
656
+ */
657
+ static toggleParticipantList() {
658
+ toggleParticipantList();
659
+ }
660
+ /**
661
+ * Shows the participant list panel.
612
662
  */
613
- static startScreenShare() {
614
- this.startScreenSharing();
663
+ static showParticipantList() {
664
+ showParticipantList();
615
665
  }
616
666
  /**
617
- * @deprecated use stopScreenSharing() instead
667
+ * Hides the participant list panel.
618
668
  */
619
- static stopScreenShare() {
620
- this.stopScreenSharing();
669
+ static hideParticipantList() {
670
+ hideParticipantList();
621
671
  }
622
672
  /**
623
673
  * @deprecated switchToVideoCall is deprecated and not supported.
@@ -710,6 +760,27 @@ async function createLocalTrack(type, deviceId = null, cameraFacing = CAMERA_FAC
710
760
  }
711
761
  }
712
762
  }
763
+ function createLocalTracks() {
764
+ const enableCompanionMode = useConfigStore.getState().enableCompanionMode;
765
+ if (!enableCompanionMode) {
766
+ const audioInputDeviceId = useConfigStore.getState().audioInputDeviceId ?? useBaseStore.getState().audioInputDevice?.deviceId;
767
+ createLocalTrack("audio", audioInputDeviceId);
768
+ }
769
+ const sessionType = useConfigStore.getState().sessionType;
770
+ if (sessionType === SESSION_TYPE.VIDEO) {
771
+ const videoInputDeviceIdP1 = useConfigStore.getState().videoInputDeviceId;
772
+ const videoInputDeviceIdP2 = useBaseStore.getState().videoInputDevice?.deviceId;
773
+ const initialCameraFacingP1 = useConfigStore.getState().initialCameraFacing;
774
+ const initialCameraFacingP2 = useBaseStore.getState().cameraFacing;
775
+ if (videoInputDeviceIdP1) {
776
+ createLocalTrack("video", videoInputDeviceIdP1);
777
+ } else if (initialCameraFacingP1) {
778
+ createLocalTrack("video", null, initialCameraFacingP2);
779
+ } else {
780
+ createLocalTrack("video", videoInputDeviceIdP2, initialCameraFacingP2);
781
+ }
782
+ }
783
+ }
713
784
  function updateAudioInputDevice(deviceId) {
714
785
  const audioInputDevices = useBaseStore.getState().audioInputDevices.filter((device) => device.deviceId !== "");
715
786
  if (audioInputDevices.length > 0) {
@@ -909,7 +980,6 @@ const initialState$7 = {
909
980
  hideLeaveSessionButton: false,
910
981
  hideToggleAudioButton: false,
911
982
  hideParticipantListButton: false,
912
- hideSwitchLayoutButton: false,
913
983
  hideChatButton: true,
914
984
  hideToggleVideoButton: false,
915
985
  hideScreenSharingButton: false,
@@ -926,7 +996,9 @@ const initialState$7 = {
926
996
  idleTimeoutPeriodAfterPrompt: 18e4,
927
997
  enableSpotlightDrag: true,
928
998
  enableSpotlightSwap: true,
929
- showFrameRate: false
999
+ showFrameRate: false,
1000
+ enableCompanionMode: false,
1001
+ isPeerCall: false
930
1002
  };
931
1003
  const useConfigStore = (0, zustand.create)()((0, zustand_middleware.subscribeWithSelector)((0, zustand_middleware.combine)(initialState$7, (set) => ({ reset: () => set(initialState$7) }))));
932
1004
  const setConfig = (config) => {
@@ -954,10 +1026,16 @@ const initialState$6 = {
954
1026
  };
955
1027
  const useParticipantStore = (0, zustand.create)()((0, zustand_middleware.subscribeWithSelector)((0, zustand_middleware.combine)(initialState$6, (set, get$1) => ({
956
1028
  addParticipant: (participant) => {
957
- set((state) => ({ participants: [...state.participants, participant] }));
1029
+ set((state) => ({ participants: state.participants.some((p) => p.pid === participant.pid) ? state.participants.map((p) => p.pid === participant.pid ? {
1030
+ ...p,
1031
+ ...participant
1032
+ } : p) : [...state.participants, participant] }));
958
1033
  },
959
1034
  addVirtualParticipant: (participant) => {
960
- set((state) => ({ virtualParticipants: [...state.virtualParticipants, participant] }));
1035
+ 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 ? {
1036
+ ...p,
1037
+ ...participant
1038
+ } : p) : [...state.virtualParticipants, participant] }));
961
1039
  },
962
1040
  clearParticipants: () => set({
963
1041
  participants: [],
@@ -1194,6 +1272,8 @@ const useConferenceStore = (0, zustand.create)()((0, zustand_middleware.subscrib
1194
1272
  sendParticipantEvent(EVENT_LISTENER_METHODS.ParticipantEventsListner.onParticipantHandRaised, participantId);
1195
1273
  },
1196
1274
  lowerHand: (participantId) => {
1275
+ const hasRaisedHand = useConferenceStore.getState().raiseHandMap.has(participantId);
1276
+ if (!hasRaisedHand) return;
1197
1277
  set((state) => {
1198
1278
  const raiseHandMap = new Map(state.raiseHandMap);
1199
1279
  raiseHandMap.delete(participantId);
@@ -1204,13 +1284,22 @@ const useConferenceStore = (0, zustand.create)()((0, zustand_middleware.subscrib
1204
1284
  leaveConference: async () => {
1205
1285
  const conference = useConferenceStore.getState().conference;
1206
1286
  if (conference) {
1207
- const { error } = await tryCatch(conference.leave());
1287
+ const { error } = await tryCatch(conference.leave(), 500);
1208
1288
  if (error) {
1209
1289
  console.warn("Error leaving conference:", error);
1210
1290
  eventBus.publish({ type: EVENT_LISTENER_METHODS.SessionStatusListener.onSessionLeft });
1211
1291
  }
1212
1292
  }
1213
1293
  },
1294
+ endConference: async () => {
1295
+ const conference = useConferenceStore.getState().conference;
1296
+ if (conference) {
1297
+ const { error } = await tryCatch(conference.end());
1298
+ if (error) {
1299
+ console.warn("Error ending conference:", error);
1300
+ }
1301
+ }
1302
+ },
1214
1303
  stopRecording: async () => {
1215
1304
  const conference = useConferenceStore.getState().conference;
1216
1305
  if (conference) {
@@ -1382,6 +1471,12 @@ const useTracksStore = (0, zustand.create)()((0, zustand_middleware.subscribeWit
1382
1471
  muted: originalTrack.isMuted() ? 1 : 0,
1383
1472
  originalTrack
1384
1473
  };
1474
+ const existingIdx = state.tracks.findIndex((t) => t.pid === participantId && t.mediaType === track.mediaType && t.local === isLocal);
1475
+ if (existingIdx !== -1) {
1476
+ const tracks = [...state.tracks];
1477
+ tracks[existingIdx] = track;
1478
+ return { tracks };
1479
+ }
1385
1480
  return { tracks: [...state.tracks, track] };
1386
1481
  }),
1387
1482
  removeTrack: (originalTrack) => set((state) => ({ tracks: state.tracks.filter((track) => track.originalTrack !== originalTrack) })),
@@ -1583,8 +1678,13 @@ useTracksStore.subscribe((state) => state.tracks.find((t) => t.mediaType === MED
1583
1678
  }
1584
1679
  if (track) {
1585
1680
  const deviceId = track.getDeviceId();
1586
- const device = useBaseStore.getState().audioInputDevices.find((d) => d.deviceId === deviceId);
1587
- updateAudioInputDeviceState(device, true);
1681
+ waitForStoreState(useBaseStore, (state) => state.audioInputDevices.length > 0).then(() => {
1682
+ const audioInputDevices = useBaseStore.getState().audioInputDevices;
1683
+ const device = audioInputDevices.find((d) => d.deviceId === deviceId);
1684
+ updateAudioInputDeviceState(device, true);
1685
+ }).catch(() => {
1686
+ updateAudioInputDeviceState(undefined, true);
1687
+ });
1588
1688
  }
1589
1689
  });
1590
1690
  useTracksStore.subscribe((state) => state.tracks.find((t) => t.mediaType === MEDIA_TYPE.VIDEO && t.local)?.originalTrack, (track, prevTrack) => {
@@ -1766,7 +1866,7 @@ const initialState$3 = {
1766
1866
  desktopSharingFrameRate: 5,
1767
1867
  chatButtonUnreadCount: 0,
1768
1868
  enableNoiseReduction: true,
1769
- sdkPlatform: PLATFORM.WEB,
1869
+ sdkPlatform: SDK_PLATFORM.WEB,
1770
1870
  webOSName: "unknown",
1771
1871
  isMobileBrowser: false,
1772
1872
  visibleParticipants: {
@@ -1790,11 +1890,11 @@ const useBaseStore = (0, zustand.create)()((0, zustand_middleware.subscribeWithS
1790
1890
  toggleParticipantListVisible: () => set((state) => ({ participantListVisible: !state.participantListVisible })),
1791
1891
  incrementConnectionRetryCount: () => set((state) => ({ connectionRetryCount: state.connectionRetryCount + 1 })),
1792
1892
  isMobileSDK: () => {
1793
- const isMobileSDK = get$1().sdkPlatform === "android" || get$1().sdkPlatform === "ios";
1893
+ const isMobileSDK = get$1().sdkPlatform !== "web";
1794
1894
  return isMobileSDK;
1795
1895
  },
1796
1896
  isMobile: () => {
1797
- const isMobileSDK = get$1().sdkPlatform === "android" || get$1().sdkPlatform === "ios";
1897
+ const isMobileSDK = get$1().sdkPlatform !== "web";
1798
1898
  const isMobileBrowser = get$1().isMobileBrowser;
1799
1899
  return isMobileSDK || isMobileBrowser;
1800
1900
  },
@@ -1840,6 +1940,7 @@ const useBaseStore = (0, zustand.create)()((0, zustand_middleware.subscribeWithS
1840
1940
  const toggleParticipantListVisible = useBaseStore.getState().toggleParticipantListVisible;
1841
1941
  const hideParticipantList = () => useBaseStore.setState({ participantListVisible: false });
1842
1942
  const showParticipantList = () => useBaseStore.setState({ participantListVisible: true });
1943
+ const toggleParticipantList = () => useBaseStore.setState((state) => ({ participantListVisible: !state.participantListVisible }));
1843
1944
  const toggleMoreMenuVisible = useBaseStore.getState().toggleMoreMenuVisible;
1844
1945
  const toggleAudioModeMenuVisible = () => {
1845
1946
  useBaseStore.setState((state) => ({ audioModeMenuVisible: !state.audioModeMenuVisible }));
@@ -1888,6 +1989,9 @@ const toggleEnableNoiseReduction = () => {
1888
1989
  const setChatButtonUnreadCount = (count) => {
1889
1990
  useBaseStore.setState({ chatButtonUnreadCount: count });
1890
1991
  };
1992
+ const setAudioMode = (mode) => {
1993
+ useBaseStore.setState({ selectedAudioModeType: mode });
1994
+ };
1891
1995
  const getLayout = () => {
1892
1996
  return useBaseStore.getState().layout;
1893
1997
  };
@@ -2054,12 +2158,34 @@ const useConnectionStore = (0, zustand.create)()((0, zustand_middleware.subscrib
2054
2158
  },
2055
2159
  reset: () => set(initialState$2)
2056
2160
  }))));
2161
+ function waitForConnection() {
2162
+ const { connectionStatus } = useConnectionStore.getState();
2163
+ if (connectionStatus === "connected") return Promise.resolve();
2164
+ return new Promise((resolve, reject) => {
2165
+ const timeout = setTimeout(() => {
2166
+ unsub();
2167
+ reject(new Error("Connection timed out after 3 seconds"));
2168
+ }, 3e3);
2169
+ const unsub = useConnectionStore.subscribe((s) => s.connectionStatus, (status) => {
2170
+ if (status === "connected") {
2171
+ clearTimeout(timeout);
2172
+ unsub();
2173
+ resolve();
2174
+ } else if (status === "error") {
2175
+ clearTimeout(timeout);
2176
+ unsub();
2177
+ reject(useConnectionStore.getState().error);
2178
+ }
2179
+ });
2180
+ });
2181
+ }
2057
2182
 
2058
2183
  //#endregion
2059
2184
  //#region calls-sdk-core/store/utils/hooks.ts
2060
2185
  const useHideMuteAudioButton = () => {
2061
2186
  const hideMuteAudioButton = useConfigStore((state) => state.hideToggleAudioButton);
2062
- return hideMuteAudioButton;
2187
+ const enableCompanionMode = useConfigStore((state) => state.enableCompanionMode);
2188
+ return hideMuteAudioButton || enableCompanionMode;
2063
2189
  };
2064
2190
  const useHideToggleVideoButton = () => {
2065
2191
  const hideToggleVideoButton = useConfigStore((state) => state.hideToggleVideoButton);
@@ -2126,8 +2252,21 @@ const getMainParticipant = () => {
2126
2252
  const useIsReconnecting = () => {
2127
2253
  const connectionStatus = useConnectionStore((state) => state.connectionStatus);
2128
2254
  const conferenceStatus = useConferenceStore((state) => state.conferenceStatus);
2129
- const reconnecting = connectionStatus === "connected" && conferenceStatus === "interrupted";
2130
- return reconnecting;
2255
+ const isP2P = useConferenceStore((state) => state.p2p);
2256
+ const [isOnline, setIsOnline] = (0, react.useState)(true);
2257
+ (0, react.useEffect)(() => {
2258
+ if (typeof window === "undefined") return;
2259
+ const controller = new AbortController();
2260
+ const { signal } = controller;
2261
+ window.addEventListener("online", () => setIsOnline(true), { signal });
2262
+ window.addEventListener("offline", () => setIsOnline(false), { signal });
2263
+ return () => controller.abort();
2264
+ }, []);
2265
+ const interrupted = connectionStatus === "connected" && conferenceStatus === "interrupted";
2266
+ if (isP2P && interrupted && isOnline) {
2267
+ return false;
2268
+ }
2269
+ return interrupted;
2131
2270
  };
2132
2271
  const useHideRecordingButton = () => {
2133
2272
  const hideRecordingButton = useConfigStore((state) => state.hideRecordingButton);
@@ -2205,6 +2344,11 @@ const useIsVideoInputSelectionSupported = () => {
2205
2344
  const isVideoInputSelectionSupported = hasVideoPermission && videoInputDevices.length > 0;
2206
2345
  return isVideoInputSelectionSupported;
2207
2346
  };
2347
+ const useShouldMirrorLocalVideo = () => {
2348
+ const mirrorLocalVideo = useBaseStore((state) => state.mirrorLocalVideo);
2349
+ const cameraFacing = useBaseStore((state) => state.cameraFacing);
2350
+ return cameraFacing === "user" && mirrorLocalVideo;
2351
+ };
2208
2352
 
2209
2353
  //#endregion
2210
2354
  //#region calls-sdk-core/store/utils/switch-camera.ts
@@ -2377,8 +2521,13 @@ var ConferenceListener = class {
2377
2521
  track.removeAllListeners(lib_jitsi_meet.default.events.track.NO_DATA_FROM_SOURCE);
2378
2522
  }
2379
2523
  onConferenceJoinInProgress() {}
2380
- onConferenceFailed(_conference, error, message) {
2381
- console.error("Conference failed:", error, message);
2524
+ onConferenceFailed(errorName, error, message) {
2525
+ console.log();
2526
+ if (errorName === lib_jitsi_meet.default.errors.conference.CONFERENCE_DESTROYED) {
2527
+ leaveSession({ forceLeave: true });
2528
+ return;
2529
+ }
2530
+ console.error("Conference failed:", errorName, error, message);
2382
2531
  useConferenceStore.setState({
2383
2532
  conferenceStatus: "error",
2384
2533
  conferenceJoined: false,
@@ -2424,17 +2573,19 @@ var ConferenceListener = class {
2424
2573
  useConferenceStore.setState({ p2p });
2425
2574
  }
2426
2575
  onTrackMuteChanged(track, participantThatMutedUs) {
2427
- if (participantThatMutedUs) {
2428
- useTracksStore.getState().updateTrack(track, { muted: track.isMuted() ? 1 : 0 });
2429
- const displayName = participantThatMutedUs.getDisplayName();
2430
- if (displayName) {
2431
- eventBus.publish({
2432
- type: INTERNAL_EVENTS.notification,
2433
- payload: {
2434
- type: "info",
2435
- message: `${displayName} has muted you.`
2436
- }
2437
- });
2576
+ if (track.isLocal()) {
2577
+ useTracksStore.getState().updateLocalTrack(track.getType(), { muted: track.isMuted() ? 1 : 0 });
2578
+ if (participantThatMutedUs) {
2579
+ const displayName = participantThatMutedUs.getDisplayName();
2580
+ if (displayName) {
2581
+ eventBus.publish({
2582
+ type: INTERNAL_EVENTS.notification,
2583
+ payload: {
2584
+ type: "info",
2585
+ message: `${displayName} has muted you.`
2586
+ }
2587
+ });
2588
+ }
2438
2589
  }
2439
2590
  }
2440
2591
  }
@@ -2489,6 +2640,24 @@ var ConferenceListener = class {
2489
2640
  useParticipantStore.getState().updateParticipant(participantId, { role: newRole });
2490
2641
  }
2491
2642
  }
2643
+ onTrackUnmuteRejected(track) {
2644
+ if (!track.isLocal()) {
2645
+ return;
2646
+ }
2647
+ const mediaType = track.getType();
2648
+ track.dispose().catch(() => {});
2649
+ useTracksStore.getState().updateLocalTrack(mediaType, {
2650
+ originalTrack: undefined,
2651
+ muted: 1
2652
+ });
2653
+ eventBus.publish({
2654
+ type: INTERNAL_EVENTS.notification,
2655
+ payload: {
2656
+ type: "info",
2657
+ message: `Your ${mediaType} unmute was rejected.`
2658
+ }
2659
+ });
2660
+ }
2492
2661
  onTalkWhileMuted() {}
2493
2662
  onConferenceError(error) {
2494
2663
  console.error("Conference error:", error);
@@ -2567,6 +2736,7 @@ function addConferenceListeners(conference) {
2567
2736
  conference.on(lib_jitsi_meet.default.events.conference.PARTICIPANT_PROPERTY_CHANGED, conferenceListener.onParticipantPropertyChanged);
2568
2737
  conference.on(lib_jitsi_meet.default.events.conference.USER_ROLE_CHANGED, conferenceListener.onUserRoleChanged);
2569
2738
  conference.on(lib_jitsi_meet.default.events.conference.TALK_WHILE_MUTED, conferenceListener.onTalkWhileMuted);
2739
+ conference.on(lib_jitsi_meet.default.events.conference.TRACK_UNMUTE_REJECTED, conferenceListener.onTrackUnmuteRejected);
2570
2740
  conference.on(lib_jitsi_meet.default.events.conference.TRACK_AUDIO_LEVEL_CHANGED, conferenceListener.onTrackAudioLevelChanged);
2571
2741
  conference.addCommandListener(CONFERENCE_COMMANDS.userInfo, (data, id) => {
2572
2742
  const vData = valibot.safeParse(UserInfoCommandSchema, safeParseJson(data.value));
@@ -2578,17 +2748,23 @@ function addConferenceListeners(conference) {
2578
2748
  }
2579
2749
  });
2580
2750
  }
2581
- async function createConference(connection, roomName) {
2751
+ async function _createConference() {
2752
+ const sessionId = useConfigStore.getState().sessionId;
2753
+ const connection = useConnectionStore.getState().connection;
2582
2754
  if (!connection) {
2583
2755
  throw new Error("No connection available");
2584
2756
  }
2757
+ const connectionStatus = useConnectionStore.getState().connectionStatus;
2758
+ if (connectionStatus !== "connected") {
2759
+ await waitForConnection();
2760
+ }
2585
2761
  const existingConference = useConferenceStore.getState().conference;
2586
2762
  if (existingConference) {
2587
2763
  console.log("Conference already exists, skipping creation");
2588
2764
  return;
2589
2765
  }
2590
2766
  const connectionConfig = useConnectionStore.getState().connectionConfig;
2591
- const conference = connection.initJitsiConference(roomName, connectionConfig);
2767
+ const conference = connection.initJitsiConference(sessionId, connectionConfig);
2592
2768
  const localAudioTrack = getLocalTrack(MEDIA_TYPE.AUDIO)?.originalTrack;
2593
2769
  const localVideoTrack = getLocalTrack(MEDIA_TYPE.VIDEO)?.originalTrack;
2594
2770
  if (localAudioTrack) {
@@ -2603,6 +2779,20 @@ async function createConference(connection, roomName) {
2603
2779
  conference.setDisplayName(useParticipantStore.getState().localParticipant.name);
2604
2780
  conference.join();
2605
2781
  }
2782
+ async function createConference() {
2783
+ const conference = useConferenceStore.getState().conference;
2784
+ if (!conference) {
2785
+ const result = await tryCatch(_createConference());
2786
+ if (result.error) {
2787
+ console.error("Error creating conference", result.error);
2788
+ useConferenceStore.setState({
2789
+ conferenceStatus: "error",
2790
+ conferenceJoined: false,
2791
+ conferenceError: result.error.message
2792
+ });
2793
+ }
2794
+ }
2795
+ }
2606
2796
  function muteParticipant(participantId) {
2607
2797
  const conference = useConferenceStore.getState().conference;
2608
2798
  conference?.muteParticipant(participantId, "audio");
@@ -2614,7 +2804,12 @@ function pauseParticipantVideo(participantId) {
2614
2804
 
2615
2805
  //#endregion
2616
2806
  //#region calls-sdk-core/handlers/connection.ts
2617
- function connect(roomName) {
2807
+ async function connect(autoJoinConference = true) {
2808
+ const existingConnection = useConnectionStore.getState().connection;
2809
+ if (existingConnection) {
2810
+ createConference();
2811
+ return;
2812
+ }
2618
2813
  const options = useConnectionStore.getState().connectionConfig;
2619
2814
  const jwt = useConnectionStore.getState().jwt;
2620
2815
  const iAmRecorder = useConfigStore.getState().iAmRecorder;
@@ -2630,15 +2825,8 @@ function connect(roomName) {
2630
2825
  async function onConnectionEstablished() {
2631
2826
  useConnectionStore.getState().connectionEstablished(connection);
2632
2827
  eventBus.publish({ type: INTERNAL_EVENTS.onConnectionEstablished });
2633
- const result = await tryCatch(createConference(connection, roomName));
2634
- if (result.error) {
2635
- console.error("Error creating conference", result.error);
2636
- useConferenceStore.setState({
2637
- conferenceStatus: "error",
2638
- conferenceJoined: false,
2639
- conferenceError: result.error.message
2640
- });
2641
- }
2828
+ if (!autoJoinConference) return;
2829
+ createConference();
2642
2830
  }
2643
2831
  function onConnectionFailed(err, message, ...args) {
2644
2832
  unsubscribe();
@@ -2836,6 +3024,7 @@ var Mutex = class {
2836
3024
  //#region calls-sdk-core/handlers/init.ts
2837
3025
  function initializeLib() {
2838
3026
  lib_jitsi_meet.default.init();
3027
+ lib_jitsi_meet.default.setLogLevel(lib_jitsi_meet.default.logLevels.ERROR);
2839
3028
  console.log("JitsiMeetJS initialized successfully.");
2840
3029
  }
2841
3030
 
@@ -2844,6 +3033,7 @@ function initializeLib() {
2844
3033
  let isSessionStarted = false;
2845
3034
  let reconnectTimeoutId = null;
2846
3035
  const RECONNECT_DEBOUNCE_DELAY = 3e3;
3036
+ initializeLib();
2847
3037
  function startSession() {
2848
3038
  const sessionId = useConfigStore.getState().sessionId;
2849
3039
  if (!sessionId) {
@@ -2856,29 +3046,13 @@ function startSession() {
2856
3046
  }
2857
3047
  isSessionStarted = true;
2858
3048
  console.log(`Session started in room: ${sessionId}`);
2859
- initializeLib();
2860
- const audioInputDeviceId = useConfigStore.getState().audioInputDeviceId ?? useBaseStore.getState().audioInputDevice?.deviceId;
2861
- createLocalTrack("audio", audioInputDeviceId);
2862
- const sessionType = useConfigStore.getState().sessionType;
2863
- if (sessionType === SESSION_TYPE.VIDEO) {
2864
- const videoInputDeviceIdP1 = useConfigStore.getState().videoInputDeviceId;
2865
- const videoInputDeviceIdP2 = useBaseStore.getState().videoInputDevice?.deviceId;
2866
- const initialCameraFacingP1 = useConfigStore.getState().initialCameraFacing;
2867
- const initialCameraFacingP2 = useBaseStore.getState().cameraFacing;
2868
- if (videoInputDeviceIdP1) {
2869
- createLocalTrack("video", videoInputDeviceIdP1);
2870
- } else if (initialCameraFacingP1) {
2871
- createLocalTrack("video", null, initialCameraFacingP2);
2872
- } else {
2873
- createLocalTrack("video", videoInputDeviceIdP2, initialCameraFacingP2);
2874
- }
2875
- }
3049
+ createLocalTracks();
2876
3050
  const audioOutputDeviceId = useConfigStore.getState().audioOutputDeviceId ?? useBaseStore.getState().audioOutputDevice?.deviceId;
2877
3051
  if (audioOutputDeviceId) {
2878
3052
  updateAudioOutputDevice(audioOutputDeviceId);
2879
3053
  }
2880
3054
  eventBus.startEmitting();
2881
- const test = tryCatchSync(() => connect(sessionId));
3055
+ const test = tryCatchSync(() => connect());
2882
3056
  if (test.error) {
2883
3057
  console.error("Error connecting to session:", test.error);
2884
3058
  useConnectionStore.getState().connectionFailed(test.error.message);
@@ -2895,8 +3069,14 @@ async function _leaveSession() {
2895
3069
  await useConnectionStore.getState().disconnect();
2896
3070
  }
2897
3071
  const sessionMutex = new Mutex();
2898
- function leaveSession() {
3072
+ function leaveSession(options = {}) {
2899
3073
  return sessionMutex.run(async () => {
3074
+ const isPeerCall = useConfigStore.getState().isPeerCall;
3075
+ const shouldEnd = isPeerCall && !options.forceLeave;
3076
+ if (shouldEnd) {
3077
+ useConferenceStore.getState().endConference();
3078
+ return;
3079
+ }
2900
3080
  useBaseStore.getState().clearIdealTimeoutTimer();
2901
3081
  cancelPendingReconnect();
2902
3082
  await _leaveSession();
@@ -3993,10 +4173,16 @@ const PopupMenu = ({ visible, onClose, options, anchorLayout }) => {
3993
4173
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Modal, {
3994
4174
  transparent: true,
3995
4175
  animationType: "none",
4176
+ supportedOrientations: [
4177
+ "portrait",
4178
+ "landscape-left",
4179
+ "landscape-right"
4180
+ ],
3996
4181
  onRequestClose: onClose,
3997
4182
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Pressable, {
3998
4183
  style: styles$27.backdrop,
3999
4184
  onPress: onClose,
4185
+ testID: "cometchat-popup-menu-backdrop",
4000
4186
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Animated.View, {
4001
4187
  style: [styles$27.menu, {
4002
4188
  [isBelowMiddle ? "bottom" : "top"]: isBelowMiddle ? callContainerDimension.height - (y - 4) : y + height + 4,
@@ -4017,6 +4203,7 @@ const PopupMenu = ({ visible, onClose, options, anchorLayout }) => {
4017
4203
  },
4018
4204
  activeOpacity: option.selected ? DISABLED_OPTION_OPACITY : .2,
4019
4205
  style: [styles$27.menuItem, option.selected ? styles$27.menuItemSelected : {}],
4206
+ testID: `cometchat-popup-menu-option-${index}`,
4020
4207
  children: [option.iconName && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon_native_default, {
4021
4208
  name: option.iconName,
4022
4209
  size: 24,
@@ -4178,6 +4365,7 @@ const MoreOptionButton = ({ ruid }) => {
4178
4365
  ref: buttonRef,
4179
4366
  style: styles$4.moreButton,
4180
4367
  onPress: showMenu,
4368
+ testID: "cometchat-participant-more-options-button",
4181
4369
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon_native_default, {
4182
4370
  name: "more",
4183
4371
  size: 20,
@@ -4347,6 +4535,7 @@ const Tile = ({ participant, style, zOrder, showLabel = true, disablePress = fal
4347
4535
  const videoTrack = useTrackByParticipantId(pid, type === "screen-share" ? MEDIA_TYPE.SCREENSHARE : MEDIA_TYPE.VIDEO)?.originalTrack;
4348
4536
  const videoMuted = useTrackMuted(type === "screen-share" ? MEDIA_TYPE.SCREENSHARE : MEDIA_TYPE.VIDEO, pid);
4349
4537
  const enableParticipantContextMenu = useEnableParticipantContextMenu();
4538
+ const shouldMirror = useShouldMirrorLocalVideo();
4350
4539
  const [size, fontSize] = react.default.useMemo(() => {
4351
4540
  const flatStyle = react_native.StyleSheet.flatten(style);
4352
4541
  const width$1 = flatStyle?.width;
@@ -4364,6 +4553,7 @@ const Tile = ({ participant, style, zOrder, showLabel = true, disablePress = fal
4364
4553
  activeOpacity: 1,
4365
4554
  disabled: disablePress,
4366
4555
  style: [styles$24.callScreen, style],
4556
+ testID: `cometchat-tile-${pid}`,
4367
4557
  children: [
4368
4558
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
4369
4559
  style: styles$24.tileAvatar,
@@ -4379,7 +4569,7 @@ const Tile = ({ participant, style, zOrder, showLabel = true, disablePress = fal
4379
4569
  objectFit: type === "screen-share" ? "contain" : "cover",
4380
4570
  muted: videoMuted,
4381
4571
  zOrder,
4382
- mirror: isLocal
4572
+ mirror: isLocal && type !== "screen-share" && shouldMirror
4383
4573
  }),
4384
4574
  showLabel && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label_native_default, { participant }),
4385
4575
  enableParticipantContextMenu && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
@@ -4438,6 +4628,7 @@ const GroupAvatarTile = ({ startIndex = 4, style }) => {
4438
4628
  const overflowCount = Math.max(0, participantCount - startIndex - 3);
4439
4629
  const visible = participants.slice(startIndex, startIndex + (overflowCount === 1 ? 4 : 3));
4440
4630
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.TouchableOpacity, {
4631
+ testID: "cometchat-group-avatar-tile",
4441
4632
  style: [styles$23.container, style],
4442
4633
  onPress: toggleParticipantListVisible,
4443
4634
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
@@ -4545,6 +4736,7 @@ function SidebarLayout() {
4545
4736
  const mainParticipant = useMainParticipant();
4546
4737
  const participants = allParticipants.length > 1 ? [mainParticipant].concat(allParticipants) : [mainParticipant];
4547
4738
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.View, {
4739
+ testID: "cometchat-sidebar-layout",
4548
4740
  style: styles$22.container,
4549
4741
  children: [
4550
4742
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tile_native_default, {
@@ -4681,23 +4873,26 @@ const Spotlight = () => {
4681
4873
  if (otherParticipant) {
4682
4874
  spotlightParticipants.push(otherParticipant);
4683
4875
  }
4684
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tile_native_default, {
4685
- participant: spotlightParticipants[0],
4686
- disablePress: true
4687
- }, spotlightParticipants[0].ruid), spotlightParticipants[1] && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Pan, {
4688
- disableDrag: !enableSpotlightDrag,
4689
- layout: {
4690
- width: mainAreaDimension.width,
4691
- height: mainAreaDimension.height
4692
- },
4693
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tile_native_default, {
4694
- showLabel: false,
4695
- disablePress: !enableSpotlightSwap,
4696
- participant: spotlightParticipants[1],
4697
- style: styles$20.panTile,
4698
- zOrder: 1
4699
- }, spotlightParticipants[1].ruid)
4700
- })] });
4876
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.View, {
4877
+ testID: "cometchat-spotlight-layout",
4878
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tile_native_default, {
4879
+ participant: spotlightParticipants[0],
4880
+ disablePress: true
4881
+ }, spotlightParticipants[0].ruid), spotlightParticipants[1] && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Pan, {
4882
+ disableDrag: !enableSpotlightDrag,
4883
+ layout: {
4884
+ width: mainAreaDimension.width,
4885
+ height: mainAreaDimension.height
4886
+ },
4887
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tile_native_default, {
4888
+ showLabel: false,
4889
+ disablePress: !enableSpotlightSwap,
4890
+ participant: spotlightParticipants[1],
4891
+ style: styles$20.panTile,
4892
+ zOrder: 1
4893
+ }, spotlightParticipants[1].ruid)
4894
+ })]
4895
+ });
4701
4896
  };
4702
4897
  const styles$20 = react_native.StyleSheet.create({ panTile: {
4703
4898
  borderColor: "#1A1A1A",
@@ -4730,6 +4925,7 @@ function TileLayout() {
4730
4925
  participantCount: isPIPLayoutEnabled ? 1 : participants.length
4731
4926
  });
4732
4927
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.FlatList, {
4928
+ testID: "cometchat-tile-layout",
4733
4929
  data: isPIPLayoutEnabled ? [mainParticipant] : participants,
4734
4930
  renderItem: ({ item }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tile_native_default, {
4735
4931
  participant: item,
@@ -5079,10 +5275,16 @@ function ConfirmationDialog() {
5079
5275
  visible,
5080
5276
  transparent: true,
5081
5277
  animationType: "fade",
5278
+ supportedOrientations: [
5279
+ "portrait",
5280
+ "landscape-left",
5281
+ "landscape-right"
5282
+ ],
5082
5283
  onRequestClose: handleBackdropPress,
5083
5284
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Pressable, {
5084
5285
  style: styles$15.backdrop,
5085
5286
  onPress: handleBackdropPress,
5287
+ testID: "cometchat-confirmation-dialog-backdrop",
5086
5288
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
5087
5289
  style: styles$15.dialog,
5088
5290
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.View, {
@@ -5125,7 +5327,7 @@ function ConfirmationDialog() {
5125
5327
  //#endregion
5126
5328
  //#region src/ui/bottom-sheet/BottomSheet.native.tsx
5127
5329
  const SCREEN_HEIGHT = react_native.Dimensions.get("window").height;
5128
- const BottomSheet = ({ children, isVisible, onClose, maxHeight = SCREEN_HEIGHT * .4 }) => {
5330
+ const BottomSheet = ({ children, isVisible, onClose, maxHeight = SCREEN_HEIGHT * .4, testID }) => {
5129
5331
  const visibleTranslateY = SCREEN_HEIGHT - maxHeight;
5130
5332
  const hiddenTranslateY = (0, react.useRef)(SCREEN_HEIGHT).current;
5131
5333
  const animatedValue = (0, react.useRef)(new react_native.Animated.Value(hiddenTranslateY)).current;
@@ -5151,8 +5353,10 @@ const BottomSheet = ({ children, isVisible, onClose, maxHeight = SCREEN_HEIGHT *
5151
5353
  const bottomSheetAnimation = { transform: [{ translateY: animatedValue }] };
5152
5354
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [isVisible && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.TouchableWithoutFeedback, {
5153
5355
  onPress: onClose,
5356
+ testID: "cometchat-bottom-sheet-backdrop",
5154
5357
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, { style: styles$14.backdrop })
5155
5358
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.Animated.View, {
5359
+ testID,
5156
5360
  style: [
5157
5361
  styles$14.bottomSheet,
5158
5362
  bottomSheetAnimation,
@@ -5210,8 +5414,9 @@ var BottomSheet_native_default = BottomSheet;
5210
5414
 
5211
5415
  //#endregion
5212
5416
  //#region src/ui/control-pane/MenuItem.native.tsx
5213
- const MenuItem = ({ iconName, label, onPress, selected = false }) => {
5417
+ const MenuItem = ({ iconName, label, onPress, selected = false, testID }) => {
5214
5418
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.TouchableOpacity, {
5419
+ testID,
5215
5420
  onPress: () => {
5216
5421
  hideAllBottomSheets();
5217
5422
  onPress();
@@ -5264,6 +5469,7 @@ const AudioModesMenu = ({ isVisible, onClose }) => {
5264
5469
  style: [commonStyles.bodyRegular, styles$12.noItemsText],
5265
5470
  children: "No audio modes available"
5266
5471
  }), audioModes.map((mode, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MenuItem_native_default, {
5472
+ testID: `cometchat-menu-item-audio-${mode.type.toLowerCase()}`,
5267
5473
  iconName: AUDIO_MODE_TYPE_ICON_MAP[mode.type],
5268
5474
  label: mode.type,
5269
5475
  selected: mode.selected,
@@ -5315,6 +5521,7 @@ const AudioModeButton = () => {
5315
5521
  style: controlPaneStyles.controlButton,
5316
5522
  onPress: toggleAudioModeMenuVisible,
5317
5523
  activeOpacity: .7,
5524
+ testID: "cometchat-audio-mode-button",
5318
5525
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon_native_default, {
5319
5526
  name: "speaker-fill",
5320
5527
  fill: "#FFF",
@@ -5340,6 +5547,7 @@ const AudioControl = () => {
5340
5547
  style: [controlPaneStyles.controlButton, muted && controlPaneStyles.toggledButton],
5341
5548
  onPress,
5342
5549
  activeOpacity: .7,
5550
+ testID: "cometchat-audio-toggle-button",
5343
5551
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon_native_default, {
5344
5552
  name: muted ? "mic-off-fill" : "mic-fill",
5345
5553
  fill: muted ? "#9F3032" : "#FFF",
@@ -5365,6 +5573,7 @@ const VideoControl = () => {
5365
5573
  style: [controlPaneStyles.controlButton, videoMuted && controlPaneStyles.toggledButton],
5366
5574
  onPress,
5367
5575
  activeOpacity: .7,
5576
+ testID: "cometchat-video-toggle-button",
5368
5577
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon_native_default, {
5369
5578
  name: videoMuted ? "video-off-fill" : "video-fill",
5370
5579
  fill: videoMuted ? "#9F3032" : "#FFF",
@@ -5389,6 +5598,7 @@ const LeaveSessionButton = () => {
5389
5598
  style: [controlPaneStyles.controlButton, controlPaneStyles.leaveSessionButton],
5390
5599
  onPress,
5391
5600
  activeOpacity: .7,
5601
+ testID: "cometchat-leave-session-button",
5392
5602
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon_native_default, {
5393
5603
  name: "call-end",
5394
5604
  fill: "#FFF",
@@ -5405,6 +5615,7 @@ const MoreMenuButton = () => {
5405
5615
  style: controlPaneStyles.controlButton,
5406
5616
  onPress: toggleMoreMenuVisible,
5407
5617
  activeOpacity: .7,
5618
+ testID: "cometchat-more-menu-button",
5408
5619
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon_native_default, {
5409
5620
  name: "more",
5410
5621
  fill: "#FFF",
@@ -5480,22 +5691,26 @@ const MoreMenu = ({ isVisible, onClose }) => {
5480
5691
  toggleParticipantListVisible();
5481
5692
  }, []);
5482
5693
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BottomSheet_native_default, {
5694
+ testID: "cometchat-more-menu-bottom-sheet",
5483
5695
  maxHeight: bottomSheetMaxHeight,
5484
5696
  isVisible,
5485
5697
  onClose,
5486
5698
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.ScrollView, { children: [
5487
5699
  numberOfVisibleItems === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
5700
+ testID: "cometchat-more-menu-empty-state",
5488
5701
  style: [commonStyles.bodyRegular, styles$11.noItemsText],
5489
5702
  children: "No options available"
5490
5703
  }),
5491
5704
  !hideScreenSharingButton && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScreenShareButton_default, {}),
5492
5705
  !hideRaiseHandButton && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MenuItem_native_default, {
5706
+ testID: "cometchat-menu-item-raise-hand",
5493
5707
  iconName: "raise-hand-fill",
5494
5708
  label: raiseHandTimestamp ? "Lower Hand" : "Raise Hand",
5495
5709
  onPress: onRaiseHandPress,
5496
5710
  selected: Boolean(raiseHandTimestamp)
5497
5711
  }),
5498
5712
  !hideRecordingButton && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MenuItem_native_default, {
5713
+ testID: isRecording ? "cometchat-menu-item-stop-recording" : "cometchat-menu-item-start-recording",
5499
5714
  iconName: isRecording ? "record-stop-fill" : "record-fill",
5500
5715
  label: isRecording ? "Stop Recording" : "Start Recording",
5501
5716
  onPress: () => {
@@ -5506,6 +5721,7 @@ const MoreMenu = ({ isVisible, onClose }) => {
5506
5721
  }
5507
5722
  }),
5508
5723
  !hideParticipantListButton && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MenuItem_native_default, {
5724
+ testID: "cometchat-menu-item-participants",
5509
5725
  iconName: "participants",
5510
5726
  label: "Participants",
5511
5727
  onPress: onParticipantListPress
@@ -5632,6 +5848,7 @@ const ChangeLayout = () => {
5632
5848
  eventBus.publish({ type: EVENT_LISTENER_METHODS.ButtonClickListener.onChangeLayoutButtonClicked });
5633
5849
  showMenu();
5634
5850
  },
5851
+ testID: "cometchat-change-layout-button",
5635
5852
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon_native_default, {
5636
5853
  name: "tile-fill",
5637
5854
  fill: "#FFFFFF"
@@ -5684,6 +5901,7 @@ const ChatButton = () => {
5684
5901
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.TouchableOpacity, {
5685
5902
  style: [styles$9.iconButton],
5686
5903
  onPress,
5904
+ testID: "cometchat-chat-button",
5687
5905
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon_native_default, {
5688
5906
  name: "chat",
5689
5907
  fill: "#FFFFFF"
@@ -5740,6 +5958,7 @@ const SwitchCamera = () => {
5740
5958
  return null;
5741
5959
  }
5742
5960
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.TouchableOpacity, {
5961
+ testID: "cometchat-switch-camera-button",
5743
5962
  disabled,
5744
5963
  style: [styles$8.iconButton, disabled && styles$8.iconButtonDisabled],
5745
5964
  onPress,
@@ -5772,6 +5991,7 @@ const SessionTimer = () => {
5772
5991
  return null;
5773
5992
  }
5774
5993
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
5994
+ testID: "cometchat-session-timer",
5775
5995
  style: [commonStyles.caption1Regular, styles$7.meetingTime],
5776
5996
  children: miliSecondsToMMSS(conferenceElapsedTime)
5777
5997
  });
@@ -5883,69 +6103,80 @@ const IdealTimeoutModal = ({ style = {} }) => {
5883
6103
  transparent: true,
5884
6104
  visible: idleTimeoutModalVisible,
5885
6105
  animationType: "none",
6106
+ supportedOrientations: [
6107
+ "portrait",
6108
+ "landscape-left",
6109
+ "landscape-right"
6110
+ ],
5886
6111
  statusBarTranslucent: true,
5887
6112
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.TouchableWithoutFeedback, {
5888
6113
  onPress: handleOverlayPress,
6114
+ testID: "cometchat-idle-timeout-overlay",
5889
6115
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Animated.View, {
5890
6116
  style: [styles$6.overlay, { opacity: fadeAnim }],
5891
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.TouchableWithoutFeedback, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Animated.View, {
5892
- style: [
5893
- styles$6.modal,
5894
- style,
5895
- {
5896
- opacity: fadeAnim,
5897
- transform: [{ scale: scaleAnim }]
5898
- }
5899
- ],
5900
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.View, {
5901
- style: styles$6.content,
5902
- children: [
5903
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
5904
- style: styles$6.timerIcon,
5905
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
5906
- style: [commonStyles.heading3Bold, styles$6.timerText],
5907
- children: formattedTime
5908
- })
5909
- }),
5910
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.View, {
5911
- style: styles$6.textContent,
5912
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
5913
- style: [commonStyles.heading2Medium, styles$6.title],
5914
- children: "Are you still there?"
5915
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.Text, {
5916
- style: [commonStyles.bodyRegular, styles$6.subtitle],
5917
- children: [
5918
- "You are the only one here, so this call will end in less than ",
5919
- ceilMinutes,
5920
- " minute",
5921
- ceilMinutes > 1 ? "s" : "",
5922
- ". Do you want to stay in this call?"
5923
- ]
5924
- })]
5925
- }),
5926
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.View, {
5927
- style: styles$6.actions,
5928
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.TouchableOpacity, {
5929
- style: [styles$6.button, styles$6.buttonSecondary],
5930
- onPress: onStayInCall,
5931
- activeOpacity: .8,
6117
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.TouchableWithoutFeedback, {
6118
+ testID: "cometchat-idle-timeout-modal",
6119
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Animated.View, {
6120
+ style: [
6121
+ styles$6.modal,
6122
+ style,
6123
+ {
6124
+ opacity: fadeAnim,
6125
+ transform: [{ scale: scaleAnim }]
6126
+ }
6127
+ ],
6128
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.View, {
6129
+ style: styles$6.content,
6130
+ children: [
6131
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
6132
+ style: styles$6.timerIcon,
5932
6133
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
5933
- style: [commonStyles.bodyMedium, styles$6.buttonSecondaryText],
5934
- children: "Stay on the call"
6134
+ style: [commonStyles.heading3Bold, styles$6.timerText],
6135
+ children: formattedTime
5935
6136
  })
5936
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.TouchableOpacity, {
5937
- style: [styles$6.button, styles$6.buttonPrimary],
5938
- onPress: leaveSession,
5939
- activeOpacity: .8,
5940
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
5941
- style: [commonStyles.bodyMedium, styles$6.buttonPrimaryText],
5942
- children: "Leave now"
5943
- })
5944
- })]
5945
- })
5946
- ]
6137
+ }),
6138
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.View, {
6139
+ style: styles$6.textContent,
6140
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
6141
+ style: [commonStyles.heading2Medium, styles$6.title],
6142
+ children: "Are you still there?"
6143
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.Text, {
6144
+ style: [commonStyles.bodyRegular, styles$6.subtitle],
6145
+ children: [
6146
+ "You are the only one here, so this call will end in less than ",
6147
+ ceilMinutes,
6148
+ " minute",
6149
+ ceilMinutes > 1 ? "s" : "",
6150
+ ". Do you want to stay in this call?"
6151
+ ]
6152
+ })]
6153
+ }),
6154
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.View, {
6155
+ style: styles$6.actions,
6156
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.TouchableOpacity, {
6157
+ style: [styles$6.button, styles$6.buttonSecondary],
6158
+ onPress: onStayInCall,
6159
+ activeOpacity: .8,
6160
+ testID: "cometchat-idle-timeout-stay-button",
6161
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
6162
+ style: [commonStyles.bodyMedium, styles$6.buttonSecondaryText],
6163
+ children: "Stay on the call"
6164
+ })
6165
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.TouchableOpacity, {
6166
+ style: [styles$6.button, styles$6.buttonPrimary],
6167
+ onPress: () => leaveSession(),
6168
+ activeOpacity: .8,
6169
+ testID: "cometchat-idle-timeout-leave-button",
6170
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
6171
+ style: [commonStyles.bodyMedium, styles$6.buttonPrimaryText],
6172
+ children: "Leave now"
6173
+ })
6174
+ })]
6175
+ })
6176
+ ]
6177
+ })
5947
6178
  })
5948
- }) })
6179
+ })
5949
6180
  })
5950
6181
  })
5951
6182
  });
@@ -5964,6 +6195,7 @@ const styles$6 = react_native.StyleSheet.create({
5964
6195
  borderWidth: 1,
5965
6196
  borderColor: "#383838",
5966
6197
  width: "100%",
6198
+ maxWidth: 372,
5967
6199
  paddingTop: 32,
5968
6200
  paddingHorizontal: 20,
5969
6201
  paddingBottom: 20,
@@ -6042,6 +6274,7 @@ const ShareInviteButton = () => {
6042
6274
  style: styles$5.shareButtonContainer,
6043
6275
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.TouchableOpacity, {
6044
6276
  style: styles$5.shareButton,
6277
+ testID: "cometchat-share-invite-button",
6045
6278
  onPress: () => {
6046
6279
  eventBus.publish({ type: "onShareInviteButtonClicked" });
6047
6280
  },
@@ -6178,6 +6411,7 @@ const ParticipantList = () => {
6178
6411
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.TouchableOpacity, {
6179
6412
  onPress: toggleParticipantListVisible,
6180
6413
  accessibilityLabel: "Close participants list",
6414
+ testID: "cometchat-participant-list-close-button",
6181
6415
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon_native_default, {
6182
6416
  name: "close",
6183
6417
  size: 24,
@@ -6199,7 +6433,8 @@ const ParticipantList = () => {
6199
6433
  style: styles$3.searchInput,
6200
6434
  value: searchTerm,
6201
6435
  onChangeText: setSearchTerm,
6202
- placeholderTextColor: "#858585"
6436
+ placeholderTextColor: "#858585",
6437
+ testID: "cometchat-participant-search-input"
6203
6438
  })]
6204
6439
  })
6205
6440
  }),
@@ -6453,6 +6688,7 @@ function ToastItemView({ toast, onDismiss }) {
6453
6688
  }), toast.action && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Pressable, {
6454
6689
  style: styles$2.actionButton,
6455
6690
  onPress: handleActionPress,
6691
+ testID: "cometchat-toast-action-button",
6456
6692
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
6457
6693
  style: styles$2.actionText,
6458
6694
  children: toast.action.label
@@ -6505,13 +6741,17 @@ function CallUI(props) {
6505
6741
  const isConferenceJoined = useIsConferenceJoined();
6506
6742
  (0, react.useLayoutEffect)(() => {
6507
6743
  eventBus.publish({ type: INTERNAL_EVENTS.lifecycle.componentDidMount });
6508
- updateConfig(props.callSettings);
6744
+ updateConfig(props.sessionSettings);
6509
6745
  return () => {
6510
6746
  eventBus.publish({ type: INTERNAL_EVENTS.lifecycle.componentWillUnmount }, true);
6511
6747
  };
6512
- }, [props.callSettings]);
6748
+ }, [props.sessionSettings]);
6513
6749
  (0, react.useEffect)(() => {
6514
- useBaseStore.setState({ sdkPlatform: react_native.Platform.OS });
6750
+ if (props.sessionSettings.sdkPlatform) {
6751
+ useBaseStore.setState({ sdkPlatform: props.sessionSettings.sdkPlatform });
6752
+ } else {
6753
+ useBaseStore.setState({ sdkPlatform: react_native.Platform.OS === "ios" ? "react-native-ios" : "react-native-android" });
6754
+ }
6515
6755
  startSession();
6516
6756
  }, []);
6517
6757
  (0, react.useEffect)(() => {
@@ -6526,13 +6766,13 @@ function CallUI(props) {
6526
6766
  (0, react.useEffect)(() => {
6527
6767
  if (react_native.Platform.OS === "android") {
6528
6768
  AudioModeModule_default.setMode(type === SESSION_TYPE.VOICE ? AudioModeModule_default.AUDIO_CALL : AudioModeModule_default.VIDEO_CALL);
6529
- if (props.callSettings.audioMode) {
6530
- AudioModeModule_default.setAudioDevice(props.callSettings.audioMode);
6769
+ if (props.sessionSettings.audioMode) {
6770
+ AudioModeModule_default.setAudioDevice(props.sessionSettings.audioMode);
6531
6771
  }
6532
6772
  } else if (react_native.Platform.OS === "ios") {
6533
6773
  AudioModeModule_default.updateDeviceList();
6534
6774
  }
6535
- }, [props.callSettings.audioMode, type]);
6775
+ }, [props.sessionSettings.audioMode, type]);
6536
6776
  if (isPIPLayoutEnabled) {
6537
6777
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PiPTile_default, {});
6538
6778
  }
@@ -6661,10 +6901,10 @@ const convertLegacyCallSettingsToV5Props = (callSettings) => {
6661
6901
  if (cs.defaultAudioMode === "BLUETOOTH" || cs.defaultAudioMode === "EARPIECE" || cs.defaultAudioMode === "HEADPHONES" || cs.defaultAudioMode === "SPEAKER") {
6662
6902
  v5Props.audioMode = cs.defaultAudioMode;
6663
6903
  }
6664
- if (cs.mode === "SPOTLIGHT") {
6665
- v5Props.layout = "SPOTLIGHT";
6666
- } else {
6667
- v5Props.layout = "SIDEBAR";
6904
+ if (typeof cs.layout === "string") {
6905
+ v5Props.layout = cs.layout;
6906
+ } else if (cs.mode === "SPOTLIGHT" || cs.mode === "SIDEBAR") {
6907
+ v5Props.layout = cs.mode;
6668
6908
  }
6669
6909
  if (cs.idleTimeoutPeriod) {
6670
6910
  v5Props.idleTimeoutPeriodAfterPrompt = 6e4;
@@ -6824,8 +7064,8 @@ async function callVerifyTokenAPI({ appId, region, calltoken, baseURL }) {
6824
7064
  }
6825
7065
 
6826
7066
  //#endregion
6827
- //#region src/AppRN.tsx
6828
- function App(props) {
7067
+ //#region src/AppReactNativeSDK.tsx
7068
+ function AppReactNativeSDK(props) {
6829
7069
  const [internalSettings, setInternalSettings] = react.default.useState(null);
6830
7070
  const [infoMessage, setInfoMessage] = react.default.useState(null);
6831
7071
  (0, react.useEffect)(() => {
@@ -6837,7 +7077,7 @@ function App(props) {
6837
7077
  }, []);
6838
7078
  (0, react.useEffect)(() => {
6839
7079
  const listeners = [];
6840
- const cs = props.callSettings ?? {};
7080
+ const cs = props.sessionSettings ?? {};
6841
7081
  if (cs.listener?.onUserJoined) {
6842
7082
  listeners.push(CometChatCalls.addEventListener("onParticipantJoined", cs.listener.onUserJoined));
6843
7083
  }
@@ -6875,7 +7115,7 @@ function App(props) {
6875
7115
  listener();
6876
7116
  });
6877
7117
  };
6878
- }, [props.callSettings]);
7118
+ }, [props.sessionSettings]);
6879
7119
  (0, react.useEffect)(() => {
6880
7120
  callVerifyTokenAPI({
6881
7121
  appId: CometChatCalls.appSettings?.appId || "",
@@ -6904,14 +7144,12 @@ function App(props) {
6904
7144
  visible: true
6905
7145
  });
6906
7146
  }
6907
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(index_native_default, { callSettings: {
6908
- ...props.callSettings,
6909
- ...convertLegacyCallSettingsToV5Props(props?.callSettings ?? {}),
7147
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(index_native_default, { sessionSettings: {
7148
+ ...props.sessionSettings,
7149
+ ...convertLegacyCallSettingsToV5Props(props?.sessionSettings ?? {}),
6910
7150
  internalSettings
6911
7151
  } });
6912
7152
  }
6913
- var AppRN_default = App;
6914
- const AppComponent = App;
6915
7153
 
6916
7154
  //#endregion
6917
7155
  //#region src/v4/Constants.ts
@@ -10644,7 +10882,7 @@ var CometChatCalls = class extends SessionMethodsCore {
10644
10882
  static OngoingCallListener = OngoingCallListener;
10645
10883
  static CallSettingsBuilder = CallSettingsBuilder;
10646
10884
  static CallAppSettingsBuilder = CallAppSettingsBuilder;
10647
- static Component = AppComponent;
10885
+ static Component = AppReactNativeSDK;
10648
10886
  /**
10649
10887
  * Initializes the CometChat Calls SDK with the provided app settings.
10650
10888
  * Must be called before any other SDK methods.
@@ -10666,7 +10904,7 @@ var CometChatCalls = class extends SessionMethodsCore {
10666
10904
  }
10667
10905
  this.appSettings = parsedAppSettings.output;
10668
10906
  this.isInitialized = true;
10669
- const savedUser = this.getSavedUser();
10907
+ const savedUser = await this.getSavedUser();
10670
10908
  if (savedUser) {
10671
10909
  let parsedUser;
10672
10910
  if (typeof savedUser === "string") {
@@ -10735,12 +10973,11 @@ var CometChatCalls = class extends SessionMethodsCore {
10735
10973
  if (this.loggedInUser && this.loggedInUser.uid !== uid) {
10736
10974
  await this.logoutInternal();
10737
10975
  }
10738
- console.log("Logging in user with UID:", uid);
10739
10976
  const authToken = await this.loginWithUID(uid, resolvedAuthKey);
10740
10977
  const user = await this.authenticateWithToken(authToken);
10741
10978
  this.loginInProgress = false;
10742
10979
  this.loggedInUser = user;
10743
- this.saveUser(user);
10980
+ await this.saveUser(user);
10744
10981
  this.notifyLoginSuccess(user);
10745
10982
  return user;
10746
10983
  } catch (error) {
@@ -10788,7 +11025,7 @@ var CometChatCalls = class extends SessionMethodsCore {
10788
11025
  const user = await this.authenticateWithToken(authToken);
10789
11026
  this.loginInProgress = false;
10790
11027
  this.loggedInUser = user;
10791
- this.saveUser(user);
11028
+ await this.saveUser(user);
10792
11029
  this.notifyLoginSuccess(user);
10793
11030
  return user;
10794
11031
  } catch (error) {
@@ -10921,7 +11158,7 @@ var CometChatCalls = class extends SessionMethodsCore {
10921
11158
  appId
10922
11159
  },
10923
11160
  body: {
10924
- platform: "web",
11161
+ platform: "react-native",
10925
11162
  deviceId: this.generateDeviceId()
10926
11163
  }
10927
11164
  });
@@ -10942,7 +11179,7 @@ var CometChatCalls = class extends SessionMethodsCore {
10942
11179
  appId
10943
11180
  },
10944
11181
  body: {
10945
- platform: "web",
11182
+ platform: "react-native",
10946
11183
  deviceId: this.generateDeviceId()
10947
11184
  }
10948
11185
  });
@@ -10982,7 +11219,7 @@ var CometChatCalls = class extends SessionMethodsCore {
10982
11219
  }
10983
11220
  }
10984
11221
  this.loggedInUser = null;
10985
- this.clearSavedUser();
11222
+ await this.clearSavedUser();
10986
11223
  }
10987
11224
  static async callGenerateTokenAPI(sessionId, authToken) {
10988
11225
  const appId = this.appSettings?.appId || "";
@@ -11010,9 +11247,11 @@ var CometChatCalls = class extends SessionMethodsCore {
11010
11247
  baseURL: this.getBaseURL()
11011
11248
  });
11012
11249
  }
11013
- static saveUser(user) {
11250
+ static getStorageKey() {
11251
+ return `${this.appSettings?.appId}:common_store/user`;
11252
+ }
11253
+ static async saveUser(user) {
11014
11254
  try {
11015
- const key = `${this.appSettings?.appId}:common_store/user`;
11016
11255
  const userWithDefaults = {
11017
11256
  hasBlockedMe: false,
11018
11257
  blockedByMe: false,
@@ -11021,31 +11260,28 @@ var CometChatCalls = class extends SessionMethodsCore {
11021
11260
  role: user.role || "default",
11022
11261
  wsChannel: user.wsChannel || { identity: `[${this.appSettings?.appId}]${user.uid}` }
11023
11262
  };
11024
- localStorage.setItem(key, JSON.stringify(userWithDefaults));
11263
+ await __react_native_async_storage_async_storage.default.setItem(this.getStorageKey(), JSON.stringify(userWithDefaults));
11025
11264
  } catch (error) {
11026
- console.warn("Failed to save user to localStorage:", error);
11265
+ console.warn("Failed to save user to AsyncStorage:", error);
11027
11266
  }
11028
11267
  }
11029
- static getSavedUser() {
11268
+ static async getSavedUser() {
11030
11269
  try {
11031
- const key = `${this.appSettings?.appId}:common_store/user`;
11032
- const savedUser = localStorage.getItem(key);
11033
- return savedUser ? savedUser : null;
11270
+ return await __react_native_async_storage_async_storage.default.getItem(this.getStorageKey());
11034
11271
  } catch (error) {
11035
- console.warn("Failed to get saved user from localStorage:", error);
11272
+ console.warn("Failed to get saved user from AsyncStorage:", error);
11036
11273
  return null;
11037
11274
  }
11038
11275
  }
11039
- static clearSavedUser() {
11276
+ static async clearSavedUser() {
11040
11277
  try {
11041
- const key = `${this.appSettings?.appId}:common_store/user`;
11042
- localStorage.removeItem(key);
11278
+ await __react_native_async_storage_async_storage.default.removeItem(this.getStorageKey());
11043
11279
  } catch (error) {
11044
- console.warn("Failed to clear saved user from localStorage:", error);
11280
+ console.warn("Failed to clear saved user from AsyncStorage:", error);
11045
11281
  }
11046
11282
  }
11047
11283
  static generateDeviceId() {
11048
- return "web_" + Math.random().toString(36).substr(2, 9);
11284
+ return "rn_" + Math.random().toString(36).substring(2, 11);
11049
11285
  }
11050
11286
  static createError(error) {
11051
11287
  if (error.errorCode && error.errorDescription) {
@@ -11104,13 +11340,32 @@ var CometChatCalls = class extends SessionMethodsCore {
11104
11340
  * Adds an event listener for SDK events.
11105
11341
  * @param eventType - The type of event to listen for.
11106
11342
  * @param listener - The callback function to invoke when the event fires.
11343
+ * @param options - Optional configuration including an AbortSignal for automatic cleanup.
11107
11344
  * @returns An unsubscribe function to remove the listener.
11108
11345
  */
11109
- static addEventListener(eventType, listener) {
11110
- return eventBus.subscribe(eventType, listener);
11346
+ static addEventListener(eventType, listener, options) {
11347
+ return eventBus.subscribe(eventType, listener, options);
11348
+ }
11349
+ /**
11350
+ * Sets the audio output mode (mobile only).
11351
+ * @param mode - The audio mode to set (e.g., 'SPEAKER', 'EARPIECE', 'BLUETOOTH', 'HEADPHONES').
11352
+ */
11353
+ static setAudioMode(mode) {
11354
+ setAudioMode(mode);
11355
+ }
11356
+ /**
11357
+ * Enables Picture-in-Picture (PIP) layout during the call.
11358
+ */
11359
+ static enablePictureInPictureLayout() {
11360
+ enablePictureInPictureLayout();
11361
+ }
11362
+ /**
11363
+ * Disables Picture-in-Picture (PIP) layout.
11364
+ */
11365
+ static disablePictureInPictureLayout() {
11366
+ disablePictureInPictureLayout();
11111
11367
  }
11112
11368
  };
11113
11369
 
11114
11370
  //#endregion
11115
- exports.CometChatCalls = CometChatCalls;
11116
- //# sourceMappingURL=index.js.map
11371
+ exports.CometChatCalls = CometChatCalls;