@stream-io/video-react-sdk 1.38.0 → 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.
- package/CHANGELOG.md +26 -6
- package/dist/css/embedded.css +1 -0
- package/dist/css/embedded.css.map +1 -1
- package/dist/css/styles.css +1 -0
- package/dist/css/styles.css.map +1 -1
- package/dist/embedded.cjs.js +57 -38
- package/dist/embedded.cjs.js.map +1 -1
- package/dist/embedded.es.js +59 -40
- package/dist/embedded.es.js.map +1 -1
- package/dist/index.cjs.js +57 -39
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +59 -41
- package/dist/index.es.js.map +1 -1
- package/dist/src/components/CallParticipantsList/CallParticipantListingItem.d.ts +1 -1
- package/dist/src/core/components/Audio/Audio.d.ts +1 -4
- package/dist/src/core/components/ParticipantView/ParticipantView.d.ts +1 -1
- package/package.json +9 -7
- package/src/components/CallParticipantsList/CallParticipantListingItem.tsx +50 -46
- package/src/components/CallParticipantsList/CallParticipantsList.tsx +17 -6
- package/src/components/DeviceSettings/AudioVolumeIndicator.tsx +8 -4
- package/src/components/Search/SearchInput.tsx +4 -2
- package/src/core/components/Audio/Audio.tsx +4 -4
- package/src/core/components/ParticipantView/ParticipantView.tsx +4 -3
- package/src/core/components/Video/BaseVideo.tsx +3 -1
- package/src/core/components/Video/Video.tsx +5 -2
- package/src/embedded/hooks/useWakeLock.ts +1 -0
- package/src/embedded/livestream/viewer/ViewerLobby.tsx +5 -6
- package/src/hooks/useDragToScroll.ts +9 -4
- package/src/hooks/useScrollPosition.ts +2 -2
- package/src/wrappers/LivestreamPlayer/LivestreamPlayer.tsx +5 -6
package/dist/embedded.es.js
CHANGED
|
@@ -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, useIsVideoConnecting, StreamCallProvider, StreamVideoProvider
|
|
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 || !
|
|
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,
|
|
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:
|
|
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
|
|
1024
|
+
const barRef = useRef(null);
|
|
1022
1025
|
useEffect(() => {
|
|
1023
1026
|
if (!isEnabled || !mediaStream)
|
|
1024
1027
|
return;
|
|
1025
|
-
const disposeSoundDetector = createSoundDetector(mediaStream, ({ audioLevel: al }) =>
|
|
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"
|
|
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.
|
|
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
|
-
|
|
1555
|
+
onExitSearch();
|
|
1547
1556
|
};
|
|
1548
1557
|
inputElement.addEventListener('keydown', handleKeyDown);
|
|
1549
1558
|
return () => {
|
|
1550
1559
|
inputElement.removeEventListener('keydown', handleKeyDown);
|
|
1551
1560
|
};
|
|
1552
|
-
}, [
|
|
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
|
|
1626
|
-
return participants.filter((p) =>
|
|
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
|
|
1640
|
-
return blockedUsers.filter((userId) => userId.
|
|
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,
|
|
@@ -1908,7 +1926,7 @@ const SpeechIndicator = () => {
|
|
|
1908
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" })] }));
|
|
1909
1927
|
};
|
|
1910
1928
|
|
|
1911
|
-
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) {
|
|
1912
1930
|
const { isLocalParticipant, isSpeaking, isDominantSpeaker, sessionId } = participant;
|
|
1913
1931
|
const hasAudioTrack = hasAudio(participant);
|
|
1914
1932
|
const hasVideoTrack = hasVideo(participant);
|
|
@@ -1952,7 +1970,7 @@ const ParticipantView = forwardRef(function ParticipantView({ participant, track
|
|
|
1952
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 ||
|
|
1953
1971
|
trackType !== 'videoTrack' ||
|
|
1954
1972
|
isParticipantVideoEnabled(participant.sessionId), mirror: mirror, autoPlay: true }), isComponentType(ParticipantViewUI) ? (jsx(ParticipantViewUI, {})) : (ParticipantViewUI)] }) }));
|
|
1955
|
-
});
|
|
1973
|
+
}));
|
|
1956
1974
|
ParticipantView.displayName = 'ParticipantView';
|
|
1957
1975
|
|
|
1958
1976
|
// re-exporting the StreamCallProvider as StreamCall
|
|
@@ -2170,20 +2188,20 @@ function checkConditions(obj, conditions) {
|
|
|
2170
2188
|
const eqOperatorValue = isDateString(eqOperator.$eq)
|
|
2171
2189
|
? new Date(eqOperator.$eq).getTime()
|
|
2172
2190
|
: eqOperator.$eq;
|
|
2173
|
-
match
|
|
2191
|
+
match &&= eqOperatorValue === value;
|
|
2174
2192
|
}
|
|
2175
2193
|
else if (maybeOperator && '$neq' in operator) {
|
|
2176
2194
|
const neqOperator = operator;
|
|
2177
|
-
match
|
|
2195
|
+
match &&= neqOperator.$neq !== value;
|
|
2178
2196
|
}
|
|
2179
2197
|
else if (maybeOperator && '$in' in operator) {
|
|
2180
2198
|
const inOperator = operator;
|
|
2181
|
-
match
|
|
2199
|
+
match &&= inOperator.$in.includes(value);
|
|
2182
2200
|
}
|
|
2183
2201
|
else if (maybeOperator && '$contains' in operator) {
|
|
2184
2202
|
if (Array.isArray(value)) {
|
|
2185
2203
|
const containsOperator = operator;
|
|
2186
|
-
match
|
|
2204
|
+
match &&= value.includes(containsOperator.$contains);
|
|
2187
2205
|
}
|
|
2188
2206
|
else {
|
|
2189
2207
|
match = false;
|
|
@@ -2194,35 +2212,35 @@ function checkConditions(obj, conditions) {
|
|
|
2194
2212
|
const gtOperatorValue = isDateString(gtOperator.$gt)
|
|
2195
2213
|
? new Date(gtOperator.$gt).getTime()
|
|
2196
2214
|
: gtOperator.$gt;
|
|
2197
|
-
match
|
|
2215
|
+
match &&= value > gtOperatorValue;
|
|
2198
2216
|
}
|
|
2199
2217
|
else if (maybeOperator && '$gte' in operator) {
|
|
2200
2218
|
const gteOperator = operator;
|
|
2201
2219
|
const gteOperatorValue = isDateString(gteOperator.$gte)
|
|
2202
2220
|
? new Date(gteOperator.$gte).getTime()
|
|
2203
2221
|
: gteOperator.$gte;
|
|
2204
|
-
match
|
|
2222
|
+
match &&= value >= gteOperatorValue;
|
|
2205
2223
|
}
|
|
2206
2224
|
else if (maybeOperator && '$lt' in operator) {
|
|
2207
2225
|
const ltOperator = operator;
|
|
2208
2226
|
const ltOperatorValue = isDateString(ltOperator.$lt)
|
|
2209
2227
|
? new Date(ltOperator.$lt).getTime()
|
|
2210
2228
|
: ltOperator.$lt;
|
|
2211
|
-
match
|
|
2229
|
+
match &&= value < ltOperatorValue;
|
|
2212
2230
|
}
|
|
2213
2231
|
else if (maybeOperator && '$lte' in operator) {
|
|
2214
2232
|
const lteOperator = operator;
|
|
2215
2233
|
const lteOperatorValue = isDateString(lteOperator.$lte)
|
|
2216
2234
|
? new Date(lteOperator.$lte).getTime()
|
|
2217
2235
|
: lteOperator.$lte;
|
|
2218
|
-
match
|
|
2236
|
+
match &&= value <= lteOperatorValue;
|
|
2219
2237
|
// } else if (maybeOperator && '$autocomplete' in operator) {
|
|
2220
2238
|
// TODO: regexp solution maybe?
|
|
2221
2239
|
// match &&= false;
|
|
2222
2240
|
}
|
|
2223
2241
|
else {
|
|
2224
2242
|
const eqValue = operator;
|
|
2225
|
-
match
|
|
2243
|
+
match &&= eqValue === value;
|
|
2226
2244
|
}
|
|
2227
2245
|
if (!match) {
|
|
2228
2246
|
return false;
|
|
@@ -3061,6 +3079,7 @@ const useWakeLock = () => {
|
|
|
3061
3079
|
if (interrupted)
|
|
3062
3080
|
return wls.release();
|
|
3063
3081
|
wakeLockSentinel = wls;
|
|
3082
|
+
return undefined;
|
|
3064
3083
|
})
|
|
3065
3084
|
.catch((error) => console.debug(`Couldn't setup WakeLock due to: ${error}`));
|
|
3066
3085
|
return () => {
|
|
@@ -3395,12 +3414,12 @@ const ViewerLobby = ({ onJoin }) => {
|
|
|
3395
3414
|
}
|
|
3396
3415
|
}, [canJoin, autoJoin, onJoin]);
|
|
3397
3416
|
useEffect(() => {
|
|
3398
|
-
if (
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3417
|
+
if (canJoinEarly)
|
|
3418
|
+
return;
|
|
3419
|
+
const handle = setInterval(() => {
|
|
3420
|
+
setCanJoinEarly(checkCanJoinEarly(startsAt, joinAheadTimeSeconds));
|
|
3421
|
+
}, 1000);
|
|
3422
|
+
return () => clearInterval(handle);
|
|
3404
3423
|
}, [canJoinEarly, startsAt, joinAheadTimeSeconds]);
|
|
3405
3424
|
useEffect(() => {
|
|
3406
3425
|
if (!startsAt || startsAtPassed)
|