@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
@@ -1,11 +1,11 @@
1
1
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
2
- import { useState, useEffect, Fragment as Fragment$1, createContext, useContext, useCallback, useMemo, useRef, isValidElement, forwardRef, useLayoutEffect, lazy, Suspense } from 'react';
3
- import { useCall, useCallStateHooks, useI18n, Restricted, useToggleCallRecording, useConnectedUser, useIsAudioConnecting, StreamCallProvider, StreamVideoProvider, useEffectEvent } from '@stream-io/video-react-bindings';
2
+ import { memo, useState, useEffect, Fragment as Fragment$1, createContext, useContext, useCallback, useMemo, useRef, isValidElement, forwardRef, useLayoutEffect, lazy, Suspense } from 'react';
3
+ import { useCall, useCallStateHooks, useI18n, Restricted, useToggleCallRecording, useConnectedUser, useEffectEvent, useIsAudioConnecting, useIsVideoConnecting, StreamCallProvider, StreamVideoProvider } from '@stream-io/video-react-bindings';
4
4
  import { hasAudio, hasScreenShareAudio, hasVideo, hasScreenShare, OwnCapability, Browsers, VisibilityState, hasPausedTrack, createSoundDetector, SfuModels, isPinned, name, NoiseCancellationSettingsModeEnum, CallingState, combineComparators, screenSharing, speakerLayoutSortPreset, paginatedLayoutSortPreset, CallTypes, defaultSortPreset, humanize, StreamVideoClient } from '@stream-io/video-client';
5
5
  import { useFloating, offset, shift, flip, size, autoUpdate, FloatingOverlay, FloatingPortal, arrow, FloatingArrow, useListItem, useListNavigation, useTypeahead, useClick, useDismiss, useRole, useInteractions, FloatingFocusManager, FloatingList } from '@floating-ui/react';
6
6
  import clsx from 'clsx';
7
7
 
8
- const Audio = ({ participant, trackType = 'audioTrack', ...rest }) => {
8
+ const Audio = memo(function Audio({ participant, trackType = 'audioTrack', ...rest }) {
9
9
  const call = useCall();
10
10
  const [audioElement, setAudioElement] = useState(null);
11
11
  const { userId, sessionId } = participant;
@@ -18,7 +18,7 @@ const Audio = ({ participant, trackType = 'audioTrack', ...rest }) => {
18
18
  };
19
19
  }, [call, sessionId, audioElement, trackType]);
20
20
  return (jsx("audio", { autoPlay: true, ...rest, ref: setAudioElement, "data-user-id": userId, "data-session-id": sessionId, "data-track-type": trackType }));
21
- };
21
+ });
22
22
  Audio.displayName = 'Audio';
23
23
 
24
24
  const ParticipantsAudio = (props) => {
@@ -94,7 +94,7 @@ const useVerticalScrollPosition = (scrollElement, threshold = SCROLL_THRESHOLD)
94
94
  };
95
95
  const resizeObserver = new ResizeObserver(scrollHandler);
96
96
  resizeObserver.observe(scrollElement);
