@stream-io/video-react-sdk 1.37.7 → 1.39.0

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.
Files changed (33) hide show
  1. package/CHANGELOG.md +4409 -0
  2. package/dist/css/embedded.css +1 -0
  3. package/dist/css/embedded.css.map +1 -1
  4. package/dist/css/styles.css +1 -0
  5. package/dist/css/styles.css.map +1 -1
  6. package/dist/embedded.cjs.js +60 -39
  7. package/dist/embedded.cjs.js.map +1 -1
  8. package/dist/embedded.es.js +62 -41
  9. package/dist/embedded.es.js.map +1 -1
  10. package/dist/index.cjs.js +60 -40
  11. package/dist/index.cjs.js.map +1 -1
  12. package/dist/index.es.js +62 -42
  13. package/dist/index.es.js.map +1 -1
  14. package/dist/src/components/CallParticipantsList/CallParticipantListingItem.d.ts +1 -1
  15. package/dist/src/core/components/Audio/Audio.d.ts +1 -4
  16. package/dist/src/core/components/ParticipantView/ParticipantView.d.ts +1 -1
  17. package/dist/src/translations/index.d.ts +1 -0
  18. package/package.json +9 -7
  19. package/src/components/CallParticipantsList/CallParticipantListingItem.tsx +50 -46
  20. package/src/components/CallParticipantsList/CallParticipantsList.tsx +17 -6
  21. package/src/components/DeviceSettings/AudioVolumeIndicator.tsx +8 -4
  22. package/src/components/Search/SearchInput.tsx +4 -2
  23. package/src/core/components/Audio/Audio.tsx +4 -4
  24. package/src/core/components/ParticipantView/DefaultParticipantViewUI.tsx +8 -0
  25. package/src/core/components/ParticipantView/ParticipantView.tsx +4 -3
  26. package/src/core/components/Video/BaseVideo.tsx +3 -1
  27. package/src/core/components/Video/Video.tsx +5 -2
  28. package/src/embedded/hooks/useWakeLock.ts +1 -0
  29. package/src/embedded/livestream/viewer/ViewerLobby.tsx +5 -6
  30. package/src/hooks/useDragToScroll.ts +9 -4
  31. package/src/hooks/useScrollPosition.ts +2 -2
  32. package/src/translations/en.json +1 -0
  33. package/src/wrappers/LivestreamPlayer/LivestreamPlayer.tsx +5 -6
package/dist/index.es.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import { hasAudio, hasScreenShareAudio, hasVideo, hasScreenShare, OwnCapability, Browsers, VisibilityState, hasPausedTrack, createSoundDetector, SfuModels, isPinned, name, NoiseCancellationSettingsModeEnum, CallingState, paginatedLayoutSortPreset, combineComparators, screenSharing, speakerLayoutSortPreset, CallTypes, defaultSortPreset, humanize, setSdkInfo } from '@stream-io/video-client';
2
2
  export * from '@stream-io/video-client';
3
- import { useCall, useCallStateHooks, useI18n, Restricted, useToggleCallRecording, useConnectedUser, useIsAudioConnecting, StreamCallProvider, StreamVideoProvider, useStreamVideoClient, useEffectEvent } from '@stream-io/video-react-bindings';
3
+ import { useCall, useCallStateHooks, useI18n, Restricted, useToggleCallRecording, useConnectedUser, useEffectEvent, useIsAudioConnecting, useIsVideoConnecting, StreamCallProvider, StreamVideoProvider, useStreamVideoClient } from '@stream-io/video-react-bindings';
4
4
  export * from '@stream-io/video-react-bindings';
5
5
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
6
- import { useState, useEffect, Fragment as Fragment$1, createContext, useContext, useCallback, useMemo, useRef, isValidElement, forwardRef, useLayoutEffect, lazy, Suspense } from 'react';
6
+ import { memo, useState, useEffect, Fragment as Fragment$1, createContext, useContext, useCallback, useMemo, useRef, isValidElement, forwardRef, useLayoutEffect, lazy, Suspense } from 'react';
7
7
  import { useFloating, offset, shift, flip, size, autoUpdate, FloatingOverlay, FloatingPortal, arrow, FloatingArrow, useListItem, useListNavigation, useTypeahead, useClick, useDismiss, useRole, useInteractions, FloatingFocusManager, FloatingList, useHover } from '@floating-ui/react';
