@cometchat/calls-sdk-react-native 5.0.0-beta.3 → 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.mjs CHANGED
@@ -5,6 +5,7 @@ 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
7
  import JitsiMeetJS from "lib-jitsi-meet";
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";
@@ -111,10 +112,12 @@ const VIDEO_QUALITY_LEVELS = {
111
112
  LOW: 180,
112
113
  NONE: 0
113
114
  };
114
- const PLATFORM = {
115
+ const SDK_PLATFORM = {
115
116
  WEB: "web",
116
117
  ANDROID: "android",
117
- IOS: "ios"
118
+ IOS: "ios",
119
+ REACT_NATIVE_ANDROID: "react-native-android",
120
+ REACT_NATIVE_IOS: "react-native-ios"
118
121
  };
119
122
  const EVENT_LISTENER_METHODS = {
120
123
  SessionStatusListener: {
@@ -229,7 +232,7 @@ function calculateTileLayout(containerWidth, containerHeight, numberOfTiles) {
229
232
  const tileArea = totalArea / numberOfTiles;
230
233
  const minArea = MIN_TILE_WIDTH * MIN_TILE_WIDTH * MIN_ASPECT_RATIO;
231
234
  if (tileArea < minArea) {
232
- const columnCount$1 = Math.floor(containerWidth / MIN_TILE_WIDTH);
235
+ const columnCount$1 = Math.max(2, Math.floor(containerWidth / MIN_TILE_WIDTH));
233
236
  const rowCount$1 = Math.ceil(numberOfTiles / columnCount$1);
234
237
  const totalHorizontalGap$1 = columnCount$1 * GRID_GAP;
235
238
  const tileWidth$1 = (containerWidth - totalHorizontalGap$1) / columnCount$1;
@@ -412,6 +415,31 @@ function isDeviceEqual(device1, device2) {
412
415
  function getDefaultDevice(devices) {
413
416
  return devices.find((device) => device.deviceId === "default") || devices[0];
414
417
  }
418
+ /**
419
+ * Returns a promise that resolves when the given Zustand store
420
+ * satisfies the provided predicate. Resolves immediately if the
421
+ * condition is already met. Includes a timeout to avoid hanging
422
+ * forever (defaults to 5 000 ms).
423
+ */
424
+ function waitForStoreState(store, predicate, timeoutMs = 5e3) {
425
+ return new Promise((resolve, reject) => {
426
+ if (predicate(store.getState())) {
427
+ resolve();
428
+ return;
429
+ }
430
+ const timer = setTimeout(() => {
431
+ unsubscribe();
432
+ reject(new Error("waitForStoreState timed out"));
433
+ }, timeoutMs);
434
+ const unsubscribe = store.subscribe((state) => {
435
+ if (predicate(state)) {
436
+ clearTimeout(timer);
437
+ unsubscribe();
438
+ resolve();
439
+ }
440
+ });
441
+ });
442
+ }
415
443
 
416
444
  //#endregion
417
445
  //#region calls-sdk-core/utils/try-catch.ts
@@ -474,6 +502,13 @@ var SessionMethodsCore = class {
474
502
  unMuteAudioTrack();
475
503
  }
476
504
  /**
505
+ * Toggles the local user's audio mute state.
506
+ * If audio is muted, it will be unmuted, and vice versa.
507
+ */
508
+ static toggleAudio() {
509
+ toggleAudioTrack();
510
+ }
511
+ /**
477
512
  * Pauses the local user's video stream.
478
513
  */
479
514
  static pauseVideo() {
@@ -486,6 +521,13 @@ var SessionMethodsCore = class {
486
521
  resumeVideoTrack();
487
522
  }
488
523
  /**
524
+ * Toggles the local user's video stream.
525
+ * If video is paused, it will be resumed, and vice versa.
526
+ */
527
+ static toggleVideo() {
528
+ toggleVideoTrack();
529
+ }
530
+ /**
489
531
  * Local user leaves the current session.
490
532
  */
491
533
  static leaveSession() {
@@ -504,6 +546,13 @@ var SessionMethodsCore = class {
504
546
  lowerHandLocal();
505
547
  }
506
548
  /**
549
+ * Toggles the user's virtual hand state.
550
+ * If the hand is raised, it will be lowered, and vice versa.
551
+ */
552
+ static toggleHand() {
553
+ toggleRaiseHand();
554
+ }
555
+ /**
507
556
  * Switches between the front and rear camera.
508
557
  */
509
558
  static switchCamera() {
@@ -519,11 +568,22 @@ var SessionMethodsCore = class {
519
568
  /**
520
569
  * Starts recording the call.
521
570
  */
522
- static startRecording() {}
571
+ static startRecording() {
572
+ startRecording();
573
+ }
523
574
  /**
524
575
  * Stops the ongoing call recording.
525
576
  */
526
- static stopRecording() {}
577
+ static stopRecording() {
578
+ stopRecording();
579
+ }
580
+ /**
581
+ * Toggles the call recording state.
582
+ * If recording is active, it will be stopped, and vice versa.
583
+ */
584
+ static toggleRecording() {
585
+ toggleRecording();
586
+ }
527
587
  /**
528
588
  * Pins a participant's video to focus on them.
529
589
  * @param participantId - The ID of the participant to pin.
@@ -560,6 +620,24 @@ var SessionMethodsCore = class {
560
620
  setChatButtonUnreadCount(count);
561
621
  }
562
622
  /**
623
+ * Toggles the visibility of the participant list panel.
624
+ */
625
+ static toggleParticipantList() {
626
+ toggleParticipantList();
627
+ }
628
+ /**
629
+ * Shows the participant list panel.
630
+ */
631
+ static showParticipantList() {
632
+ showParticipantList();
633
+ }
634
+ /**
635
+ * Hides the participant list panel.
636
+ */
637
+ static hideParticipantList() {
638
+ hideParticipantList();
639
+ }
640
+ /**
563
641
  * @deprecated switchToVideoCall is deprecated and not supported.
564
642
  */
565
643
  static switchToVideoCall() {
@@ -916,10 +994,16 @@ const initialState$6 = {
916
994
  };
917
995
  const useParticipantStore = create()(subscribeWithSelector(combine(initialState$6, (set, get$1) => ({
918
996
  addParticipant: (participant) => {
919
- set((state) => ({ participants: [...state.participants, participant] }));
997
+ set((state) => ({ participants: state.participants.some((p) => p.pid === participant.pid) ? state.participants.map((p) => p.pid === participant.pid ? {
998
+ ...p,
999
+ ...participant
1000
+ } : p) : [...state.participants, participant] }));
920
1001
  },
921
1002
  addVirtualParticipant: (participant) => {
922
- set((state) => ({ virtualParticipants: [...state.virtualParticipants, participant] }));
1003
+ 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 ? {
1004
+ ...p,
1005
+ ...participant
1006
+ } : p) : [...state.virtualParticipants, participant] }));
923
1007
  },
924
1008
  clearParticipants: () => set({
925
1009
  participants: [],
@@ -1156,6 +1240,8 @@ const useConferenceStore = create()(subscribeWithSelector(combine(initialState$5
1156
1240
  sendParticipantEvent(EVENT_LISTENER_METHODS.ParticipantEventsListner.onParticipantHandRaised, participantId);
1157
1241
  },
1158
1242
  lowerHand: (participantId) => {
1243
+ const hasRaisedHand = useConferenceStore.getState().raiseHandMap.has(participantId);
1244
+ if (!hasRaisedHand) return;
1159
1245
  set((state) => {
1160
1246
  const raiseHandMap = new Map(state.raiseHandMap);
1161
1247
  raiseHandMap.delete(participantId);
@@ -1353,6 +1439,12 @@ const useTracksStore = create()(subscribeWithSelector(combine(initialState$4, (s
1353
1439
  muted: originalTrack.isMuted() ? 1 : 0,
1354
1440
  originalTrack
1355
1441
  };
1442
+ const existingIdx = state.tracks.findIndex((t) => t.pid === participantId && t.mediaType === track.mediaType && t.local === isLocal);
1443
+ if (existingIdx !== -1) {
1444
+ const tracks = [...state.tracks];
1445
+ tracks[existingIdx] = track;
1446
+ return { tracks };
1447
+ }
1356
1448
  return { tracks: [...state.tracks, track] };
1357
1449
  }),
1358
1450
  removeTrack: (originalTrack) => set((state) => ({ tracks: state.tracks.filter((track) => track.originalTrack !== originalTrack) })),
@@ -1554,8 +1646,13 @@ useTracksStore.subscribe((state) => state.tracks.find((t) => t.mediaType === MED
1554
1646
  }
1555
1647
  if (track) {
1556
1648
  const deviceId = track.getDeviceId();
1557
- const device = useBaseStore.getState().audioInputDevices.find((d) => d.deviceId === deviceId);
1558
- updateAudioInputDeviceState(device, true);
1649
+ waitForStoreState(useBaseStore, (state) => state.audioInputDevices.length > 0).then(() => {
1650
+ const audioInputDevices = useBaseStore.getState().audioInputDevices;
1651
+ const device = audioInputDevices.find((d) => d.deviceId === deviceId);
1652
+ updateAudioInputDeviceState(device, true);
1653
+ }).catch(() => {
1654
+ updateAudioInputDeviceState(undefined, true);
1655
+ });
1559
1656
  }
1560
1657
  });
1561
1658
  useTracksStore.subscribe((state) => state.tracks.find((t) => t.mediaType === MEDIA_TYPE.VIDEO && t.local)?.originalTrack, (track, prevTrack) => {
@@ -1737,7 +1834,7 @@ const initialState$3 = {
1737
1834
  desktopSharingFrameRate: 5,
1738
1835
  chatButtonUnreadCount: 0,
1739
1836
  enableNoiseReduction: true,
1740
- sdkPlatform: PLATFORM.WEB,
1837
+ sdkPlatform: SDK_PLATFORM.WEB,
1741
1838
  webOSName: "unknown",
1742
1839
  isMobileBrowser: false,
1743
1840
  visibleParticipants: {
@@ -1761,11 +1858,11 @@ const useBaseStore = create()(subscribeWithSelector(persist(combine(initialState
1761
1858
  toggleParticipantListVisible: () => set((state) => ({ participantListVisible: !state.participantListVisible })),
1762
1859
  incrementConnectionRetryCount: () => set((state) => ({ connectionRetryCount: state.connectionRetryCount + 1 })),
1763
1860
  isMobileSDK: () => {
1764
- const isMobileSDK = get$1().sdkPlatform === "android" || get$1().sdkPlatform === "ios";
1861
+ const isMobileSDK = get$1().sdkPlatform !== "web";
1765
1862
  return isMobileSDK;
1766
1863
  },
1767
1864
  isMobile: () => {
1768
- const isMobileSDK = get$1().sdkPlatform === "android" || get$1().sdkPlatform === "ios";
1865
+ const isMobileSDK = get$1().sdkPlatform !== "web";
1769
1866
  const isMobileBrowser = get$1().isMobileBrowser;
1770
1867
  return isMobileSDK || isMobileBrowser;
1771
1868
  },
@@ -1811,6 +1908,7 @@ const useBaseStore = create()(subscribeWithSelector(persist(combine(initialState
1811
1908
  const toggleParticipantListVisible = useBaseStore.getState().toggleParticipantListVisible;
1812
1909
  const hideParticipantList = () => useBaseStore.setState({ participantListVisible: false });
1813
1910
  const showParticipantList = () => useBaseStore.setState({ participantListVisible: true });
1911
+ const toggleParticipantList = () => useBaseStore.setState((state) => ({ participantListVisible: !state.participantListVisible }));
1814
1912
  const toggleMoreMenuVisible = useBaseStore.getState().toggleMoreMenuVisible;
1815
1913
  const toggleAudioModeMenuVisible = () => {
1816
1914
  useBaseStore.setState((state) => ({ audioModeMenuVisible: !state.audioModeMenuVisible }));
@@ -1859,6 +1957,9 @@ const toggleEnableNoiseReduction = () => {
1859
1957
  const setChatButtonUnreadCount = (count) => {
1860
1958
  useBaseStore.setState({ chatButtonUnreadCount: count });
1861
1959
  };
1960
+ const setAudioMode = (mode) => {
1961
+ useBaseStore.setState({ selectedAudioModeType: mode });
1962
+ };
1862
1963
  const getLayout = () => {
1863
1964
  return useBaseStore.getState().layout;
1864
1965
  };
@@ -2119,8 +2220,21 @@ const getMainParticipant = () => {
2119
2220
  const useIsReconnecting = () => {
2120
2221
  const connectionStatus = useConnectionStore((state) => state.connectionStatus);
2121
2222
  const conferenceStatus = useConferenceStore((state) => state.conferenceStatus);
2122
- const reconnecting = connectionStatus === "connected" && conferenceStatus === "interrupted";
2123
- return reconnecting;
2223
+ const isP2P = useConferenceStore((state) => state.p2p);
2224
+ const [isOnline, setIsOnline] = useState(true);
2225
+ useEffect(() => {
2226
+ if (typeof window === "undefined") return;
2227
+ const controller = new AbortController();
2228
+ const { signal } = controller;
2229
+ window.addEventListener("online", () => setIsOnline(true), { signal });
2230
+ window.addEventListener("offline", () => setIsOnline(false), { signal });
2231
+ return () => controller.abort();
2232
+ }, []);
2233
+ const interrupted = connectionStatus === "connected" && conferenceStatus === "interrupted";
2234
+ if (isP2P && interrupted && isOnline) {
2235
+ return false;
2236
+ }
2237
+ return interrupted;
2124
2238
  };
2125
2239
  const useHideRecordingButton = () => {
2126
2240
  const hideRecordingButton = useConfigStore((state) => state.hideRecordingButton);
@@ -2198,6 +2312,11 @@ const useIsVideoInputSelectionSupported = () => {
2198
2312
  const isVideoInputSelectionSupported = hasVideoPermission && videoInputDevices.length > 0;
2199
2313
  return isVideoInputSelectionSupported;
2200
2314
  };
2315
+ const useShouldMirrorLocalVideo = () => {
2316
+ const mirrorLocalVideo = useBaseStore((state) => state.mirrorLocalVideo);
2317
+ const cameraFacing = useBaseStore((state) => state.cameraFacing);
2318
+ return cameraFacing === "user" && mirrorLocalVideo;
2319
+ };
2201
2320
 
2202
2321
  //#endregion
2203
2322
  //#region calls-sdk-core/store/utils/switch-camera.ts
@@ -2422,17 +2541,19 @@ var ConferenceListener = class {
2422
2541
  useConferenceStore.setState({ p2p });
2423
2542
  }
2424
2543
  onTrackMuteChanged(track, participantThatMutedUs) {
2425
- if (participantThatMutedUs) {
2426
- useTracksStore.getState().updateTrack(track, { muted: track.isMuted() ? 1 : 0 });
2427
- const displayName = participantThatMutedUs.getDisplayName();
2428
- if (displayName) {
2429
- eventBus.publish({
2430
- type: INTERNAL_EVENTS.notification,
2431
- payload: {
2432
- type: "info",
2433
- message: `${displayName} has muted you.`
2434
- }
2435
- });
2544
+ if (track.isLocal()) {
2545
+ useTracksStore.getState().updateLocalTrack(track.getType(), { muted: track.isMuted() ? 1 : 0 });
2546
+ if (participantThatMutedUs) {
2547
+ const displayName = participantThatMutedUs.getDisplayName();
2548
+ if (displayName) {
2549
+ eventBus.publish({
2550
+ type: INTERNAL_EVENTS.notification,
2551
+ payload: {
2552
+ type: "info",
2553
+ message: `${displayName} has muted you.`
2554
+ }
2555
+ });
2556
+ }
2436
2557
  }
2437
2558
  }
2438
2559
  }
@@ -2487,6 +2608,24 @@ var ConferenceListener = class {
2487
2608
  useParticipantStore.getState().updateParticipant(participantId, { role: newRole });
2488
2609
  }
2489
2610
  }
2611
+ onTrackUnmuteRejected(track) {
2612
+ if (!track.isLocal()) {
2613
+ return;
2614
+ }
2615
+ const mediaType = track.getType();
2616
+ track.dispose().catch(() => {});
2617
+ useTracksStore.getState().updateLocalTrack(mediaType, {
2618
+ originalTrack: undefined,
2619
+ muted: 1
2620
+ });
2621
+ eventBus.publish({
2622
+ type: INTERNAL_EVENTS.notification,
2623
+ payload: {
2624
+ type: "info",
2625
+ message: `Your ${mediaType} unmute was rejected.`
2626
+ }
2627
+ });
2628
+ }
2490
2629
  onTalkWhileMuted() {}
2491
2630
  onConferenceError(error) {
2492
2631
  console.error("Conference error:", error);
@@ -2565,6 +2704,7 @@ function addConferenceListeners(conference) {
2565
2704
  conference.on(JitsiMeetJS.events.conference.PARTICIPANT_PROPERTY_CHANGED, conferenceListener.onParticipantPropertyChanged);
2566
2705
  conference.on(JitsiMeetJS.events.conference.USER_ROLE_CHANGED, conferenceListener.onUserRoleChanged);
2567
2706
  conference.on(JitsiMeetJS.events.conference.TALK_WHILE_MUTED, conferenceListener.onTalkWhileMuted);
2707
+ conference.on(JitsiMeetJS.events.conference.TRACK_UNMUTE_REJECTED, conferenceListener.onTrackUnmuteRejected);
2568
2708
  conference.on(JitsiMeetJS.events.conference.TRACK_AUDIO_LEVEL_CHANGED, conferenceListener.onTrackAudioLevelChanged);
2569
2709
  conference.addCommandListener(CONFERENCE_COMMANDS.userInfo, (data, id) => {
2570
2710
  const vData = v.safeParse(UserInfoCommandSchema, safeParseJson(data.value));
@@ -2852,6 +2992,7 @@ var Mutex = class {
2852
2992
  //#region calls-sdk-core/handlers/init.ts
2853
2993
  function initializeLib() {
2854
2994
  JitsiMeetJS.init();
2995
+ JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.ERROR);
2855
2996
  console.log("JitsiMeetJS initialized successfully.");
2856
2997
  }
2857
2998
 
@@ -4009,6 +4150,7 @@ const PopupMenu = ({ visible, onClose, options, anchorLayout }) => {
4009
4150
  children: /* @__PURE__ */ jsx(Pressable, {
4010
4151
  style: styles$27.backdrop,
4011
4152
  onPress: onClose,
4153
+ testID: "cometchat-popup-menu-backdrop",
4012
4154
  children: /* @__PURE__ */ jsx(Animated.View, {
4013
4155
  style: [styles$27.menu, {
4014
4156
  [isBelowMiddle ? "bottom" : "top"]: isBelowMiddle ? callContainerDimension.height - (y - 4) : y + height + 4,
@@ -4029,6 +4171,7 @@ const PopupMenu = ({ visible, onClose, options, anchorLayout }) => {
4029
4171
  },
4030
4172
  activeOpacity: option.selected ? DISABLED_OPTION_OPACITY : .2,
4031
4173
  style: [styles$27.menuItem, option.selected ? styles$27.menuItemSelected : {}],
4174
+ testID: `cometchat-popup-menu-option-${index}`,
4032
4175
  children: [option.iconName && /* @__PURE__ */ jsx(Icon_native_default, {
4033
4176
  name: option.iconName,
4034
4177
  size: 24,
@@ -4190,6 +4333,7 @@ const MoreOptionButton = ({ ruid }) => {
4190
4333
  ref: buttonRef,
4191
4334
  style: styles$4.moreButton,
4192
4335
  onPress: showMenu,
4336
+ testID: "cometchat-participant-more-options-button",
4193
4337
  children: /* @__PURE__ */ jsx(Icon_native_default, {
4194
4338
  name: "more",
4195
4339
  size: 20,
@@ -4359,6 +4503,7 @@ const Tile = ({ participant, style, zOrder, showLabel = true, disablePress = fal
4359
4503
  const videoTrack = useTrackByParticipantId(pid, type === "screen-share" ? MEDIA_TYPE.SCREENSHARE : MEDIA_TYPE.VIDEO)?.originalTrack;
4360
4504
  const videoMuted = useTrackMuted(type === "screen-share" ? MEDIA_TYPE.SCREENSHARE : MEDIA_TYPE.VIDEO, pid);
4361
4505
  const enableParticipantContextMenu = useEnableParticipantContextMenu();
4506
+ const shouldMirror = useShouldMirrorLocalVideo();
4362
4507
  const [size, fontSize] = React.useMemo(() => {
4363
4508
  const flatStyle = StyleSheet.flatten(style);
4364
4509
  const width$1 = flatStyle?.width;
@@ -4376,6 +4521,7 @@ const Tile = ({ participant, style, zOrder, showLabel = true, disablePress = fal
4376
4521
  activeOpacity: 1,
4377
4522
  disabled: disablePress,
4378
4523
  style: [styles$24.callScreen, style],
4524
+ testID: `cometchat-tile-${pid}`,
4379
4525
  children: [
4380
4526
  /* @__PURE__ */ jsx(View, {
4381
4527
  style: styles$24.tileAvatar,
@@ -4391,7 +4537,7 @@ const Tile = ({ participant, style, zOrder, showLabel = true, disablePress = fal
4391
4537
  objectFit: type === "screen-share" ? "contain" : "cover",
4392
4538
  muted: videoMuted,
4393
4539
  zOrder,
4394
- mirror: isLocal
4540
+ mirror: isLocal && type !== "screen-share" && shouldMirror
4395
4541
  }),
4396
4542
  showLabel && /* @__PURE__ */ jsx(Label_native_default, { participant }),
4397
4543
  enableParticipantContextMenu && /* @__PURE__ */ jsx(View, {
@@ -4450,6 +4596,7 @@ const GroupAvatarTile = ({ startIndex = 4, style }) => {
4450
4596
  const overflowCount = Math.max(0, participantCount - startIndex - 3);
4451
4597
  const visible = participants.slice(startIndex, startIndex + (overflowCount === 1 ? 4 : 3));
4452
4598
  return /* @__PURE__ */ jsxs(TouchableOpacity, {
4599
+ testID: "cometchat-group-avatar-tile",
4453
4600
  style: [styles$23.container, style],
4454
4601
  onPress: toggleParticipantListVisible,
4455
4602
  children: [/* @__PURE__ */ jsx(View, {
@@ -4557,6 +4704,7 @@ function SidebarLayout() {
4557
4704
  const mainParticipant = useMainParticipant();
4558
4705
  const participants = allParticipants.length > 1 ? [mainParticipant].concat(allParticipants) : [mainParticipant];
4559
4706
  return /* @__PURE__ */ jsxs(View, {
4707
+ testID: "cometchat-sidebar-layout",
4560
4708
  style: styles$22.container,
4561
4709
  children: [
4562
4710
  /* @__PURE__ */ jsx(Tile_native_default, {
@@ -4693,23 +4841,26 @@ const Spotlight = () => {
4693
4841
  if (otherParticipant) {
4694
4842
  spotlightParticipants.push(otherParticipant);
4695
4843
  }
4696
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Tile_native_default, {
4697
- participant: spotlightParticipants[0],
4698
- disablePress: true
4699
- }, spotlightParticipants[0].ruid), spotlightParticipants[1] && /* @__PURE__ */ jsx(Pan, {
4700
- disableDrag: !enableSpotlightDrag,
4701
- layout: {
4702
- width: mainAreaDimension.width,
4703
- height: mainAreaDimension.height
4704
- },
4705
- children: /* @__PURE__ */ jsx(Tile_native_default, {
4706
- showLabel: false,
4707
- disablePress: !enableSpotlightSwap,
4708
- participant: spotlightParticipants[1],
4709
- style: styles$20.panTile,
4710
- zOrder: 1
4711
- }, spotlightParticipants[1].ruid)
4712
- })] });
4844
+ return /* @__PURE__ */ jsxs(View, {
4845
+ testID: "cometchat-spotlight-layout",
4846
+ children: [/* @__PURE__ */ jsx(Tile_native_default, {
4847
+ participant: spotlightParticipants[0],
4848
+ disablePress: true
4849
+ }, spotlightParticipants[0].ruid), spotlightParticipants[1] && /* @__PURE__ */ jsx(Pan, {
4850
+ disableDrag: !enableSpotlightDrag,
4851
+ layout: {
4852
+ width: mainAreaDimension.width,
4853
+ height: mainAreaDimension.height
4854
+ },
4855
+ children: /* @__PURE__ */ jsx(Tile_native_default, {
4856
+ showLabel: false,
4857
+ disablePress: !enableSpotlightSwap,
4858
+ participant: spotlightParticipants[1],
4859
+ style: styles$20.panTile,
4860
+ zOrder: 1
4861
+ }, spotlightParticipants[1].ruid)
4862
+ })]
4863
+ });
4713
4864
  };
4714
4865
  const styles$20 = StyleSheet.create({ panTile: {
4715
4866
  borderColor: "#1A1A1A",
@@ -4742,6 +4893,7 @@ function TileLayout() {
4742
4893
  participantCount: isPIPLayoutEnabled ? 1 : participants.length
4743
4894
  });
4744
4895
  return /* @__PURE__ */ jsx(FlatList, {
4896
+ testID: "cometchat-tile-layout",
4745
4897
  data: isPIPLayoutEnabled ? [mainParticipant] : participants,
4746
4898
  renderItem: ({ item }) => /* @__PURE__ */ jsx(Tile_native_default, {
4747
4899
  participant: item,
@@ -5100,6 +5252,7 @@ function ConfirmationDialog() {
5100
5252
  children: /* @__PURE__ */ jsx(Pressable, {
5101
5253
  style: styles$15.backdrop,
5102
5254
  onPress: handleBackdropPress,
5255
+ testID: "cometchat-confirmation-dialog-backdrop",
5103
5256
  children: /* @__PURE__ */ jsx(View, {
5104
5257
  style: styles$15.dialog,
5105
5258
  children: /* @__PURE__ */ jsxs(View, {
@@ -5142,7 +5295,7 @@ function ConfirmationDialog() {
5142
5295
  //#endregion
5143
5296
  //#region src/ui/bottom-sheet/BottomSheet.native.tsx
5144
5297
  const SCREEN_HEIGHT = Dimensions.get("window").height;
5145
- const BottomSheet = ({ children, isVisible, onClose, maxHeight = SCREEN_HEIGHT * .4 }) => {
5298
+ const BottomSheet = ({ children, isVisible, onClose, maxHeight = SCREEN_HEIGHT * .4, testID }) => {
5146
5299
  const visibleTranslateY = SCREEN_HEIGHT - maxHeight;
5147
5300
  const hiddenTranslateY = useRef(SCREEN_HEIGHT).current;
5148
5301
  const animatedValue = useRef(new Animated.Value(hiddenTranslateY)).current;
@@ -5168,8 +5321,10 @@ const BottomSheet = ({ children, isVisible, onClose, maxHeight = SCREEN_HEIGHT *
5168
5321
  const bottomSheetAnimation = { transform: [{ translateY: animatedValue }] };
5169
5322
  return /* @__PURE__ */ jsxs(Fragment, { children: [isVisible && /* @__PURE__ */ jsx(TouchableWithoutFeedback, {
5170
5323
  onPress: onClose,
5324
+ testID: "cometchat-bottom-sheet-backdrop",
5171
5325
  children: /* @__PURE__ */ jsx(View, { style: styles$14.backdrop })
5172
5326
  }), /* @__PURE__ */ jsxs(Animated.View, {
5327
+ testID,
5173
5328
  style: [
5174
5329
  styles$14.bottomSheet,
5175
5330
  bottomSheetAnimation,
@@ -5227,8 +5382,9 @@ var BottomSheet_native_default = BottomSheet;
5227
5382
 
5228
5383
  //#endregion
5229
5384
  //#region src/ui/control-pane/MenuItem.native.tsx
5230
- const MenuItem = ({ iconName, label, onPress, selected = false }) => {
5385
+ const MenuItem = ({ iconName, label, onPress, selected = false, testID }) => {
5231
5386
  return /* @__PURE__ */ jsxs(TouchableOpacity, {
5387
+ testID,
5232
5388
  onPress: () => {
5233
5389
  hideAllBottomSheets();
5234
5390
  onPress();
@@ -5281,6 +5437,7 @@ const AudioModesMenu = ({ isVisible, onClose }) => {
5281
5437
  style: [commonStyles.bodyRegular, styles$12.noItemsText],
5282
5438
  children: "No audio modes available"
5283
5439
  }), audioModes.map((mode, index) => /* @__PURE__ */ jsx(MenuItem_native_default, {
5440
+ testID: `cometchat-menu-item-audio-${mode.type.toLowerCase()}`,
5284
5441
  iconName: AUDIO_MODE_TYPE_ICON_MAP[mode.type],
5285
5442
  label: mode.type,
5286
5443
  selected: mode.selected,
@@ -5332,6 +5489,7 @@ const AudioModeButton = () => {
5332
5489
  style: controlPaneStyles.controlButton,
5333
5490
  onPress: toggleAudioModeMenuVisible,
5334
5491
  activeOpacity: .7,
5492
+ testID: "cometchat-audio-mode-button",
5335
5493
  children: /* @__PURE__ */ jsx(Icon_native_default, {
5336
5494
  name: "speaker-fill",
5337
5495
  fill: "#FFF",
@@ -5357,6 +5515,7 @@ const AudioControl = () => {
5357
5515
  style: [controlPaneStyles.controlButton, muted && controlPaneStyles.toggledButton],
5358
5516
  onPress,
5359
5517
  activeOpacity: .7,
5518
+ testID: "cometchat-audio-toggle-button",
5360
5519
  children: /* @__PURE__ */ jsx(Icon_native_default, {
5361
5520
  name: muted ? "mic-off-fill" : "mic-fill",
5362
5521
  fill: muted ? "#9F3032" : "#FFF",
@@ -5382,6 +5541,7 @@ const VideoControl = () => {
5382
5541
  style: [controlPaneStyles.controlButton, videoMuted && controlPaneStyles.toggledButton],
5383
5542
  onPress,
5384
5543
  activeOpacity: .7,
5544
+ testID: "cometchat-video-toggle-button",
5385
5545
  children: /* @__PURE__ */ jsx(Icon_native_default, {
5386
5546
  name: videoMuted ? "video-off-fill" : "video-fill",
5387
5547
  fill: videoMuted ? "#9F3032" : "#FFF",
@@ -5406,6 +5566,7 @@ const LeaveSessionButton = () => {
5406
5566
  style: [controlPaneStyles.controlButton, controlPaneStyles.leaveSessionButton],
5407
5567
  onPress,
5408
5568
  activeOpacity: .7,
5569
+ testID: "cometchat-leave-session-button",
5409
5570
  children: /* @__PURE__ */ jsx(Icon_native_default, {
5410
5571
  name: "call-end",
5411
5572
  fill: "#FFF",
@@ -5422,6 +5583,7 @@ const MoreMenuButton = () => {
5422
5583
  style: controlPaneStyles.controlButton,
5423
5584
  onPress: toggleMoreMenuVisible,
5424
5585
  activeOpacity: .7,
5586
+ testID: "cometchat-more-menu-button",
5425
5587
  children: /* @__PURE__ */ jsx(Icon_native_default, {
5426
5588
  name: "more",
5427
5589
  fill: "#FFF",
@@ -5497,22 +5659,26 @@ const MoreMenu = ({ isVisible, onClose }) => {
5497
5659
  toggleParticipantListVisible();
5498
5660
  }, []);
5499
5661
  return /* @__PURE__ */ jsx(BottomSheet_native_default, {
5662
+ testID: "cometchat-more-menu-bottom-sheet",
5500
5663
  maxHeight: bottomSheetMaxHeight,
5501
5664
  isVisible,
5502
5665
  onClose,
5503
5666
  children: /* @__PURE__ */ jsxs(ScrollView, { children: [
5504
5667
  numberOfVisibleItems === 0 && /* @__PURE__ */ jsx(Text, {
5668
+ testID: "cometchat-more-menu-empty-state",
5505
5669
  style: [commonStyles.bodyRegular, styles$11.noItemsText],
5506
5670
  children: "No options available"
5507
5671
  }),
5508
5672
  !hideScreenSharingButton && /* @__PURE__ */ jsx(ScreenShareButton_default, {}),
5509
5673
  !hideRaiseHandButton && /* @__PURE__ */ jsx(MenuItem_native_default, {
5674
+ testID: "cometchat-menu-item-raise-hand",
5510
5675
  iconName: "raise-hand-fill",
5511
5676
  label: raiseHandTimestamp ? "Lower Hand" : "Raise Hand",
5512
5677
  onPress: onRaiseHandPress,
5513
5678
  selected: Boolean(raiseHandTimestamp)
5514
5679
  }),
5515
5680
  !hideRecordingButton && /* @__PURE__ */ jsx(MenuItem_native_default, {
5681
+ testID: isRecording ? "cometchat-menu-item-stop-recording" : "cometchat-menu-item-start-recording",
5516
5682
  iconName: isRecording ? "record-stop-fill" : "record-fill",
5517
5683
  label: isRecording ? "Stop Recording" : "Start Recording",
5518
5684
  onPress: () => {
@@ -5523,6 +5689,7 @@ const MoreMenu = ({ isVisible, onClose }) => {
5523
5689
  }
5524
5690
  }),
5525
5691
  !hideParticipantListButton && /* @__PURE__ */ jsx(MenuItem_native_default, {
5692
+ testID: "cometchat-menu-item-participants",
5526
5693
  iconName: "participants",
5527
5694
  label: "Participants",
5528
5695
  onPress: onParticipantListPress
@@ -5649,6 +5816,7 @@ const ChangeLayout = () => {
5649
5816
  eventBus.publish({ type: EVENT_LISTENER_METHODS.ButtonClickListener.onChangeLayoutButtonClicked });
5650
5817
  showMenu();
5651
5818
  },
5819
+ testID: "cometchat-change-layout-button",
5652
5820
  children: /* @__PURE__ */ jsx(Icon_native_default, {
5653
5821
  name: "tile-fill",
5654
5822
  fill: "#FFFFFF"
@@ -5701,6 +5869,7 @@ const ChatButton = () => {
5701
5869
  return /* @__PURE__ */ jsxs(TouchableOpacity, {
5702
5870
  style: [styles$9.iconButton],
5703
5871
  onPress,
5872
+ testID: "cometchat-chat-button",
5704
5873
  children: [/* @__PURE__ */ jsx(Icon_native_default, {
5705
5874
  name: "chat",
5706
5875
  fill: "#FFFFFF"
@@ -5757,6 +5926,7 @@ const SwitchCamera = () => {
5757
5926
  return null;
5758
5927
  }
5759
5928
  return /* @__PURE__ */ jsx(TouchableOpacity, {
5929
+ testID: "cometchat-switch-camera-button",
5760
5930
  disabled,
5761
5931
  style: [styles$8.iconButton, disabled && styles$8.iconButtonDisabled],
5762
5932
  onPress,
@@ -5789,6 +5959,7 @@ const SessionTimer = () => {
5789
5959
  return null;
5790
5960
  }
5791
5961
  return /* @__PURE__ */ jsx(Text, {
5962
+ testID: "cometchat-session-timer",
5792
5963
  style: [commonStyles.caption1Regular, styles$7.meetingTime],
5793
5964
  children: miliSecondsToMMSS(conferenceElapsedTime)
5794
5965
  });
@@ -5908,66 +6079,72 @@ const IdealTimeoutModal = ({ style = {} }) => {
5908
6079
  statusBarTranslucent: true,
5909
6080
  children: /* @__PURE__ */ jsx(TouchableWithoutFeedback, {
5910
6081
  onPress: handleOverlayPress,
6082
+ testID: "cometchat-idle-timeout-overlay",
5911
6083
  children: /* @__PURE__ */ jsx(Animated.View, {
5912
6084
  style: [styles$6.overlay, { opacity: fadeAnim }],
5913
- children: /* @__PURE__ */ jsx(TouchableWithoutFeedback, { children: /* @__PURE__ */ jsx(Animated.View, {
5914
- style: [
5915
- styles$6.modal,
5916
- style,
5917
- {
5918
- opacity: fadeAnim,
5919
- transform: [{ scale: scaleAnim }]
5920
- }
5921
- ],
5922
- children: /* @__PURE__ */ jsxs(View, {
5923
- style: styles$6.content,
5924
- children: [
5925
- /* @__PURE__ */ jsx(View, {
5926
- style: styles$6.timerIcon,
5927
- children: /* @__PURE__ */ jsx(Text, {
5928
- style: [commonStyles.heading3Bold, styles$6.timerText],
5929
- children: formattedTime
5930
- })
5931
- }),
5932
- /* @__PURE__ */ jsxs(View, {
5933
- style: styles$6.textContent,
5934
- children: [/* @__PURE__ */ jsx(Text, {
5935
- style: [commonStyles.heading2Medium, styles$6.title],
5936
- children: "Are you still there?"
5937
- }), /* @__PURE__ */ jsxs(Text, {
5938
- style: [commonStyles.bodyRegular, styles$6.subtitle],
5939
- children: [
5940
- "You are the only one here, so this call will end in less than ",
5941
- ceilMinutes,
5942
- " minute",
5943
- ceilMinutes > 1 ? "s" : "",
5944
- ". Do you want to stay in this call?"
5945
- ]
5946
- })]
5947
- }),
5948
- /* @__PURE__ */ jsxs(View, {
5949
- style: styles$6.actions,
5950
- children: [/* @__PURE__ */ jsx(TouchableOpacity, {
5951
- style: [styles$6.button, styles$6.buttonSecondary],
5952
- onPress: onStayInCall,
5953
- activeOpacity: .8,
5954
- children: /* @__PURE__ */ jsx(Text, {
5955
- style: [commonStyles.bodyMedium, styles$6.buttonSecondaryText],
5956
- children: "Stay on the call"
5957
- })
5958
- }), /* @__PURE__ */ jsx(TouchableOpacity, {
5959
- style: [styles$6.button, styles$6.buttonPrimary],
5960
- onPress: () => leaveSession(),
5961
- activeOpacity: .8,
6085
+ children: /* @__PURE__ */ jsx(TouchableWithoutFeedback, {
6086
+ testID: "cometchat-idle-timeout-modal",
6087
+ children: /* @__PURE__ */ jsx(Animated.View, {
6088
+ style: [
6089
+ styles$6.modal,
6090
+ style,
6091
+ {
6092
+ opacity: fadeAnim,
6093
+ transform: [{ scale: scaleAnim }]
6094
+ }
6095
+ ],
6096
+ children: /* @__PURE__ */ jsxs(View, {
6097
+ style: styles$6.content,
6098
+ children: [
6099
+ /* @__PURE__ */ jsx(View, {
6100
+ style: styles$6.timerIcon,
5962
6101
  children: /* @__PURE__ */ jsx(Text, {
5963
- style: [commonStyles.bodyMedium, styles$6.buttonPrimaryText],
5964
- children: "Leave now"
6102
+ style: [commonStyles.heading3Bold, styles$6.timerText],
6103
+ children: formattedTime
5965
6104
  })
5966
- })]
5967
- })
5968
- ]
6105
+ }),
6106
+ /* @__PURE__ */ jsxs(View, {
6107
+ style: styles$6.textContent,
6108
+ children: [/* @__PURE__ */ jsx(Text, {
6109
+ style: [commonStyles.heading2Medium, styles$6.title],
6110
+ children: "Are you still there?"
6111
+ }), /* @__PURE__ */ jsxs(Text, {
6112
+ style: [commonStyles.bodyRegular, styles$6.subtitle],
6113
+ children: [
6114
+ "You are the only one here, so this call will end in less than ",
6115
+ ceilMinutes,
6116
+ " minute",
6117
+ ceilMinutes > 1 ? "s" : "",
6118
+ ". Do you want to stay in this call?"
6119
+ ]
6120
+ })]
6121
+ }),
6122
+ /* @__PURE__ */ jsxs(View, {
6123
+ style: styles$6.actions,
6124
+ children: [/* @__PURE__ */ jsx(TouchableOpacity, {
6125
+ style: [styles$6.button, styles$6.buttonSecondary],
6126
+ onPress: onStayInCall,
6127
+ activeOpacity: .8,
6128
+ testID: "cometchat-idle-timeout-stay-button",
6129
+ children: /* @__PURE__ */ jsx(Text, {
6130
+ style: [commonStyles.bodyMedium, styles$6.buttonSecondaryText],
6131
+ children: "Stay on the call"
6132
+ })
6133
+ }), /* @__PURE__ */ jsx(TouchableOpacity, {
6134
+ style: [styles$6.button, styles$6.buttonPrimary],
6135
+ onPress: () => leaveSession(),
6136
+ activeOpacity: .8,
6137
+ testID: "cometchat-idle-timeout-leave-button",
6138
+ children: /* @__PURE__ */ jsx(Text, {
6139
+ style: [commonStyles.bodyMedium, styles$6.buttonPrimaryText],
6140
+ children: "Leave now"
6141
+ })
6142
+ })]
6143
+ })
6144
+ ]
6145
+ })
5969
6146
  })
5970
- }) })
6147
+ })
5971
6148
  })
5972
6149
  })
5973
6150
  });
@@ -6065,6 +6242,7 @@ const ShareInviteButton = () => {
6065
6242
  style: styles$5.shareButtonContainer,
6066
6243
  children: /* @__PURE__ */ jsxs(TouchableOpacity, {
6067
6244
  style: styles$5.shareButton,
6245
+ testID: "cometchat-share-invite-button",
6068
6246
  onPress: () => {
6069
6247
  eventBus.publish({ type: "onShareInviteButtonClicked" });
6070
6248
  },
@@ -6201,6 +6379,7 @@ const ParticipantList = () => {
6201
6379
  }), /* @__PURE__ */ jsx(TouchableOpacity, {
6202
6380
  onPress: toggleParticipantListVisible,
6203
6381
  accessibilityLabel: "Close participants list",
6382
+ testID: "cometchat-participant-list-close-button",
6204
6383
  children: /* @__PURE__ */ jsx(Icon_native_default, {
6205
6384
  name: "close",
6206
6385
  size: 24,
@@ -6222,7 +6401,8 @@ const ParticipantList = () => {
6222
6401
  style: styles$3.searchInput,
6223
6402
  value: searchTerm,
6224
6403
  onChangeText: setSearchTerm,
6225
- placeholderTextColor: "#858585"
6404
+ placeholderTextColor: "#858585",
6405
+ testID: "cometchat-participant-search-input"
6226
6406
  })]
6227
6407
  })
6228
6408
  }),
@@ -6476,6 +6656,7 @@ function ToastItemView({ toast, onDismiss }) {
6476
6656
  }), toast.action && /* @__PURE__ */ jsx(Pressable, {
6477
6657
  style: styles$2.actionButton,
6478
6658
  onPress: handleActionPress,
6659
+ testID: "cometchat-toast-action-button",
6479
6660
  children: /* @__PURE__ */ jsx(Text, {
6480
6661
  style: styles$2.actionText,
6481
6662
  children: toast.action.label
@@ -6534,7 +6715,11 @@ function CallUI(props) {
6534
6715
  };
6535
6716
  }, [props.sessionSettings]);
6536
6717
  useEffect(() => {
6537
- useBaseStore.setState({ sdkPlatform: Platform.OS });
6718
+ if (props.sessionSettings.sdkPlatform) {
6719
+ useBaseStore.setState({ sdkPlatform: props.sessionSettings.sdkPlatform });
6720
+ } else {
6721
+ useBaseStore.setState({ sdkPlatform: Platform.OS === "ios" ? "react-native-ios" : "react-native-android" });
6722
+ }
6538
6723
  startSession();
6539
6724
  }, []);
6540
6725
  useEffect(() => {
@@ -6684,10 +6869,10 @@ const convertLegacyCallSettingsToV5Props = (callSettings) => {
6684
6869
  if (cs.defaultAudioMode === "BLUETOOTH" || cs.defaultAudioMode === "EARPIECE" || cs.defaultAudioMode === "HEADPHONES" || cs.defaultAudioMode === "SPEAKER") {
6685
6870
  v5Props.audioMode = cs.defaultAudioMode;
6686
6871
  }
6687
- if (cs.mode === "SPOTLIGHT") {
6688
- v5Props.layout = "SPOTLIGHT";
6689
- } else {
6690
- v5Props.layout = "SIDEBAR";
6872
+ if (typeof cs.layout === "string") {
6873
+ v5Props.layout = cs.layout;
6874
+ } else if (cs.mode === "SPOTLIGHT" || cs.mode === "SIDEBAR") {
6875
+ v5Props.layout = cs.mode;
6691
6876
  }
6692
6877
  if (cs.idleTimeoutPeriod) {
6693
6878
  v5Props.idleTimeoutPeriodAfterPrompt = 6e4;
@@ -10687,7 +10872,7 @@ var CometChatCalls = class extends SessionMethodsCore {
10687
10872
  }
10688
10873
  this.appSettings = parsedAppSettings.output;
10689
10874
  this.isInitialized = true;
10690
- const savedUser = this.getSavedUser();
10875
+ const savedUser = await this.getSavedUser();
10691
10876
  if (savedUser) {
10692
10877
  let parsedUser;
10693
10878
  if (typeof savedUser === "string") {
@@ -10760,7 +10945,7 @@ var CometChatCalls = class extends SessionMethodsCore {
10760
10945
  const user = await this.authenticateWithToken(authToken);
10761
10946
  this.loginInProgress = false;
10762
10947
  this.loggedInUser = user;
10763
- this.saveUser(user);
10948
+ await this.saveUser(user);
10764
10949
  this.notifyLoginSuccess(user);
10765
10950
  return user;
10766
10951
  } catch (error) {
@@ -10808,7 +10993,7 @@ var CometChatCalls = class extends SessionMethodsCore {
10808
10993
  const user = await this.authenticateWithToken(authToken);
10809
10994
  this.loginInProgress = false;
10810
10995
  this.loggedInUser = user;
10811
- this.saveUser(user);
10996
+ await this.saveUser(user);
10812
10997
  this.notifyLoginSuccess(user);
10813
10998
  return user;
10814
10999
  } catch (error) {
@@ -10941,7 +11126,7 @@ var CometChatCalls = class extends SessionMethodsCore {
10941
11126
  appId
10942
11127
  },
10943
11128
  body: {
10944
- platform: "web",
11129
+ platform: "react-native",
10945
11130
  deviceId: this.generateDeviceId()
10946
11131
  }
10947
11132
  });
@@ -10962,7 +11147,7 @@ var CometChatCalls = class extends SessionMethodsCore {
10962
11147
  appId
10963
11148
  },
10964
11149
  body: {
10965
- platform: "web",
11150
+ platform: "react-native",
10966
11151
  deviceId: this.generateDeviceId()
10967
11152
  }
10968
11153
  });
@@ -11002,7 +11187,7 @@ var CometChatCalls = class extends SessionMethodsCore {
11002
11187
  }
11003
11188
  }
11004
11189
  this.loggedInUser = null;
11005
- this.clearSavedUser();
11190
+ await this.clearSavedUser();
11006
11191
  }
11007
11192
  static async callGenerateTokenAPI(sessionId, authToken) {
11008
11193
  const appId = this.appSettings?.appId || "";
@@ -11030,9 +11215,11 @@ var CometChatCalls = class extends SessionMethodsCore {
11030
11215
  baseURL: this.getBaseURL()
11031
11216
  });
11032
11217
  }
11033
- static saveUser(user) {
11218
+ static getStorageKey() {
11219
+ return `${this.appSettings?.appId}:common_store/user`;
11220
+ }
11221
+ static async saveUser(user) {
11034
11222
  try {
11035
- const key = `${this.appSettings?.appId}:common_store/user`;
11036
11223
  const userWithDefaults = {
11037
11224
  hasBlockedMe: false,
11038
11225
  blockedByMe: false,
@@ -11041,31 +11228,28 @@ var CometChatCalls = class extends SessionMethodsCore {
11041
11228
  role: user.role || "default",
11042
11229
  wsChannel: user.wsChannel || { identity: `[${this.appSettings?.appId}]${user.uid}` }
11043
11230
  };
11044
- localStorage.setItem(key, JSON.stringify(userWithDefaults));
11231
+ await AsyncStorage.setItem(this.getStorageKey(), JSON.stringify(userWithDefaults));
11045
11232
  } catch (error) {
11046
- console.warn("Failed to save user to localStorage:", error);
11233
+ console.warn("Failed to save user to AsyncStorage:", error);
11047
11234
  }
11048
11235
  }
11049
- static getSavedUser() {
11236
+ static async getSavedUser() {
11050
11237
  try {
11051
- const key = `${this.appSettings?.appId}:common_store/user`;
11052
- const savedUser = localStorage.getItem(key);
11053
- return savedUser ? savedUser : null;
11238
+ return await AsyncStorage.getItem(this.getStorageKey());
11054
11239
  } catch (error) {
11055
- console.warn("Failed to get saved user from localStorage:", error);
11240
+ console.warn("Failed to get saved user from AsyncStorage:", error);
11056
11241
  return null;
11057
11242
  }
11058
11243
  }
11059
- static clearSavedUser() {
11244
+ static async clearSavedUser() {
11060
11245
  try {
11061
- const key = `${this.appSettings?.appId}:common_store/user`;
11062
- localStorage.removeItem(key);
11246
+ await AsyncStorage.removeItem(this.getStorageKey());
11063
11247
  } catch (error) {
11064
- console.warn("Failed to clear saved user from localStorage:", error);
11248
+ console.warn("Failed to clear saved user from AsyncStorage:", error);
11065
11249
  }
11066
11250
  }
11067
11251
  static generateDeviceId() {
11068
- return "web_" + Math.random().toString(36).substr(2, 9);
11252
+ return "rn_" + Math.random().toString(36).substring(2, 11);
11069
11253
  }
11070
11254
  static createError(error) {
11071
11255
  if (error.errorCode && error.errorDescription) {
@@ -11131,6 +11315,13 @@ var CometChatCalls = class extends SessionMethodsCore {
11131
11315
  return eventBus.subscribe(eventType, listener, options);
11132
11316
  }
11133
11317
  /**
11318
+ * Sets the audio output mode (mobile only).
11319
+ * @param mode - The audio mode to set (e.g., 'SPEAKER', 'EARPIECE', 'BLUETOOTH', 'HEADPHONES').
11320
+ */
11321
+ static setAudioMode(mode) {
11322
+ setAudioMode(mode);
11323
+ }
11324
+ /**
11134
11325
  * Enables Picture-in-Picture (PIP) layout during the call.
11135
11326
  */
11136
11327
  static enablePictureInPictureLayout() {
@@ -11145,5 +11336,4 @@ var CometChatCalls = class extends SessionMethodsCore {
11145
11336
  };
11146
11337
 
11147
11338
  //#endregion
11148
- export { CometChatCalls };
11149
- //# sourceMappingURL=index.mjs.map
11339
+ export { CometChatCalls };