97
- scrollElement.addEventListener('scroll', scrollHandler);
97
+ scrollElement.addEventListener('scroll', scrollHandler, { passive: true });
98
98
  return () => {
99
99
  scrollElement.removeEventListener('scroll', scrollHandler);
100
100
  resizeObserver.disconnect();
@@ -122,7 +122,7 @@ const useHorizontalScrollPosition = (scrollElement, threshold = SCROLL_THRESHOLD
122
122
  };
123
123
  const resizeObserver = new ResizeObserver(scrollHandler);
124
124
  resizeObserver.observe(scrollElement);
125
- scrollElement.addEventListener('scroll', scrollHandler);
125
+ scrollElement.addEventListener('scroll', scrollHandler, { passive: true });
126
126
  return () => {
127
127
  scrollElement.removeEventListener('scroll', scrollHandler);
128
128
  resizeObserver.disconnect();
@@ -213,6 +213,7 @@ function useDeviceList(devices, selectedDeviceId) {
213
213
  * @param options - Options for customizing the drag-to-scroll behavior.
214
214
  */
215
215
  function useDragToScroll(element, options = {}) {
216
+ const { enabled, decay = 0.95, minVelocity = 0.5, dragThreshold = 5, } = options;
216
217
  const stateRef = useRef({
217
218
  isDragging: false,
218
219
  isPointerActive: false,
@@ -225,9 +226,8 @@ function useDragToScroll(element, options = {}) {
225
226
  startY: 0,
226
227
  });
227
228
  useEffect(() => {
228
- if (!element || !options.enabled)
229
+ if (!element || !enabled)
229
230
  return;
230
- const { decay = 0.95, minVelocity = 0.5, dragThreshold = 5 } = options;
231
231
  const state = stateRef.current;
232
232
  const stopMomentum = () => {
233
233
  if (state.rafId) {
@@ -311,7 +311,7 @@ function useDragToScroll(element, options = {}) {
311
311
  window.removeEventListener('pointercancel', onPointerUpOrCancel);
312
312
  stopMomentum();
313
313
  };
314
- }, [element, options]);
314
+ }, [element, enabled, decay, minVelocity, dragThreshold]);
315
315
  }
316
316
 
317
317
  var MenuVisualType;
@@ -541,9 +541,10 @@ const BaseVideo = forwardRef(function BaseVideo({ stream, ...rest }, ref) {
541
541
  if (stream === videoElement.srcObject)
542
542
  return;
543
543
  videoElement.srcObject = stream;
544
+ let timeoutId;
544
545
  if (Browsers.isSafari() || Browsers.isFirefox()) {
545
546
  // Firefox and Safari have some timing issue
546
- setTimeout(() => {
547
+ timeoutId = setTimeout(() => {
547
548
  videoElement.srcObject = stream;
548
549
  videoElement.play().catch((e) => {
549
550
  console.error(`Failed to play stream`, e);
@@ -551,6 +552,7 @@ const BaseVideo = forwardRef(function BaseVideo({ stream, ...rest }, ref) {
551
552
  }, 0);
552
553
  }
553
554
  return () => {
555
+ clearTimeout(timeoutId);
554
556
  videoElement.pause();
555
557
  videoElement.srcObject = null;
556
558
  };
@@ -587,6 +589,7 @@ const DefaultPictureInPicturePlaceholder = forwardRef(function DefaultPictureInP
587
589
  return (jsx(BaseVideoPlaceholder, { ref: ref, ...props, children: t('Video is playing in a popup') }));
588
590
  });
589
591
 
592
+ const ABSOLUTE_POSITION_STYLE = { position: 'absolute' };
590
593
  const Video$1 = ({ enabled = true, mirror, trackType, participant, className, VideoPlaceholder = DefaultVideoPlaceholder, PictureInPicturePlaceholder = DefaultPictureInPicturePlaceholder, refs, ...rest }) => {
591
594
  const { sessionId, videoStream, screenShareStream, viewportVisibilityState, isLocalParticipant, userId, } = participant;
592
595
  const call = useCall();
@@ -659,7 +662,7 @@ const Video$1 = ({ enabled = true, mirror, trackType, participant, className, Vi
659
662
  }), "data-user-id": userId, "data-session-id": sessionId, ref: (element) => {
660
663
  setVideoElement(element);
661
664
  refs?.setVideoElement?.(element);
662
- } })), isPiP && PictureInPicturePlaceholder && (jsx(PictureInPicturePlaceholder, { style: { position: 'absolute' }, participant: participant })), (hasNoVideoOrInvisible || isVideoPaused) && VideoPlaceholder && (jsx(VideoPlaceholder, { style: { position: 'absolute' }, participant: participant, ref: refs?.setVideoPlaceholderElement }))] }));
665
+ } })), isPiP && PictureInPicturePlaceholder && (jsx(PictureInPicturePlaceholder, { style: ABSOLUTE_POSITION_STYLE, participant: participant })), (hasNoVideoOrInvisible || isVideoPaused) && VideoPlaceholder && (jsx(VideoPlaceholder, { style: ABSOLUTE_POSITION_STYLE, participant: participant, ref: refs?.setVideoPlaceholderElement }))] }));
663
666
  };
664
667
  Video$1.displayName = 'Video';
665
668
 