8
8
  import clsx from 'clsx';
9
9
 
10
- const Audio = ({ participant, trackType = 'audioTrack', ...rest }) => {
10
+ const Audio = memo(function Audio({ participant, trackType = 'audioTrack', ...rest }) {
11
11
  const call = useCall();
12
12
  const [audioElement, setAudioElement] = useState(null);
13
13
  const { userId, sessionId } = participant;
@@ -20,7 +20,7 @@ const Audio = ({ participant, trackType = 'audioTrack', ...rest }) => {
20
20
  };
21
21
  }, [call, sessionId, audioElement, trackType]);
22
22
  return (jsx("audio", { autoPlay: true, ...rest, ref: setAudioElement, "data-user-id": userId, "data-session-id": sessionId, "data-track-type": trackType }));
23
- };
23
+ });
24
24
  Audio.displayName = 'Audio';
25
25
 
26
26
  const ParticipantsAudio = (props) => {
@@ -108,7 +108,7 @@ const useVerticalScrollPosition = (scrollElement, threshold = SCROLL_THRESHOLD)
108
108
  };
109
109
  const resizeObserver = new ResizeObserver(scrollHandler);
110
110
  resizeObserver.observe(scrollElement);
111
- scrollElement.addEventListener('scroll', scrollHandler);
111
+ scrollElement.addEventListener('scroll', scrollHandler, { passive: true });
112
112
  return () => {
113
113
  scrollElement.removeEventListener('scroll', scrollHandler);
114
114
  resizeObserver.disconnect();
@@ -136,7 +136,7 @@ const useHorizontalScrollPosition = (scrollElement, threshold = SCROLL_THRESHOLD
136
136
  };
137
137
  const resizeObserver = new ResizeObserver(scrollHandler);
138
138
  resizeObserver.observe(scrollElement);
139
- scrollElement.addEventListener('scroll', scrollHandler);
139
+ scrollElement.addEventListener('scroll', scrollHandler, { passive: true });
140
140
  return () => {
141
141
  scrollElement.removeEventListener('scroll', scrollHandler);
142
142
  resizeObserver.disconnect();
@@ -357,6 +357,7 @@ const useModeration = (options) => {
357
357
  * @param options - Options for customizing the drag-to-scroll behavior.
358
358
  */
359
359
  function useDragToScroll(element, options = {}) {
360
+ const { enabled, decay = 0.95, minVelocity = 0.5, dragThreshold = 5, } = options;
360
361
  const stateRef = useRef({
361
362
  isDragging: false,
362
363
  isPointerActive: false,
@@ -369,9 +370,8 @@ function useDragToScroll(element, options = {}) {
369
370
  startY: 0,
370
371
  });
371
372
  useEffect(() => {
372
- if (!element || !options.enabled)
373
+ if (!element || !enabled)
373
374
  return;
374
- const { decay = 0.95, minVelocity = 0.5, dragThreshold = 5 } = options;
375
375
  const state = stateRef.current;
376
376
  const stopMomentum = () => {
377
377
  if (state.rafId) {
@@ -455,7 +455,7 @@ function useDragToScroll(element, options = {}) {
455
455
  window.removeEventListener('pointercancel', onPointerUpOrCancel);
456
456
  stopMomentum();
457
457
  };
458
- }, [element, options]);
458
+ }, [element, enabled, decay, minVelocity, dragThreshold]);
459
459
  }
460
460
 
461
461
  var MenuVisualType;
@@ -685,9 +685,10 @@ const BaseVideo = forwardRef(function BaseVideo({ stream, ...rest }, ref) {
685
685
  if (stream === videoElement.srcObject)
686
686
  return;
687
687
  videoElement.srcObject = stream;
688
+ let timeoutId;
688
689
  if (Browsers.isSafari() || Browsers.isFirefox()) {
689
690
  // Firefox and Safari have some timing issue
690
- setTimeout(() => {
691
+ timeoutId = setTimeout(() => {
691
692
  videoElement.srcObject = stream;
692
693
  videoElement.play().catch((e) => {
693
694
  console.error(`Failed to play stream`, e);
@@ -695,6 +696,7 @@ const BaseVideo = forwardRef(function BaseVideo({ stream, ...rest }, ref) {
695
696
  }, 0);
696
697
  }
697
698
  return () => {
699
+ clearTimeout(timeoutId);
698
700
  videoElement.pause();
699
701
  videoElement.srcObject = null;
700
702
  };
@@ -731,6 +733,7 @@ const DefaultPictureInPicturePlaceholder = forwardRef(function DefaultPictureInP
731
733
  return (jsx(BaseVideoPlaceholder, { ref: ref, ...props, children: t('Video is playing in a popup') }));
732
734
  });
733
735
 
736
+ const ABSOLUTE_POSITION_STYLE = { position: 'absolute' };
734
737
  const Video$1 = ({ enabled = true, mirror, trackType, participant, className, VideoPlaceholder = DefaultVideoPlaceholder, PictureInPicturePlaceholder = DefaultPictureInPicturePlaceholder, refs, ...rest }) => {
735
738
  const { sessionId, videoStream, screenShareStream, viewportVisibilityState, isLocalParticipant, userId, } = participant;
736
739
  const call = useCall();
@@ -803,7 +806,7 @@ const Video$1 = ({ enabled = true, mirror, trackType, participant, className, Vi
803
806
  }), "data-user-id": userId, "data-session-id": sessionId, ref: (element) => {
804
807
  setVideoElement(element);
805
808
  refs?.setVideoElement?.(element);
806
- } })), isPiP && PictureInPicturePlaceholder && (jsx(PictureInPicturePlaceholder, { style: { position: 'absolute' }, participant: participant })), (hasNoVideoOrInvisible || isVideoPaused) && VideoPlaceholder && (jsx(VideoPlaceholder, { style: { position: 'absolute' }, participant: participant, ref: refs?.setVideoPlaceholderElement }))] }));
809
+ } })), isPiP && PictureInPicturePlaceholder && (jsx(PictureInPicturePlaceholder, { style: ABSOLUTE_POSITION_STYLE, participant: participant })), (hasNoVideoOrInvisible || isVideoPaused) && VideoPlaceholder && (jsx(VideoPlaceholder, { style: ABSOLUTE_POSITION_STYLE, participant: participant, ref: refs?.setVideoPlaceholderElement }))] }));
807
810
  };
