@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/index.cjs.js
CHANGED
|
@@ -7,7 +7,7 @@ var react = require('react');
|
|
|
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) => {
|
|
@@ -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 = react.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
|
react.useEffect(() => {
|
|
372
|
-
if (!element || !
|
|
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,
|
|
458
|
+
}, [element, enabled, decay, minVelocity, dragThreshold]);
|
|
459
459
|
}
|
|
460
460
|
|
|
461
461
|
exports.MenuVisualType = void 0;
|
|
@@ -685,9 +685,10 @@ const BaseVideo = react.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 (videoClient.Browsers.isSafari() || videoClient.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 = react.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 = react.forwardRef(function DefaultPict
|
|
|
731
733
|
return (jsxRuntime.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 = videoReactBindings.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 && (jsxRuntime.jsx(PictureInPicturePlaceholder, { style:
|
|
809
|
+
} })), 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 }))] }));
|
|
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 } = videoReactBindings.useCallStateHooks();
|
|
1218
1221
|
const { isEnabled, mediaStream } = useMicrophoneState();
|
|
1219
|
-
const
|
|
1222
|
+
const barRef = react.useRef(null);
|
|
1220
1223
|
react.useEffect(() => {
|
|
1221
1224
|
if (!isEnabled || !mediaStream)
|
|
1222
1225
|
return;
|
|
1223
|
-
const disposeSoundDetector = videoClient.createSoundDetector(mediaStream, ({ audioLevel: al }) =>
|
|
1226
|
+
const disposeSoundDetector = videoClient.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 (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"
|
|
1235
|
+
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" }) })] }));
|
|
1229
1236
|
};
|
|
1230
1237
|
|
|
1231
1238
|
const LEVEL_BARS = 5;
|
|
@@ -1399,7 +1406,7 @@ const SpeakerTest = (props) => {
|
|
|
1399
1406
|
const audioElementRef = react.useRef(null);
|
|
1400
1407
|
const [isPlaying, setIsPlaying] = react.useState(false);
|
|
1401
1408
|
const { t } = videoReactBindings.useI18n();
|
|
1402
|
-
const { audioUrl = `https://unpkg.com/${"@stream-io/video-react-sdk"}@${"1.
|
|
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
|
react.useEffect(() => {
|
|
1405
1412
|
const audio = audioElementRef.current;
|
|
@@ -1903,13 +1910,14 @@ const CallParticipantListHeader = ({ onClose, }) => {
|
|
|
1903
1910
|
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" })] }));
|
|
1904
1911
|
};
|
|
1905
1912
|
|
|
1906
|
-
const CallParticipantListingItem = ({ participant, DisplayName = DefaultDisplayName, })
|
|
1913
|
+
const CallParticipantListingItem = react.memo(function CallParticipantListingItem({ participant, DisplayName = DefaultDisplayName, }) {
|
|
1907
1914
|
const isAudioOn = videoClient.hasAudio(participant);
|
|
1908
1915
|
const isVideoOn = videoClient.hasVideo(participant);
|
|
1909
1916
|
const isPinnedOn = videoClient.isPinned(participant);
|
|
1910
1917
|
const { t } = videoReactBindings.useI18n();
|
|
1911
1918
|
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, {}) }) })] })] }));
|
|
1912
|
-
};
|
|
1919
|
+
});
|
|
1920
|
+
CallParticipantListingItem.displayName = 'CallParticipantListingItem';
|
|
1913
1921
|
const MediaIndicator = (props) => (jsxRuntime.jsx(WithTooltip, { ...props }));
|
|
1914
1922
|
const DefaultDisplayName = ({ participant }) => {
|
|
1915
1923
|
const connectedUser = videoReactBindings.useConnectedUser();
|
|
@@ -1941,18 +1949,19 @@ const EmptyParticipantSearchList = () => {
|
|
|
1941
1949
|
|
|
1942
1950
|
const SearchInput = ({ exitSearch, isActive, ...rest }) => {
|
|
1943
1951
|
const [inputElement, setInputElement] = react.useState(null);
|
|
1952
|
+
const onExitSearch = videoReactBindings.useEffectEvent(exitSearch);
|
|
1944
1953
|
react.useEffect(() => {
|
|
1945
1954
|
if (!inputElement)
|
|
1946
1955
|
return;
|
|
1947
1956
|
const handleKeyDown = (e) => {
|
|
1948
1957
|
if (e.key.toLowerCase() === 'escape')
|
|
1949
|
-
|
|
1958
|
+
onExitSearch();
|
|
1950
1959
|
};
|
|
1951
1960
|
inputElement.addEventListener('keydown', handleKeyDown);
|
|
1952
1961
|
return () => {
|
|
1953
1962
|
inputElement.removeEventListener('keydown', handleKeyDown);
|
|
1954
1963
|
};
|
|
1955
|
-
}, [
|
|
1964
|
+
}, [inputElement]);
|
|
1956
1965
|
return (jsxRuntime.jsxs("div", { className: clsx('str-video__search-input__container', {
|
|
1957
1966
|
'str-video__search-input__container--active': isActive,
|
|
1958
1967
|
}), 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" }))] }));
|
|
@@ -2023,10 +2032,19 @@ const CallParticipantListContentHeader = ({ userListType, setUserListType, }) =>
|
|
|
2023
2032
|
const ActiveUsersSearchResults = ({ searchQuery, activeUsersSearchFn: activeUsersSearchFnFromProps, debounceSearchInterval, }) => {
|
|
2024
2033
|
const { useParticipants } = videoReactBindings.useCallStateHooks();
|
|
2025
2034
|
const participants = useParticipants({ sortBy: videoClient.name });
|
|
2035
|
+
// cache: each distinct name is normalized once
|
|
2036
|
+
const normalizedNameCache = react.useRef(null);
|
|
2026
2037
|
const activeUsersSearchFn = react.useCallback(async (queryString) => {
|
|
2027
2038
|
const normalizedQuery = normalizeString(queryString);
|
|
2028
|
-
const
|
|
2029
|
-
return participants.filter((p) =>
|
|
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 } = videoReactBindings.useCallStateHooks();
|
|
2040
2058
|
const blockedUsers = useCallBlockedUserIds();
|
|
2041
2059
|
const blockedUsersSearchFn = react.useCallback(async (queryString) => {
|
|
2042
|
-
const
|
|
2043
|
-
return blockedUsers.filter((userId) => userId.
|
|
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,
|
|
@@ -2406,7 +2424,7 @@ const SpeechIndicator = () => {
|
|
|
2406
2424
|
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" })] }));
|
|
2407
2425
|
};
|
|
2408
2426
|
|
|
2409
|
-
const ParticipantView = react.forwardRef(function ParticipantView({ participant, trackType = 'videoTrack', mirror, muteAudio, refs: { setVideoElement, setVideoPlaceholderElement } = {}, className, VideoPlaceholder, PictureInPicturePlaceholder, ParticipantViewUI = DefaultParticipantViewUI, }, ref) {
|
|
2427
|
+
const ParticipantView = react.memo(react.forwardRef(function ParticipantView({ participant, trackType = 'videoTrack', mirror, muteAudio, refs: { setVideoElement, setVideoPlaceholderElement } = {}, className, VideoPlaceholder, PictureInPicturePlaceholder, ParticipantViewUI = DefaultParticipantViewUI, }, ref) {
|
|
2410
2428
|
const { isLocalParticipant, isSpeaking, isDominantSpeaker, sessionId } = participant;
|
|
2411
2429
|
const hasAudioTrack = videoClient.hasAudio(participant);
|
|
2412
2430
|
const hasVideoTrack = videoClient.hasVideo(participant);
|
|
@@ -2450,7 +2468,7 @@ const ParticipantView = react.forwardRef(function ParticipantView({ participant,
|
|
|
2450
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: 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 ||
|
|
2451
2469
|
trackType !== 'videoTrack' ||
|
|
2452
2470
|
isParticipantVideoEnabled(participant.sessionId), mirror: mirror, autoPlay: true }), isComponentType(ParticipantViewUI) ? (jsxRuntime.jsx(ParticipantViewUI, {})) : (ParticipantViewUI)] }) }));
|
|
2453
|
-
});
|
|
2471
|
+
}));
|
|
2454
2472
|
ParticipantView.displayName = 'ParticipantView';
|
|
2455
2473
|
|
|
2456
2474
|
// re-exporting the StreamCallProvider as StreamCall
|
|
@@ -2668,20 +2686,20 @@ function checkConditions(obj, conditions) {
|
|
|
2668
2686
|
const eqOperatorValue = isDateString(eqOperator.$eq)
|
|
2669
2687
|
? new Date(eqOperator.$eq).getTime()
|
|
2670
2688
|
: eqOperator.$eq;
|
|
2671
|
-
match
|
|
2689
|
+
match &&= eqOperatorValue === value;
|
|
2672
2690
|
}
|
|
2673
2691
|
else if (maybeOperator && '$neq' in operator) {
|
|
2674
2692
|
const neqOperator = operator;
|
|
2675
|
-
match
|
|
2693
|
+
match &&= neqOperator.$neq !== value;
|
|
2676
2694
|
}
|
|
2677
2695
|
else if (maybeOperator && '$in' in operator) {
|
|
2678
2696
|
const inOperator = operator;
|
|
2679
|
-
match
|
|
2697
|
+
match &&= inOperator.$in.includes(value);
|
|
2680
2698
|
}
|
|
2681
2699
|
else if (maybeOperator && '$contains' in operator) {
|
|
2682
2700
|
if (Array.isArray(value)) {
|
|
2683
2701
|
const containsOperator = operator;
|
|
2684
|
-
match
|
|
2702
|
+
match &&= value.includes(containsOperator.$contains);
|
|
2685
2703
|
}
|
|
2686
2704
|
else {
|
|
2687
2705
|
match = false;
|
|
@@ -2692,35 +2710,35 @@ function checkConditions(obj, conditions) {
|
|
|
2692
2710
|
const gtOperatorValue = isDateString(gtOperator.$gt)
|
|
2693
2711
|
? new Date(gtOperator.$gt).getTime()
|
|
2694
2712
|
: gtOperator.$gt;
|
|
2695
|
-
match
|
|
2713
|
+
match &&= value > gtOperatorValue;
|
|
2696
2714
|
}
|
|
2697
2715
|
else if (maybeOperator && '$gte' in operator) {
|
|
2698
2716
|
const gteOperator = operator;
|
|
2699
2717
|
const gteOperatorValue = isDateString(gteOperator.$gte)
|
|
2700
2718
|
? new Date(gteOperator.$gte).getTime()
|
|
2701
2719
|
: gteOperator.$gte;
|
|
2702
|
-
match
|
|
2720
|
+
match &&= value >= gteOperatorValue;
|
|
2703
2721
|
}
|
|
2704
2722
|
else if (maybeOperator && '$lt' in operator) {
|
|
2705
2723
|
const ltOperator = operator;
|
|
2706
2724
|
const ltOperatorValue = isDateString(ltOperator.$lt)
|
|
2707
2725
|
? new Date(ltOperator.$lt).getTime()
|
|
2708
2726
|
: ltOperator.$lt;
|
|
2709
|
-
match
|
|
2727
|
+
match &&= value < ltOperatorValue;
|
|
2710
2728
|
}
|
|
2711
2729
|
else if (maybeOperator && '$lte' in operator) {
|
|
2712
2730
|
const lteOperator = operator;
|
|
2713
2731
|
const lteOperatorValue = isDateString(lteOperator.$lte)
|
|
2714
2732
|
? new Date(lteOperator.$lte).getTime()
|
|
2715
2733
|
: lteOperator.$lte;
|
|
2716
|
-
match
|
|
2734
|
+
match &&= value <= lteOperatorValue;
|
|
2717
2735
|
// } else if (maybeOperator && '$autocomplete' in operator) {
|
|
2718
2736
|
// TODO: regexp solution maybe?
|
|
2719
2737
|
// match &&= false;
|
|
2720
2738
|
}
|
|
2721
2739
|
else {
|
|
2722
2740
|
const eqValue = operator;
|
|
2723
|
-
match
|
|
2741
|
+
match &&= eqValue === value;
|
|
2724
2742
|
}
|
|
2725
2743
|
if (!match) {
|
|
2726
2744
|
return false;
|
|
@@ -3250,12 +3268,12 @@ const useCanJoinEarly = () => {
|
|
|
3250
3268
|
const joinAheadTimeSeconds = settings?.backstage.join_ahead_time_seconds;
|
|
3251
3269
|
const [canJoinEarly, setCanJoinEarly] = react.useState(() => checkCanJoinEarly(startsAt, joinAheadTimeSeconds));
|
|
3252
3270
|
react.useEffect(() => {
|
|
3253
|
-
if (
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3271
|
+
if (canJoinEarly)
|
|
3272
|
+
return;
|
|
3273
|
+
const handle = setInterval(() => {
|
|
3274
|
+
setCanJoinEarly(checkCanJoinEarly(startsAt, joinAheadTimeSeconds));
|
|
3275
|
+
}, 1000);
|
|
3276
|
+
return () => clearInterval(handle);
|
|
3259
3277
|
}, [canJoinEarly, startsAt, joinAheadTimeSeconds]);
|
|
3260
3278
|
return canJoinEarly;
|
|
3261
3279
|
};
|
|
@@ -3266,7 +3284,7 @@ const checkCanJoinEarly = (startsAt, joinAheadTimeSeconds) => {
|
|
|
3266
3284
|
return Date.now() >= +startsAt - (joinAheadTimeSeconds ?? 0) * 1000;
|
|
3267
3285
|
};
|
|
3268
3286
|
|
|
3269
|
-
const [major, minor, patch] = ("1.
|
|
3287
|
+
const [major, minor, patch] = ("1.39.0").split('.');
|
|
3270
3288
|
videoClient.setSdkInfo({
|
|
3271
3289
|
type: videoClient.SfuModels.SdkType.REACT,
|
|
3272
3290
|
major,
|