@@ -1018,16 +1021,20 @@ const ScreenShareButton = (props) => {
1018
1021
  const AudioVolumeIndicator = () => {
1019
1022
  const { useMicrophoneState } = useCallStateHooks();
1020
1023
  const { isEnabled, mediaStream } = useMicrophoneState();
1021
- const [audioLevel, setAudioLevel] = useState(0);
1024
+ const barRef = useRef(null);
1022
1025
  useEffect(() => {
1023
1026
  if (!isEnabled || !mediaStream)
1024
1027
  return;
1025
- const disposeSoundDetector = createSoundDetector(mediaStream, ({ audioLevel: al }) => setAudioLevel(al), { detectionFrequencyInMs: 80, destroyStreamOnStop: false });
1028
+ const disposeSoundDetector = createSoundDetector(mediaStream, ({ audioLevel: al }) => {
1029
+ if (barRef.current) {
1030
+ barRef.current.style.transform = `scaleX(${al / 100})`;
1031
+ }
1032
+ }, { detectionFrequencyInMs: 80, destroyStreamOnStop: false });
1026
1033
  return () => {
1027
1034
  disposeSoundDetector().catch(console.error);
1028
1035
  };
1029
1036
  }, [isEnabled, mediaStream]);
1030
- 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})` } }) })] }));
1037
+ 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" }) })] }));
1031
1038
  };
1032
1039
 
1033
1040
  const LEVEL_BARS = 5;
@@ -1201,7 +1208,7 @@ const SpeakerTest = (props) => {
1201
1208
  const audioElementRef = useRef(null);
1202
1209
  const [isPlaying, setIsPlaying] = useState(false);
1203
1210
  const { t } = useI18n();
1204
- const { audioUrl = `https://unpkg.com/${"@stream-io/video-react-sdk"}@${"1.37.7"}/assets/piano.mp3`, } = props;
1211
+ const { audioUrl = `https://unpkg.com/${"@stream-io/video-react-sdk"}@${"1.39.0"}/assets/piano.mp3`, } = props;
1205
1212
  // Update audio output device when selection changes
1206
1213
  useEffect(() => {
1207
1214
  const audio = audioElementRef.current;
@@ -1500,13 +1507,14 @@ const CallParticipantListHeader = ({ onClose, }) => {
1500
1507
  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" })] }));
1501
1508
  };
1502
1509
 
1503
- const CallParticipantListingItem = ({ participant, DisplayName = DefaultDisplayName, }) => {
1510
+ const CallParticipantListingItem = memo(function CallParticipantListingItem({ participant, DisplayName = DefaultDisplayName, }) {
1504
1511
  const isAudioOn = hasAudio(participant);
1505
1512
  const isVideoOn = hasVideo(participant);
1506
1513
  const isPinnedOn = isPinned(participant);
1507
1514
  const { t } = useI18n();
1508
1515
  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, {}) }) })] })] }));
1509
- };
1516
+ });
1517
+ CallParticipantListingItem.displayName = 'CallParticipantListingItem';
1510
1518
  const MediaIndicator = (props) => (jsx(WithTooltip, { ...props }));
