@stream-io/video-react-sdk 1.38.0 → 1.39.1
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 +31 -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.cjs.js
CHANGED
|
@@ -7,7 +7,7 @@ var videoClient = require('@stream-io/video-client');
|
|
|
7
7
|
var react$1 = require('@floating-ui/react');
|
|
8
8
|
var clsx = require('clsx');
|
|
9
9
|
|
|
10
|
-
const Audio = ({ participant, trackType = 'audioTrack', ...rest })
|
|
10
|
+
const Audio = react.memo(function Audio({ participant, trackType = 'audioTrack', ...rest }) {
|
|
11
11
|
const call = videoReactBindings.useCall();
|
|
12
12
|
const [audioElement, setAudioElement] = react.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 (jsxRuntime.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) => {
|
|
@@ -96,7 +96,7 @@ const useVerticalScrollPosition = (scrollElement, threshold = SCROLL_THRESHOLD)
|
|
|
96
96
|
};
|
|
97
97
|
const resizeObserver = new ResizeObserver(scrollHandler);
|
|
98
98
|
resizeObserver.observe(scrollElement);
|
|
99
|
-
scrollElement.addEventListener('scroll', scrollHandler);
|
|
99
|
+
scrollElement.addEventListener('scroll', scrollHandler, { passive: true });
|
|
100
100
|
return () => {
|
|
101
101
|
scrollElement.removeEventListener('scroll', scrollHandler);
|
|
102
102
|
resizeObserver.disconnect();
|
|
@@ -124,7 +124,7 @@ const useHorizontalScrollPosition = (scrollElement, threshold = SCROLL_THRESHOLD
|
|
|
124
124
|
};
|
|
125
125
|
const resizeObserver = new ResizeObserver(scrollHandler);
|
|
126
126
|
resizeObserver.observe(scrollElement);
|
|
127
|
-
scrollElement.addEventListener('scroll', scrollHandler);
|
|
127
|
+
scrollElement.addEventListener('scroll', scrollHandler, { passive: true });
|
|
128
128
|
return () => {
|
|
129
129
|
scrollElement.removeEventListener('scroll', scrollHandler);
|
|
130
130
|
resizeObserver.disconnect();
|
|
@@ -215,6 +215,7 @@ function useDeviceList(devices, selectedDeviceId) {
|
|
|
215
215
|
* @param options - Options for customizing the drag-to-scroll behavior.
|
|
216
216
|
*/
|
|
217
217
|
function useDragToScroll(element, options = {}) {
|
|
218
|
+
const { enabled, decay = 0.95, minVelocity = 0.5, dragThreshold = 5, } = options;
|
|
218
219
|
const stateRef = react.useRef({
|
|
219
220
|
isDragging: false,
|
|
220
221
|
isPointerActive: false,
|
|
@@ -227,9 +228,8 @@ function useDragToScroll(element, options = {}) {
|
|
|
227
228
|
startY: 0,
|
|
228
229
|
});
|
|
229
230
|
react.useEffect(() => {
|
|
230
|
-
if (!element || !
|
|
231
|
+
if (!element || !enabled)
|
|
231
232
|
return;
|
|
232
|
-
const { decay = 0.95, minVelocity = 0.5, dragThreshold = 5 } = options;
|
|
233
233
|
const state = stateRef.current;
|
|
234
234
|
const stopMomentum = () => {
|
|
235
235
|
if (state.rafId) {
|
|
@@ -313,7 +313,7 @@ function useDragToScroll(element, options = {}) {
|
|
|
313
313
|
window.removeEventListener('pointercancel', onPointerUpOrCancel);
|
|
314
314
|
stopMomentum();
|
|
315
315
|
};
|
|
316
|
-
}, [element,
|
|
316
|
+
}, [element, enabled, decay, minVelocity, dragThreshold]);
|
|
317
317
|
}
|
|
318
318
|
|
|
319
319
|
var MenuVisualType;
|
|
@@ -543,9 +543,10 @@ const BaseVideo = react.forwardRef(function BaseVideo({ stream, ...rest }, ref)
|
|
|
543
543
|
if (stream === videoElement.srcObject)
|
|
544
544
|
return;
|
|
545
545
|
videoElement.srcObject = stream;
|
|
546
|
+
let timeoutId;
|
|
546
547
|
if (videoClient.Browsers.isSafari() || videoClient.Browsers.isFirefox()) {
|
|
547
548
|
// Firefox and Safari have some timing issue
|
|
548
|
-
setTimeout(() => {
|
|
549
|
+
timeoutId = setTimeout(() => {
|
|
549
550
|
videoElement.srcObject = stream;
|
|
550
551
|
videoElement.play().catch((e) => {
|
|
551
552
|
console.error(`Failed to play stream`, e);
|
|
@@ -553,6 +554,7 @@ const BaseVideo = react.forwardRef(function BaseVideo({ stream, ...rest }, ref)
|
|
|
553
554
|
}, 0);
|
|
554
555
|
}
|
|
555
556
|
return () => {
|
|
557
|
+
clearTimeout(timeoutId);
|
|
556
558
|
videoElement.pause();
|
|
557
559
|
videoElement.srcObject = null;
|
|
558
560
|
};
|
|
@@ -589,6 +591,7 @@ const DefaultPictureInPicturePlaceholder = react.forwardRef(function DefaultPict
|
|
|
589
591
|
return (jsxRuntime.jsx(BaseVideoPlaceholder, { ref: ref, ...props, children: t('Video is playing in a popup') }));
|
|
590
592
|
});
|
|
591
593
|
|
|
594
|
+
const ABSOLUTE_POSITION_STYLE = { position: 'absolute' };
|
|
592
595
|
const Video$1 = ({ enabled = true, mirror, trackType, participant, className, VideoPlaceholder = DefaultVideoPlaceholder, PictureInPicturePlaceholder = DefaultPictureInPicturePlaceholder, refs, ...rest }) => {
|
|
593
596
|
const { sessionId, videoStream, screenShareStream, viewportVisibilityState, isLocalParticipant, userId, } = participant;
|
|
594
597
|
const call = videoReactBindings.useCall();
|
|
@@ -661,7 +664,7 @@ const Video$1 = ({ enabled = true, mirror, trackType, participant, className, Vi
|
|
|
661
664
|
}), "data-user-id": userId, "data-session-id": sessionId, ref: (element) => {
|
|
662
665
|
setVideoElement(element);
|
|
663
666
|
refs?.setVideoElement?.(element);
|
|
664
|
-
} })), isPiP && PictureInPicturePlaceholder && (jsxRuntime.jsx(PictureInPicturePlaceholder, { style:
|
|
667
|
+
} })), isPiP && PictureInPicturePlaceholder && (jsxRuntime.jsx(PictureInPicturePlaceholder, { style: ABSOLUTE_POSITION_STYLE, participant: participant })), (hasNoVideoOrInvisible || isVideoPaused) && VideoPlaceholder && (jsxRuntime.jsx(VideoPlaceholder, { style: ABSOLUTE_POSITION_STYLE, participant: participant, ref: refs?.setVideoPlaceholderElement }))] }));
|
|
665
668
|
};
|
|
666
669
|
Video$1.displayName = 'Video';
|
|
667
670
|
|
|
@@ -1020,16 +1023,20 @@ const ScreenShareButton = (props) => {
|
|
|
1020
1023
|
const AudioVolumeIndicator = () => {
|
|
1021
1024
|
const { useMicrophoneState } = videoReactBindings.useCallStateHooks();
|
|
1022
1025
|
const { isEnabled, mediaStream } = useMicrophoneState();
|
|
1023
|
-
const
|
|
1026
|
+
const barRef = react.useRef(null);
|
|
1024
1027
|
react.useEffect(() => {
|
|
1025
1028
|
if (!isEnabled || !mediaStream)
|
|
1026
1029
|
return;
|
|
1027
|
-
const disposeSoundDetector = videoClient.createSoundDetector(mediaStream, ({ audioLevel: al }) =>
|
|
1030
|
+
const disposeSoundDetector = videoClient.createSoundDetector(mediaStream, ({ audioLevel: al }) => {
|
|
1031
|
+
if (barRef.current) {
|
|
1032
|
+
barRef.current.style.transform = `scaleX(${al / 100})`;
|
|
1033
|
+
}
|
|
1034
|
+
}, { detectionFrequencyInMs: 80, destroyStreamOnStop: false });
|
|
1028
1035
|
return () => {
|
|
1029
1036
|
disposeSoundDetector().catch(console.error);
|
|
1030
1037
|
};
|
|
1031
1038
|
}, [isEnabled, mediaStream]);
|
|
1032
|
-
return (jsxRuntime.jsxs("div", { className: "str-video__audio-volume-indicator", children: [jsxRuntime.jsx(Icon, { icon: isEnabled ? 'mic' : 'mic-off' }), jsxRuntime.jsx("div", { className: "str-video__audio-volume-indicator__bar", children: jsxRuntime.jsx("div", { className: "str-video__audio-volume-indicator__bar-value"
|
|
1039
|
+
return (jsxRuntime.jsxs("div", { className: "str-video__audio-volume-indicator", children: [jsxRuntime.jsx(Icon, { icon: isEnabled ? 'mic' : 'mic-off' }), jsxRuntime.jsx("div", { className: "str-video__audio-volume-indicator__bar", children: jsxRuntime.jsx("div", { ref: barRef, className: "str-video__audio-volume-indicator__bar-value" }) })] }));
|
|
1033
1040
|
};
|
|
1034
1041
|
|
|
1035
1042
|
const LEVEL_BARS = 5;
|
|
@@ -1203,7 +1210,7 @@ const SpeakerTest = (props) => {
|
|
|
1203
1210
|
const audioElementRef = react.useRef(null);
|
|
1204
1211
|
const [isPlaying, setIsPlaying] = react.useState(false);
|
|
1205
1212
|
const { t } = videoReactBindings.useI18n();
|
|
1206
|
-
const { audioUrl = `https://unpkg.com/${"@stream-io/video-react-sdk"}@${"1.
|
|
1213
|
+
const { audioUrl = `https://unpkg.com/${"@stream-io/video-react-sdk"}@${"1.39.1"}/assets/piano.mp3`, } = props;
|
|
1207
1214
|
// Update audio output device when selection changes
|
|
1208
1215
|
react.useEffect(() => {
|
|
1209
1216
|
const audio = audioElementRef.current;
|
|
@@ -1502,13 +1509,14 @@ const CallParticipantListHeader = ({ onClose, }) => {
|
|
|
1502
1509
|
return (jsxRuntime.jsxs("div", { className: "str-video__participant-list-header", children: [jsxRuntime.jsxs("div", { className: "str-video__participant-list-header__title", children: [t('Participants'), ' ', jsxRuntime.jsxs("span", { className: "str-video__participant-list-header__title-count", children: ["[", participants.length, "]"] }), anonymousParticipantCount > 0 && (jsxRuntime.jsx("span", { className: "str-video__participant-list-header__title-anonymous", children: t('Anonymous', { count: anonymousParticipantCount }) }))] }), jsxRuntime.jsx(IconButton, { onClick: onClose, className: "str-video__participant-list-header__close-button", icon: "close" })] }));
|
|
1503
1510
|
};
|
|
1504
1511
|
|
|
1505
|
-
const CallParticipantListingItem = ({ participant, DisplayName = DefaultDisplayName, })
|
|
1512
|
+
const CallParticipantListingItem = react.memo(function CallParticipantListingItem({ participant, DisplayName = DefaultDisplayName, }) {
|
|
1506
1513
|
const isAudioOn = videoClient.hasAudio(participant);
|
|
1507
1514
|
const isVideoOn = videoClient.hasVideo(participant);
|
|
1508
1515
|
const isPinnedOn = videoClient.isPinned(participant);
|
|
1509
1516
|
const { t } = videoReactBindings.useI18n();
|
|
1510
1517
|
return (jsxRuntime.jsxs("div", { className: "str-video__participant-listing-item", children: [jsxRuntime.jsx(Avatar, { name: participant.name, imageSrc: participant.image }), jsxRuntime.jsx(DisplayName, { participant: participant }), jsxRuntime.jsxs("div", { className: "str-video__participant-listing-item__media-indicator-group", children: [jsxRuntime.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'}`) }), jsxRuntime.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 && (jsxRuntime.jsx(MediaIndicator, { title: t('Pinned'), className: clsx('str-video__participant-listing-item__icon', 'str-video__participant-listing-item__icon-pinned') })), jsxRuntime.jsx(MenuToggle, { placement: "bottom-end", ToggleButton: ToggleButton$2, children: jsxRuntime.jsx(ParticipantViewContext.Provider, { value: { participant, trackType: 'none' }, children: jsxRuntime.jsx(ParticipantActionsContextMenu, {}) }) })] })] }));
|
|
1511
|
-
};
|
|
1518
|
+
});
|
|
1519
|
+
CallParticipantListingItem.displayName = 'CallParticipantListingItem';
|
|
1512
1520
|
const MediaIndicator = (props) => (jsxRuntime.jsx(WithTooltip, { ...props }));
|
|
1513
1521
|
const DefaultDisplayName = ({ participant }) => {
|
|
1514
1522
|
const connectedUser = videoReactBindings.useConnectedUser();
|
|
@@ -1540,18 +1548,19 @@ const EmptyParticipantSearchList = () => {
|
|
|
1540
1548
|
|
|
1541
1549
|
const SearchInput = ({ exitSearch, isActive, ...rest }) => {
|
|
1542
1550
|
const [inputElement, setInputElement] = react.useState(null);
|
|
1551
|
+
const onExitSearch = videoReactBindings.useEffectEvent(exitSearch);
|
|
1543
1552
|
react.useEffect(() => {
|
|
1544
1553
|
if (!inputElement)
|
|
1545
1554
|
return;
|
|
1546
1555
|
const handleKeyDown = (e) => {
|
|
1547
1556
|
if (e.key.toLowerCase() === 'escape')
|
|
1548
|
-
|
|
1557
|
+
onExitSearch();
|
|
1549
1558
|
};
|
|
1550
1559
|
inputElement.addEventListener('keydown', handleKeyDown);
|
|
1551
1560
|
return () => {
|
|
1552
1561
|
inputElement.removeEventListener('keydown', handleKeyDown);
|
|
1553
1562
|
};
|
|
1554
|
-
}, [
|
|
1563
|
+
}, [inputElement]);
|
|
1555
1564
|
return (jsxRuntime.jsxs("div", { className: clsx('str-video__search-input__container', {
|
|
1556
1565
|
'str-video__search-input__container--active': isActive,
|
|
1557
1566
|
}), children: [jsxRuntime.jsx("input", { placeholder: "Search", ...rest, ref: setInputElement }), isActive ? (jsxRuntime.jsx("button", { className: "str-video__search-input__clear-btn", onClick: exitSearch, children: jsxRuntime.jsx("span", { className: "str-video__search-input__icon--active" }) })) : (jsxRuntime.jsx("span", { className: "str-video__search-input__icon" }))] }));
|
|
@@ -1622,10 +1631,19 @@ const CallParticipantListContentHeader = ({ userListType, setUserListType, }) =>
|
|
|
1622
1631
|
const ActiveUsersSearchResults = ({ searchQuery, activeUsersSearchFn: activeUsersSearchFnFromProps, debounceSearchInterval, }) => {
|
|
1623
1632
|
const { useParticipants } = videoReactBindings.useCallStateHooks();
|
|
1624
1633
|
const participants = useParticipants({ sortBy: videoClient.name });
|
|
1634
|
+
// cache: each distinct name is normalized once
|
|
1635
|
+
const normalizedNameCache = react.useRef(null);
|
|
1625
1636
|
const activeUsersSearchFn = react.useCallback(async (queryString) => {
|
|
1626
1637
|
const normalizedQuery = normalizeString(queryString);
|
|
1627
|
-
const
|
|
1628
|
-
return participants.filter((p) =>
|
|
1638
|
+
const cache = (normalizedNameCache.current ??= new Map());
|
|
1639
|
+
return participants.filter((p) => {
|
|
1640
|
+
let normalizedName = cache.get(p.name);
|
|
1641
|
+
if (normalizedName === undefined) {
|
|
1642
|
+
normalizedName = normalizeString(p.name);
|
|
1643
|
+
cache.set(p.name, normalizedName);
|
|
1644
|
+
}
|
|
1645
|
+
return normalizedName.includes(normalizedQuery);
|
|
1646
|
+
});
|
|
1629
1647
|
}, [participants]);
|
|
1630
1648
|
const { searchQueryInProgress, searchResults } = useSearch({
|
|
1631
1649
|
searchFn: activeUsersSearchFnFromProps ?? activeUsersSearchFn,
|
|
@@ -1638,8 +1656,8 @@ const BlockedUsersSearchResults = ({ blockedUsersSearchFn: blockedUsersSearchFnF
|
|
|
1638
1656
|
const { useCallBlockedUserIds } = videoReactBindings.useCallStateHooks();
|
|
1639
1657
|
const blockedUsers = useCallBlockedUserIds();
|
|
1640
1658
|
const blockedUsersSearchFn = react.useCallback(async (queryString) => {
|
|
1641
|
-
const
|
|
1642
|
-
return blockedUsers.filter((userId) => userId.
|
|
1659
|
+
const normalizedQuery = queryString.toLowerCase();
|
|
1660
|
+
return blockedUsers.filter((userId) => userId.toLowerCase().includes(normalizedQuery));
|
|
1643
1661
|
}, [blockedUsers]);
|
|
1644
1662
|
const { searchQueryInProgress, searchResults } = useSearch({
|
|
1645
1663
|
searchFn: blockedUsersSearchFnFromProps ?? blockedUsersSearchFn,
|
|
@@ -1910,7 +1928,7 @@ const SpeechIndicator = () => {
|
|
|
1910
1928
|
return (jsxRuntime.jsxs("span", { className: clsx('str-video__speech-indicator', isSpeaking && 'str-video__speech-indicator--speaking', isDominantSpeaker && 'str-video__speech-indicator--dominant'), children: [jsxRuntime.jsx("span", { className: "str-video__speech-indicator__bar" }), jsxRuntime.jsx("span", { className: "str-video__speech-indicator__bar" }), jsxRuntime.jsx("span", { className: "str-video__speech-indicator__bar" })] }));
|
|
1911
1929
|
};
|
|
1912
1930
|
|
|
1913
|
-
const ParticipantView = react.forwardRef(function ParticipantView({ participant, trackType = 'videoTrack', mirror, muteAudio, refs: { setVideoElement, setVideoPlaceholderElement } = {}, className, VideoPlaceholder, PictureInPicturePlaceholder, ParticipantViewUI = DefaultParticipantViewUI, }, ref) {
|
|
1931
|
+
const ParticipantView = react.memo(react.forwardRef(function ParticipantView({ participant, trackType = 'videoTrack', mirror, muteAudio, refs: { setVideoElement, setVideoPlaceholderElement } = {}, className, VideoPlaceholder, PictureInPicturePlaceholder, ParticipantViewUI = DefaultParticipantViewUI, }, ref) {
|
|
1914
1932
|
const { isLocalParticipant, isSpeaking, isDominantSpeaker, sessionId } = participant;
|
|
1915
1933
|
const hasAudioTrack = videoClient.hasAudio(participant);
|
|
1916
1934
|
const hasVideoTrack = videoClient.hasVideo(participant);
|
|
@@ -1954,7 +1972,7 @@ const ParticipantView = react.forwardRef(function ParticipantView({ participant,
|
|
|
1954
1972
|
}, 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: jsxRuntime.jsxs(ParticipantViewContext.Provider, { value: participantViewContextValue, children: [!isLocalParticipant && !muteAudio && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [hasAudioTrack && (jsxRuntime.jsx(Audio, { participant: participant, trackType: "audioTrack" })), hasScreenShareAudioTrack && (jsxRuntime.jsx(Audio, { participant: participant, trackType: "screenShareAudioTrack" }))] })), jsxRuntime.jsx(Video$1, { VideoPlaceholder: VideoPlaceholder, PictureInPicturePlaceholder: PictureInPicturePlaceholder, participant: participant, trackType: trackType, refs: videoRefs, enabled: isLocalParticipant ||
|
|
1955
1973
|
trackType !== 'videoTrack' ||
|
|
1956
1974
|
isParticipantVideoEnabled(participant.sessionId), mirror: mirror, autoPlay: true }), isComponentType(ParticipantViewUI) ? (jsxRuntime.jsx(ParticipantViewUI, {})) : (ParticipantViewUI)] }) }));
|
|
1957
|
-
});
|
|
1975
|
+
}));
|
|
1958
1976
|
ParticipantView.displayName = 'ParticipantView';
|
|
1959
1977
|
|
|
1960
1978
|
// re-exporting the StreamCallProvider as StreamCall
|
|
@@ -2172,20 +2190,20 @@ function checkConditions(obj, conditions) {
|
|
|
2172
2190
|
const eqOperatorValue = isDateString(eqOperator.$eq)
|
|
2173
2191
|
? new Date(eqOperator.$eq).getTime()
|
|
2174
2192
|
: eqOperator.$eq;
|
|
2175
|
-
match
|
|
2193
|
+
match &&= eqOperatorValue === value;
|
|
2176
2194
|
}
|
|
2177
2195
|
else if (maybeOperator && '$neq' in operator) {
|
|
2178
2196
|
const neqOperator = operator;
|
|
2179
|
-
match
|
|
2197
|
+
match &&= neqOperator.$neq !== value;
|
|
2180
2198
|
}
|
|
2181
2199
|
else if (maybeOperator && '$in' in operator) {
|
|
2182
2200
|
const inOperator = operator;
|
|
2183
|
-
match
|
|
2201
|
+
match &&= inOperator.$in.includes(value);
|
|
2184
2202
|
}
|
|
2185
2203
|
else if (maybeOperator && '$contains' in operator) {
|
|
2186
2204
|
if (Array.isArray(value)) {
|
|
2187
2205
|
const containsOperator = operator;
|
|
2188
|
-
match
|
|
2206
|
+
match &&= value.includes(containsOperator.$contains);
|
|
2189
2207
|
}
|
|
2190
2208
|
else {
|
|
2191
2209
|
match = false;
|
|
@@ -2196,35 +2214,35 @@ function checkConditions(obj, conditions) {
|
|
|
2196
2214
|
const gtOperatorValue = isDateString(gtOperator.$gt)
|
|
2197
2215
|
? new Date(gtOperator.$gt).getTime()
|
|
2198
2216
|
: gtOperator.$gt;
|
|
2199
|
-
match
|
|
2217
|
+
match &&= value > gtOperatorValue;
|
|
2200
2218
|
}
|
|
2201
2219
|
else if (maybeOperator && '$gte' in operator) {
|
|
2202
2220
|
const gteOperator = operator;
|
|
2203
2221
|
const gteOperatorValue = isDateString(gteOperator.$gte)
|
|
2204
2222
|
? new Date(gteOperator.$gte).getTime()
|
|
2205
2223
|
: gteOperator.$gte;
|
|
2206
|
-
match
|
|
2224
|
+
match &&= value >= gteOperatorValue;
|
|
2207
2225
|
}
|
|
2208
2226
|
else if (maybeOperator && '$lt' in operator) {
|
|
2209
2227
|
const ltOperator = operator;
|
|
2210
2228
|
const ltOperatorValue = isDateString(ltOperator.$lt)
|
|
2211
2229
|
? new Date(ltOperator.$lt).getTime()
|
|
2212
2230
|
: ltOperator.$lt;
|
|
2213
|
-
match
|
|
2231
|
+
match &&= value < ltOperatorValue;
|
|
2214
2232
|
}
|
|
2215
2233
|
else if (maybeOperator && '$lte' in operator) {
|
|
2216
2234
|
const lteOperator = operator;
|
|
2217
2235
|
const lteOperatorValue = isDateString(lteOperator.$lte)
|
|
2218
2236
|
? new Date(lteOperator.$lte).getTime()
|
|
2219
2237
|
: lteOperator.$lte;
|
|
2220
|
-
match
|
|
2238
|
+
match &&= value <= lteOperatorValue;
|
|
2221
2239
|
// } else if (maybeOperator && '$autocomplete' in operator) {
|
|
2222
2240
|
// TODO: regexp solution maybe?
|
|
2223
2241
|
// match &&= false;
|
|
2224
2242
|
}
|
|
2225
2243
|
else {
|
|
2226
2244
|
const eqValue = operator;
|
|
2227
|
-
match
|
|
2245
|
+
match &&= eqValue === value;
|
|
2228
2246
|
}
|
|
2229
2247
|
if (!match) {
|
|
2230
2248
|
return false;
|
|
@@ -3063,6 +3081,7 @@ const useWakeLock = () => {
|
|
|
3063
3081
|
if (interrupted)
|
|
3064
3082
|
return wls.release();
|
|
3065
3083
|
wakeLockSentinel = wls;
|
|
3084
|
+
return undefined;
|
|
3066
3085
|
})
|
|
3067
3086
|
.catch((error) => console.debug(`Couldn't setup WakeLock due to: ${error}`));
|
|
3068
3087
|
return () => {
|
|
@@ -3397,12 +3416,12 @@ const ViewerLobby = ({ onJoin }) => {
|
|
|
3397
3416
|
}
|
|
3398
3417
|
}, [canJoin, autoJoin, onJoin]);
|
|
3399
3418
|
react.useEffect(() => {
|
|
3400
|
-
if (
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3419
|
+
if (canJoinEarly)
|
|
3420
|
+
return;
|
|
3421
|
+
const handle = setInterval(() => {
|
|
3422
|
+
setCanJoinEarly(checkCanJoinEarly(startsAt, joinAheadTimeSeconds));
|
|
3423
|
+
}, 1000);
|
|
3424
|
+
return () => clearInterval(handle);
|
|
3406
3425
|
}, [canJoinEarly, startsAt, joinAheadTimeSeconds]);
|
|
3407
3426
|
react.useEffect(() => {
|
|
3408
3427
|
if (!startsAt || startsAtPassed)
|