808
811
  Video$1.displayName = 'Video';
809
812
 
@@ -1216,16 +1219,20 @@ const ScreenShareButton = (props) => {
1216
1219
  const AudioVolumeIndicator = () => {
1217
1220
  const { useMicrophoneState } = useCallStateHooks();
1218
1221
  const { isEnabled, mediaStream } = useMicrophoneState();
1219
- const [audioLevel, setAudioLevel] = useState(0);
1222
+ const barRef = useRef(null);
1220
1223
  useEffect(() => {
1221
1224
  if (!isEnabled || !mediaStream)
1222
1225
  return;
1223
- const disposeSoundDetector = createSoundDetector(mediaStream, ({ audioLevel: al }) => setAudioLevel(al), { detectionFrequencyInMs: 80, destroyStreamOnStop: false });
1226
+ const disposeSoundDetector = createSoundDetector(mediaStream, ({ audioLevel: al }) => {
1227
+ if (barRef.current) {
1228
+ barRef.current.style.transform = `scaleX(${al / 100})`;
1229
+ }
1230
+ }, { detectionFrequencyInMs: 80, destroyStreamOnStop: false });
1224
1231
  return () => {
1225
1232
  disposeSoundDetector().catch(console.error);
1226
1233
  };
1227
1234
  }, [isEnabled, mediaStream]);
1228
- return (jsxs("div", { className: "str-video__audio-volume-indicator", children: [jsx(Icon, { icon: isEnabled ? 'mic' : 'mic-off' }), jsx("div", { className: "str-video__audio-volume-indicator__bar", children: jsx("div", { className: "str-video__audio-volume-indicator__bar-value", style: { transform: `scaleX(${audioLevel / 100})` } }) })] }));
1235
+ return (jsxs("div", { className: "str-video__audio-volume-indicator", children: [jsx(Icon, { icon: isEnabled ? 'mic' : 'mic-off' }), jsx("div", { className: "str-video__audio-volume-indicator__bar", children: jsx("div", { ref: barRef, className: "str-video__audio-volume-indicator__bar-value" }) })] }));
1229
1236
  };
1230
1237
 
1231
1238
  const LEVEL_BARS = 5;
@@ -1399,7 +1406,7 @@ const SpeakerTest = (props) => {
1399
1406
  const audioElementRef = useRef(null);
1400
1407
  const [isPlaying, setIsPlaying] = useState(false);
1401
1408
  const { t } = useI18n();
1402
- const { audioUrl = `https://unpkg.com/${"@stream-io/video-react-sdk"}@${"1.37.7"}/assets/piano.mp3`, } = props;
1409
+ const { audioUrl = `https://unpkg.com/${"@stream-io/video-react-sdk"}@${"1.39.0"}/assets/piano.mp3`, } = props;
1403
1410
  // Update audio output device when selection changes
1404
1411
  useEffect(() => {
1405
1412
  const audio = audioElementRef.current;
@@ -1903,13 +1910,14 @@ const CallParticipantListHeader = ({ onClose, }) => {
1903
1910
  return (jsxs("div", { className: "str-video__participant-list-header", children: [jsxs("div", { className: "str-video__participant-list-header__title", children: [t('Participants'), ' ', jsxs("span", { className: "str-video__participant-list-header__title-count", children: ["[", participants.length, "]"] }), anonymousParticipantCount > 0 && (jsx("span", { className: "str-video__participant-list-header__title-anonymous", children: t('Anonymous', { count: anonymousParticipantCount }) }))] }), jsx(IconButton, { onClick: onClose, className: "str-video__participant-list-header__close-button", icon: "close" })] }));
1904
1911
  };
1905
1912
 
1906
- const CallParticipantListingItem = ({ participant, DisplayName = DefaultDisplayName, }) => {
1913
+ const CallParticipantListingItem = memo(function CallParticipantListingItem({ participant, DisplayName = DefaultDisplayName, }) {
1907
1914
  const isAudioOn = hasAudio(participant);
1908
1915
  const isVideoOn = hasVideo(participant);
1909
1916
  const isPinnedOn = isPinned(participant);
1910
1917
  const { t } = useI18n();
1911
1918
  return (jsxs("div", { className: "str-video__participant-listing-item", children: [jsx(Avatar, { name: participant.name, imageSrc: participant.image }), jsx(DisplayName, { participant: participant }), jsxs("div", { className: "str-video__participant-listing-item__media-indicator-group", children: [jsx(MediaIndicator, { title: isAudioOn ? t('Microphone on') : t('Microphone off'), className: clsx('str-video__participant-listing-item__icon', `str-video__participant-listing-item__icon-${isAudioOn ? 'mic' : 'mic-off'}`) }), jsx(MediaIndicator, { title: isVideoOn ? t('Camera on') : t('Camera off'), className: clsx('str-video__participant-listing-item__icon', `str-video__participant-listing-item__icon-${isVideoOn ? 'camera' : 'camera-off'}`) }), isPinnedOn && (jsx(MediaIndicator, { title: t('Pinned'), className: clsx('str-video__participant-listing-item__icon', 'str-video__participant-listing-item__icon-pinned') })), jsx(MenuToggle, { placement: "bottom-end", ToggleButton: ToggleButton$2, children: jsx(ParticipantViewContext.Provider, { value: { participant, trackType: 'none' }, children: jsx(ParticipantActionsContextMenu, {}) }) })] })] }));
1912
- };
1919
+ });
1920
+ CallParticipantListingItem.displayName = 'CallParticipantListingItem';
1913
1921
  const MediaIndicator = (props) => (jsx(WithTooltip, { ...props }));
1914
1922
  const DefaultDisplayName = ({ participant }) => {
1915
1923
  const connectedUser = useConnectedUser();
@@ -1941,18 +1949,19 @@ const EmptyParticipantSearchList = () => {
1941
1949
 
1942
1950
  const SearchInput = ({ exitSearch, isActive, ...rest }) => {
1943
1951
  const [inputElement, setInputElement] = useState(null);
1952
+ const onExitSearch = useEffectEvent(exitSearch);
1944
1953
  useEffect(() => {
1945
1954
  if (!inputElement)
1946
1955
  return;
1947
1956
  const handleKeyDown = (e) => {
1948
1957
  if (e.key.toLowerCase() === 'escape')
1949
- exitSearch();
1958
+ onExitSearch();
1950
1959
  };
1951
1960
  inputElement.addEventListener('keydown', handleKeyDown);
1952
1961
  return () => {
1953
1962
  inputElement.removeEventListener('keydown', handleKeyDown);
1954
1963
  };
1955
- }, [exitSearch, inputElement]);
1964
+ }, [inputElement]);
1956
1965
  return (jsxs("div", { className: clsx('str-video__search-input__container', {
1957
1966
  'str-video__search-input__container--active': isActive,
1958
1967
  }), children: [jsx("input", { placeholder: "Search", ...rest, ref: setInputElement }), isActive ? (jsx("button", { className: "str-video__search-input__clear-btn", onClick: exitSearch, children: jsx("span", { className: "str-video__search-input__icon--active" }) })) : (jsx("span", { className: "str-video__search-input__icon" }))] }));
@@ -2023,10 +2032,19 @@ const CallParticipantListContentHeader = ({ userListType, setUserListType, }) =>
2023
2032
  const ActiveUsersSearchResults = ({ searchQuery, activeUsersSearchFn: activeUsersSearchFnFromProps, debounceSearchInterval, }) => {
2024
2033
  const { useParticipants } = useCallStateHooks();
2025
2034
  const participants = useParticipants({ sortBy: name });
2035
+ // cache: each distinct name is normalized once
2036
+ const normalizedNameCache = useRef(null);
2026
2037
  const activeUsersSearchFn = useCallback(async (queryString) => {
2027
2038
  const normalizedQuery = normalizeString(queryString);
2028
- const queryRegExp = new RegExp(normalizedQuery, 'i');
2029
- return participants.filter((p) => normalizeString(p.name).match(queryRegExp));
2039
+ const cache = (normalizedNameCache.current ??= new Map());
2040
+ return participants.filter((p) => {
2041
+ let normalizedName = cache.get(p.name);
2042
+ if (normalizedName === undefined) {
2043
+ normalizedName = normalizeString(p.name);
2044
+ cache.set(p.name, normalizedName);
2045
+ }
2046
+ return normalizedName.includes(normalizedQuery);
2047
+ });
2030
2048
  }, [participants]);
2031
2049
  const { searchQueryInProgress, searchResults } = useSearch({
2032
2050
  searchFn: activeUsersSearchFnFromProps ?? activeUsersSearchFn,
@@ -2039,8 +2057,8 @@ const BlockedUsersSearchResults = ({ blockedUsersSearchFn: blockedUsersSearchFnF
2039
2057
  const { useCallBlockedUserIds } = useCallStateHooks();
2040
2058
  const blockedUsers = useCallBlockedUserIds();
2041
2059
  const blockedUsersSearchFn = useCallback(async (queryString) => {
2042
- const queryRegExp = new RegExp(queryString, 'i');
2043
- return blockedUsers.filter((userId) => userId.match(queryRegExp));
2060
+ const normalizedQuery = queryString.toLowerCase();
2061
+ return blockedUsers.filter((userId) => userId.toLowerCase().includes(normalizedQuery));
2044
2062
  }, [blockedUsers]);
2045
2063
  const { searchQueryInProgress, searchResults } = useSearch({
2046
2064
  searchFn: blockedUsersSearchFnFromProps ?? blockedUsersSearchFn,
@@ -2396,7 +2414,8 @@ const ParticipantDetails = ({ indicatorsVisible = true, }) => {
2396
2414
  const canUnpin = !!pin && pin.isLocalPin;
2397
2415
  const isTrackPaused = trackType !== 'none' ? hasPausedTrack(participant, trackType) : false;
2398
2416
  const isAudioConnecting = useIsAudioConnecting(participant);
2399
- return (jsxs(Fragment, { children: [jsx("div", { className: "str-video__participant-details", children: jsxs("div", { className: "str-video__participant-details__name", children: [name || userId, indicatorsVisible && isAudioConnecting && (jsx(LoadingIndicator, { className: "str-video__participant-details__name--audio-connecting", tooltip: t('Audio is connecting...') })), indicatorsVisible && !hasAudioTrack && (jsx("span", { className: "str-video__participant-details__name--audio-muted" })), indicatorsVisible && !hasVideoTrack && (jsx("span", { className: "str-video__participant-details__name--video-muted" })), indicatorsVisible && isTrackPaused && (jsx("span", { title: t('Video paused due to insufficient bandwidth'), className: "str-video__participant-details__name--track-paused" })), indicatorsVisible && pin && (jsx("span", { title: canUnpin ? t('Unpin') : t('Pinned'), onClick: canUnpin ? () => call?.unpin(sessionId) : undefined, className: "str-video__participant-details__name--pinned" })), indicatorsVisible && jsx(SpeechIndicator, {})] }) }), indicatorsVisible && (jsx(Notification, { isVisible: isLocalParticipant &&
2417
+ const isVideoConnecting = useIsVideoConnecting(participant);
2418
+ return (jsxs(Fragment, { children: [jsx("div", { className: "str-video__participant-details", children: jsxs("div", { className: "str-video__participant-details__name", children: [name || userId, indicatorsVisible && isAudioConnecting && (jsx(LoadingIndicator, { className: "str-video__participant-details__name--audio-connecting", tooltip: t('Audio is connecting...') })), indicatorsVisible && !hasAudioTrack && (jsx("span", { className: "str-video__participant-details__name--audio-muted" })), indicatorsVisible && isVideoConnecting && (jsx(LoadingIndicator, { className: "str-video__participant-details__name--video-connecting", tooltip: t('Video is connecting...') })), indicatorsVisible && !hasVideoTrack && (jsx("span", { className: "str-video__participant-details__name--video-muted" })), indicatorsVisible && isTrackPaused && (jsx("span", { title: t('Video paused due to insufficient bandwidth'), className: "str-video__participant-details__name--track-paused" })), indicatorsVisible && pin && (jsx("span", { title: canUnpin ? t('Unpin') : t('Pinned'), onClick: canUnpin ? () => call?.unpin(sessionId) : undefined, className: "str-video__participant-details__name--pinned" })), indicatorsVisible && jsx(SpeechIndicator, {})] }) }), indicatorsVisible && (jsx(Notification, { isVisible: isLocalParticipant &&
2400
2419
  connectionQuality === SfuModels.ConnectionQuality.POOR, message: t('Poor connection quality'), children: connectionQualityAsString && (jsx("span", { className: clsx('str-video__participant-details__connection-quality', `str-video__participant-details__connection-quality--${connectionQualityAsString}`), title: connectionQualityAsString })) }))] }));
2401
2420
  };
2402
2421
  const SpeechIndicator = () => {
@@ -2405,7 +2424,7 @@ const SpeechIndicator = () => {
2405
2424
  return (jsxs("span", { className: clsx('str-video__speech-indicator', isSpeaking && 'str-video__speech-indicator--speaking', isDominantSpeaker && 'str-video__speech-indicator--dominant'), children: [jsx("span", { className: "str-video__speech-indicator__bar" }), jsx("span", { className: "str-video__speech-indicator__bar" }), jsx("span", { className: "str-video__speech-indicator__bar" })] }));
2406
2425
  };
2407
2426
 
2408
- const ParticipantView = forwardRef(function ParticipantView({ participant, trackType = 'videoTrack', mirror, muteAudio, refs: { setVideoElement, setVideoPlaceholderElement } = {}, className, VideoPlaceholder, PictureInPicturePlaceholder, ParticipantViewUI = DefaultParticipantViewUI, }, ref) {
2427
+ const ParticipantView = memo(forwardRef(function ParticipantView({ participant, trackType = 'videoTrack', mirror, muteAudio, refs: { setVideoElement, setVideoPlaceholderElement } = {}, className, VideoPlaceholder, PictureInPicturePlaceholder, ParticipantViewUI = DefaultParticipantViewUI, }, ref) {
2409
2428
  const { isLocalParticipant, isSpeaking, isDominantSpeaker, sessionId } = participant;
2410
2429
  const hasAudioTrack = hasAudio(participant);
2411
2430
  const hasVideoTrack = hasVideo(participant);
@@ -2449,7 +2468,7 @@ const ParticipantView = forwardRef(function ParticipantView({ participant, track
2449
2468
  }, className: clsx('str-video__participant-view', isDominantSpeaker && 'str-video__participant-view--dominant-speaker', isSpeaking && 'str-video__participant-view--speaking', !hasVideoTrack && 'str-video__participant-view--no-video', !hasAudioTrack && 'str-video__participant-view--no-audio', className), children: jsxs(ParticipantViewContext.Provider, { value: participantViewContextValue, children: [!isLocalParticipant && !muteAudio && (jsxs(Fragment, { children: [hasAudioTrack && (jsx(Audio, { participant: participant, trackType: "audioTrack" })), hasScreenShareAudioTrack && (jsx(Audio, { participant: participant, trackType: "screenShareAudioTrack" }))] })), jsx(Video$1, { VideoPlaceholder: VideoPlaceholder, PictureInPicturePlaceholder: PictureInPicturePlaceholder, participant: participant, trackType: trackType, refs: videoRefs, enabled: isLocalParticipant ||
2450
2469
  trackType !== 'videoTrack' ||
2451
2470
  isParticipantVideoEnabled(participant.sessionId), mirror: mirror, autoPlay: true }), isComponentType(ParticipantViewUI) ? (jsx(ParticipantViewUI, {})) : (ParticipantViewUI)] }) }));
2452
- });
2471
+ }));
2453
2472
  ParticipantView.displayName = 'ParticipantView';
2454
2473
 
2455
2474
  // re-exporting the StreamCallProvider as StreamCall
@@ -2576,6 +2595,7 @@ var en = {
2576
2595
  "Poor connection quality": "Poor connection quality. Please check your internet connection.",
2577
2596
  "Video paused due to insufficient bandwidth": "Video paused due to insufficient bandwidth",
2578
2597
  "Audio is connecting...": "Audio is connecting...",
2598
+ "Video is connecting...": "Video is connecting...",
2579
2599
  Participants: Participants,
2580
2600
  Anonymous: Anonymous,
2581
2601
  "No participants found": "No participants found",
@@ -2666,20 +2686,20 @@ function checkConditions(obj, conditions) {
2666
2686
  const eqOperatorValue = isDateString(eqOperator.$eq)
2667
2687
  ? new Date(eqOperator.$eq).getTime()
2668
2688
  : eqOperator.$eq;
2669
- match && (match = eqOperatorValue === value);
2689
+ match &&= eqOperatorValue === value;
2670
2690
  }
2671
2691
  else if (maybeOperator && '$neq' in operator) {
2672
2692
  const neqOperator = operator;
2673
- match && (match = neqOperator.$neq !== value);
2693
+ match &&= neqOperator.$neq !== value;
2674
2694
  }
2675
2695
  else if (maybeOperator && '$in' in operator) {
2676
2696
  const inOperator = operator;
2677
- match && (match = inOperator.$in.includes(value));
2697
+ match &&= inOperator.$in.includes(value);
2678
2698
  }
2679
2699
  else if (maybeOperator && '$contains' in operator) {
2680
2700
  if (Array.isArray(value)) {
2681
2701
  const containsOperator = operator;
2682
- match && (match = value.includes(containsOperator.$contains));
2702
+ match &&= value.includes(containsOperator.$contains);
2683
2703
  }
2684
2704
  else {
2685
2705
  match = false;
@@ -2690,35 +2710,35 @@ function checkConditions(obj, conditions) {
2690
2710
  const gtOperatorValue = isDateString(gtOperator.$gt)
2691
2711
  ? new Date(gtOperator.$gt).getTime()
2692
2712
  : gtOperator.$gt;
2693
- match && (match = value > gtOperatorValue);
2713
+ match &&= value > gtOperatorValue;
2694
2714
  }
2695
2715
  else if (maybeOperator && '$gte' in operator) {
2696
2716
  const gteOperator = operator;
2697
2717
  const gteOperatorValue = isDateString(gteOperator.$gte)
2698
2718
  ? new Date(gteOperator.$gte).getTime()
2699
2719
  : gteOperator.$gte;
2700
- match && (match = value >= gteOperatorValue);
2720
+ match &&= value >= gteOperatorValue;
2701
2721
  }
2702
2722
  else if (maybeOperator && '$lt' in operator) {
2703
2723
  const ltOperator = operator;
2704
2724
  const ltOperatorValue = isDateString(ltOperator.$lt)
2705
2725
  ? new Date(ltOperator.$lt).getTime()
2706
2726
  : ltOperator.$lt;
2707
- match && (match = value < ltOperatorValue);
2727
+ match &&= value < ltOperatorValue;
2708
2728
  }
2709
2729
  else if (maybeOperator && '$lte' in operator) {
2710
2730
  const lteOperator = operator;
2711
2731
  const lteOperatorValue = isDateString(lteOperator.$lte)
2712
2732
  ? new Date(lteOperator.$lte).getTime()
2713
2733
  : lteOperator.$lte;
2714
- match && (match = value <= lteOperatorValue);
2734
+ match &&= value <= lteOperatorValue;
2715
2735
  // } else if (maybeOperator && '$autocomplete' in operator) {
2716
2736
  // TODO: regexp solution maybe?
2717
2737
  // match &&= false;
2718
2738
  }
2719
2739
  else {
2720
2740
  const eqValue = operator;
2721
- match && (match = eqValue === value);
2741
+ match &&= eqValue === value;
2722
2742
  }
2723
2743
  if (!match) {
2724
2744
  return false;
@@ -3248,12 +3268,12 @@ const useCanJoinEarly = () => {
3248
3268
  const joinAheadTimeSeconds = settings?.backstage.join_ahead_time_seconds;
3249
3269
  const [canJoinEarly, setCanJoinEarly] = useState(() => checkCanJoinEarly(startsAt, joinAheadTimeSeconds));
3250
3270
  useEffect(() => {
3251
- if (!canJoinEarly) {
3252
- const handle = setInterval(() => {
3253
- setCanJoinEarly(checkCanJoinEarly(startsAt, joinAheadTimeSeconds));
3254
- }, 1000);
3255
- return () => clearInterval(handle);
3256
- }
3271
+ if (canJoinEarly)
3272
+ return;
3273
+ const handle = setInterval(() => {
3274
+ setCanJoinEarly(checkCanJoinEarly(startsAt, joinAheadTimeSeconds));
3275
+ }, 1000);
3276
+ return () => clearInterval(handle);
3257
3277
  }, [canJoinEarly, startsAt, joinAheadTimeSeconds]);
3258
3278
  return canJoinEarly;
3259
3279
  };
@@ -3264,7 +3284,7 @@ const checkCanJoinEarly = (startsAt, joinAheadTimeSeconds) => {
3264
3284
  return Date.now() >= +startsAt - (joinAheadTimeSeconds ?? 0) * 1000;
3265
3285
  };
3266
3286
 
3267
- const [major, minor, patch] = ("1.37.7").split('.');
3287
+ const [major, minor, patch] = ("1.39.0").split('.');
3268
3288
  setSdkInfo({
3269
3289
  type: SfuModels.SdkType.REACT,
3270
3290
  major,