1511
1519
  const DefaultDisplayName = ({ participant }) => {
1512
1520
  const connectedUser = useConnectedUser();
@@ -1538,18 +1546,19 @@ const EmptyParticipantSearchList = () => {
1538
1546
 
1539
1547
  const SearchInput = ({ exitSearch, isActive, ...rest }) => {
1540
1548
  const [inputElement, setInputElement] = useState(null);
1549
+ const onExitSearch = useEffectEvent(exitSearch);
1541
1550
  useEffect(() => {
1542
1551
  if (!inputElement)
1543
1552
  return;
1544
1553
  const handleKeyDown = (e) => {
1545
1554
  if (e.key.toLowerCase() === 'escape')
1546
- exitSearch();
1555
+ onExitSearch();
1547
1556
  };
1548
1557
  inputElement.addEventListener('keydown', handleKeyDown);
1549
1558
  return () => {
1550
1559
  inputElement.removeEventListener('keydown', handleKeyDown);
1551
1560
  };
1552
- }, [exitSearch, inputElement]);
1561
+ }, [inputElement]);
1553
1562
  return (jsxs("div", { className: clsx('str-video__search-input__container', {
1554
1563
  'str-video__search-input__container--active': isActive,
1555
1564
  }), 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" }))] }));
@@ -1620,10 +1629,19 @@ const CallParticipantListContentHeader = ({ userListType, setUserListType, }) =>
1620
1629
  const ActiveUsersSearchResults = ({ searchQuery, activeUsersSearchFn: activeUsersSearchFnFromProps, debounceSearchInterval, }) => {
1621
1630
  const { useParticipants } = useCallStateHooks();
1622
1631
  const participants = useParticipants({ sortBy: name });
1632
+ // cache: each distinct name is normalized once
1633
+ const normalizedNameCache = useRef(null);
1623
1634
  const activeUsersSearchFn = useCallback(async (queryString) => {
1624
1635
  const normalizedQuery = normalizeString(queryString);
1625
- const queryRegExp = new RegExp(normalizedQuery, 'i');
1626
- return participants.filter((p) => normalizeString(p.name).match(queryRegExp));
1636
+ const cache = (normalizedNameCache.current ??= new Map());
1637
+ return participants.filter((p) => {
1638
+ let normalizedName = cache.get(p.name);
1639
+ if (normalizedName === undefined) {
1640
+ normalizedName = normalizeString(p.name);
1641
+ cache.set(p.name, normalizedName);
1642
+ }
1643
+ return normalizedName.includes(normalizedQuery);
1644
+ });
1627
1645
  }, [participants]);
1628
1646
  const { searchQueryInProgress, searchResults } = useSearch({
1629
1647
  searchFn: activeUsersSearchFnFromProps ?? activeUsersSearchFn,
@@ -1636,8 +1654,8 @@ const BlockedUsersSearchResults = ({ blockedUsersSearchFn: blockedUsersSearchFnF
1636
1654
  const { useCallBlockedUserIds } = useCallStateHooks();
1637
1655
  const blockedUsers = useCallBlockedUserIds();
1638
1656
  const blockedUsersSearchFn = useCallback(async (queryString) => {
1639
- const queryRegExp = new RegExp(queryString, 'i');
1640
- return blockedUsers.filter((userId) => userId.match(queryRegExp));
1657
+ const normalizedQuery = queryString.toLowerCase();
1658
+ return blockedUsers.filter((userId) => userId.toLowerCase().includes(normalizedQuery));
1641
1659
  }, [blockedUsers]);
1642
1660
  const { searchQueryInProgress, searchResults } = useSearch({
1643
1661
  searchFn: blockedUsersSearchFnFromProps ?? blockedUsersSearchFn,
@@ -1898,7 +1916,8 @@ const ParticipantDetails = ({ indicatorsVisible = true, }) => {
1898
1916
  const canUnpin = !!pin && pin.isLocalPin;
1899
1917
  const isTrackPaused = trackType !== 'none' ? hasPausedTrack(participant, trackType) : false;
1900
1918
  const isAudioConnecting = useIsAudioConnecting(participant);
1901
- 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 &&
1919
+ const isVideoConnecting = useIsVideoConnecting(participant);
1920
+ 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 &&
1902
1921
  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 })) }))] }));
1903
1922
  };
1904
1923
  const SpeechIndicator = () => {
@@ -1907,7 +1926,7 @@ const SpeechIndicator = () => {
1907
1926
  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" })] }));
1908
1927
  };
1909
1928
 
1910
- const ParticipantView = forwardRef(function ParticipantView({ participant, trackType = 'videoTrack', mirror, muteAudio, refs: { setVideoElement, setVideoPlaceholderElement } = {}, className, VideoPlaceholder, PictureInPicturePlaceholder, ParticipantViewUI = DefaultParticipantViewUI, }, ref) {
1929
+ const ParticipantView = memo(forwardRef(function ParticipantView({ participant, trackType = 'videoTrack', mirror, muteAudio, refs: { setVideoElement, setVideoPlaceholderElement } = {}, className, VideoPlaceholder, PictureInPicturePlaceholder, ParticipantViewUI = DefaultParticipantViewUI, }, ref) {
1911
1930
  const { isLocalParticipant, isSpeaking, isDominantSpeaker, sessionId } = participant;
1912
1931
  const hasAudioTrack = hasAudio(participant);
1913
1932
  const hasVideoTrack = hasVideo(participant);
@@ -1951,7 +1970,7 @@ const ParticipantView = forwardRef(function ParticipantView({ participant, track
1951
1970
  }, 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 ||
1952
1971
  trackType !== 'videoTrack' ||
1953
1972
  isParticipantVideoEnabled(participant.sessionId), mirror: mirror, autoPlay: true }), isComponentType(ParticipantViewUI) ? (jsx(ParticipantViewUI, {})) : (ParticipantViewUI)] }) }));
1954
- });
1973
+ }));
1955
1974
  ParticipantView.displayName = 'ParticipantView';
1956
1975
 
1957
1976
  // re-exporting the StreamCallProvider as StreamCall
@@ -2078,6 +2097,7 @@ var en = {
2078
2097
  "Poor connection quality": "Poor connection quality. Please check your internet connection.",
2079
2098
  "Video paused due to insufficient bandwidth": "Video paused due to insufficient bandwidth",
2080
2099
  "Audio is connecting...": "Audio is connecting...",
2100
+ "Video is connecting...": "Video is connecting...",
2081
2101
  Participants: Participants,
2082
2102
  Anonymous: Anonymous,
2083
2103
  "No participants found": "No participants found",
@@ -2168,20 +2188,20 @@ function checkConditions(obj, conditions) {
2168
2188
  const eqOperatorValue = isDateString(eqOperator.$eq)
2169
2189
  ? new Date(eqOperator.$eq).getTime()
2170
2190
  : eqOperator.$eq;
2171
- match && (match = eqOperatorValue === value);
2191
+ match &&= eqOperatorValue === value;
2172
2192
  }
2173
2193
  else if (maybeOperator && '$neq' in operator) {
2174
2194
  const neqOperator = operator;
2175
- match && (match = neqOperator.$neq !== value);
2195
+ match &&= neqOperator.$neq !== value;
2176
2196
  }
2177
2197
  else if (maybeOperator && '$in' in operator) {
2178
2198
  const inOperator = operator;
2179
- match && (match = inOperator.$in.includes(value));
2199
+ match &&= inOperator.$in.includes(value);
2180
2200
  }
2181
2201
  else if (maybeOperator && '$contains' in operator) {
2182
2202
  if (Array.isArray(value)) {
2183
2203
  const containsOperator = operator;
2184
- match && (match = value.includes(containsOperator.$contains));
2204
+ match &&= value.includes(containsOperator.$contains);
2185
2205
  }
2186
2206
  else {
2187
2207
  match = false;
@@ -2192,35 +2212,35 @@ function checkConditions(obj, conditions) {
2192
2212
  const gtOperatorValue = isDateString(gtOperator.$gt)
2193
2213
  ? new Date(gtOperator.$gt).getTime()
2194
2214
  : gtOperator.$gt;
2195
- match && (match = value > gtOperatorValue);
2215
+ match &&= value > gtOperatorValue;
2196
2216
  }
2197
2217
  else if (maybeOperator && '$gte' in operator) {
2198
2218
  const gteOperator = operator;
2199
2219
  const gteOperatorValue = isDateString(gteOperator.$gte)
2200
2220
  ? new Date(gteOperator.$gte).getTime()
2201
2221
  : gteOperator.$gte;
2202
- match && (match = value >= gteOperatorValue);
2222
+ match &&= value >= gteOperatorValue;
2203
2223
  }
2204
2224
  else if (maybeOperator && '$lt' in operator) {
2205
2225
  const ltOperator = operator;
2206
2226
  const ltOperatorValue = isDateString(ltOperator.$lt)
2207
2227
  ? new Date(ltOperator.$lt).getTime()
2208
2228
  : ltOperator.$lt;
2209
- match && (match = value < ltOperatorValue);
2229
+ match &&= value < ltOperatorValue;
2210
2230
  }
2211
2231
  else if (maybeOperator && '$lte' in operator) {
2212
2232
  const lteOperator = operator;
2213
2233
  const lteOperatorValue = isDateString(lteOperator.$lte)
2214
2234
  ? new Date(lteOperator.$lte).getTime()
2215
2235
  : lteOperator.$lte;
2216
- match && (match = value <= lteOperatorValue);
2236
+ match &&= value <= lteOperatorValue;
2217
2237
  // } else if (maybeOperator && '$autocomplete' in operator) {
2218
2238
  // TODO: regexp solution maybe?
2219
2239
  // match &&= false;
2220
2240
  }
2221
2241
  else {
2222
2242
  const eqValue = operator;
2223
- match && (match = eqValue === value);
2243
+ match &&= eqValue === value;
2224
2244
  }
2225
2245
  if (!match) {
2226
2246
  return false;
@@ -3059,6 +3079,7 @@ const useWakeLock = () => {
3059
3079
  if (interrupted)
3060
3080
  return wls.release();
3061
3081
  wakeLockSentinel = wls;
3082
+ return undefined;
3062
3083
  })
3063
3084
  .catch((error) => console.debug(`Couldn't setup WakeLock due to: ${error}`));
3064
3085
  return () => {
@@ -3393,12 +3414,12 @@ const ViewerLobby = ({ onJoin }) => {
3393
3414
  }
3394
3415
  }, [canJoin, autoJoin, onJoin]);
3395
3416
  useEffect(() => {
3396
- if (!canJoinEarly) {
3397
- const handle = setInterval(() => {
3398
- setCanJoinEarly(checkCanJoinEarly(startsAt, joinAheadTimeSeconds));
3399
- }, 1000);
3400
- return () => clearInterval(handle);
3401
- }
3417
+ if (canJoinEarly)
3418
+ return;
3419
+ const handle = setInterval(() => {
3420
+ setCanJoinEarly(checkCanJoinEarly(startsAt, joinAheadTimeSeconds));
3421
+ }, 1000);
3422
+ return () => clearInterval(handle);
3402
3423
  }, [canJoinEarly, startsAt, joinAheadTimeSeconds]);
3403
3424
  useEffect(() => {
3404
3425
  if (!startsAt || startsAtPassed)