@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
|
@@ -8,5 +8,5 @@ type CallParticipantListingItemProps = {
|
|
|
8
8
|
participant: StreamVideoParticipant;
|
|
9
9
|
}>;
|
|
10
10
|
};
|
|
11
|
-
export declare const CallParticipantListingItem: ({ participant, DisplayName, }: CallParticipantListingItemProps) => import("react/jsx-runtime").JSX.Element
|
|
11
|
+
export declare const CallParticipantListingItem: import("react").MemoExoticComponent<({ participant, DisplayName, }: CallParticipantListingItemProps) => import("react/jsx-runtime").JSX.Element>;
|
|
12
12
|
export {};
|
|
@@ -11,7 +11,4 @@ export type AudioProps = ComponentPropsWithoutRef<'audio'> & {
|
|
|
11
11
|
*/
|
|
12
12
|
trackType?: AudioTrackType;
|
|
13
13
|
};
|
|
14
|
-
export declare const Audio: {
|
|
15
|
-
({ participant, trackType, ...rest }: AudioProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
-
displayName: string;
|
|
17
|
-
};
|
|
14
|
+
export declare const Audio: import("react").MemoExoticComponent<({ participant, trackType, ...rest }: AudioProps) => import("react/jsx-runtime").JSX.Element>;
|
|
@@ -42,7 +42,7 @@ export type ParticipantViewProps = {
|
|
|
42
42
|
*/
|
|
43
43
|
className?: string;
|
|
44
44
|
} & Pick<VideoProps, 'VideoPlaceholder' | 'PictureInPicturePlaceholder'>;
|
|
45
|
-
export declare const ParticipantView: import("react").
|
|
45
|
+
export declare const ParticipantView: import("react").NamedExoticComponent<{
|
|
46
46
|
/**
|
|
47
47
|
* The participant whose video/audio stream we want to play.
|
|
48
48
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stream-io/video-react-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.39.1",
|
|
4
4
|
"main": "./dist/index.cjs.js",
|
|
5
5
|
"module": "./dist/index.es.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -45,9 +45,9 @@
|
|
|
45
45
|
],
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@floating-ui/react": "^0.27.19",
|
|
48
|
-
"@stream-io/video-client": "1.
|
|
49
|
-
"@stream-io/video-filters-web": "0.
|
|
50
|
-
"@stream-io/video-react-bindings": "1.
|
|
48
|
+
"@stream-io/video-client": "1.55.1",
|
|
49
|
+
"@stream-io/video-filters-web": "0.8.1",
|
|
50
|
+
"@stream-io/video-react-bindings": "1.18.1",
|
|
51
51
|
"chart.js": "^4.5.1",
|
|
52
52
|
"clsx": "^2.1.1",
|
|
53
53
|
"react-chartjs-2": "^5.3.1"
|
|
@@ -60,14 +60,16 @@
|
|
|
60
60
|
"@rollup/plugin-json": "^6.1.0",
|
|
61
61
|
"@rollup/plugin-replace": "^6.0.3",
|
|
62
62
|
"@rollup/plugin-typescript": "^12.3.0",
|
|
63
|
-
"@stream-io/audio-filters-web": "^0.
|
|
64
|
-
"@stream-io/
|
|
63
|
+
"@stream-io/audio-filters-web": "^0.9.1",
|
|
64
|
+
"@stream-io/typescript-config": "^0.0.0",
|
|
65
|
+
"@stream-io/video-styling": "^1.14.2",
|
|
66
|
+
"@types/node": "^25.9.3",
|
|
65
67
|
"@types/react": "~19.2.15",
|
|
66
68
|
"@types/react-dom": "~19.2.3",
|
|
67
69
|
"react": "19.2.3",
|
|
68
70
|
"react-dom": "19.2.3",
|
|
69
71
|
"rimraf": "^6.1.3",
|
|
70
72
|
"rollup": "^4.60.4",
|
|
71
|
-
"typescript": "^
|
|
73
|
+
"typescript": "^6.0.3"
|
|
72
74
|
}
|
|
73
75
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import clsx from 'clsx';
|
|
2
|
-
import { ComponentProps, ComponentType, forwardRef } from 'react';
|
|
2
|
+
import { ComponentProps, ComponentType, forwardRef, memo } from 'react';
|
|
3
3
|
import { useConnectedUser, useI18n } from '@stream-io/video-react-bindings';
|
|
4
4
|
import {
|
|
5
5
|
hasAudio,
|
|
@@ -20,60 +20,64 @@ type CallParticipantListingItemProps = {
|
|
|
20
20
|
/** Custom component used to display participant's name */
|
|
21
21
|
DisplayName?: ComponentType<{ participant: StreamVideoParticipant }>;
|
|
22
22
|
};
|
|
23
|
-
export const CallParticipantListingItem = (
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
export const CallParticipantListingItem = memo(
|
|
24
|
+
function CallParticipantListingItem({
|
|
25
|
+
participant,
|
|
26
|
+
DisplayName = DefaultDisplayName,
|
|
27
|
+
}: CallParticipantListingItemProps) {
|
|
28
|
+
const isAudioOn = hasAudio(participant);
|
|
29
|
+
const isVideoOn = hasVideo(participant);
|
|
30
|
+
const isPinnedOn = isPinned(participant);
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
const { t } = useI18n();
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
<MediaIndicator
|
|
39
|
-
title={isAudioOn ? t('Microphone on') : t('Microphone off')}
|
|
40
|
-
className={clsx(
|
|
41
|
-
'str-video__participant-listing-item__icon',
|
|
42
|
-
`str-video__participant-listing-item__icon-${
|
|
43
|
-
isAudioOn ? 'mic' : 'mic-off'
|
|
44
|
-
}`,
|
|
45
|
-
)}
|
|
46
|
-
/>
|
|
47
|
-
<MediaIndicator
|
|
48
|
-
title={isVideoOn ? t('Camera on') : t('Camera off')}
|
|
49
|
-
className={clsx(
|
|
50
|
-
'str-video__participant-listing-item__icon',
|
|
51
|
-
`str-video__participant-listing-item__icon-${
|
|
52
|
-
isVideoOn ? 'camera' : 'camera-off'
|
|
53
|
-
}`,
|
|
54
|
-
)}
|
|
55
|
-
/>
|
|
56
|
-
{isPinnedOn && (
|
|
34
|
+
return (
|
|
35
|
+
<div className="str-video__participant-listing-item">
|
|
36
|
+
<Avatar name={participant.name} imageSrc={participant.image} />
|
|
37
|
+
<DisplayName participant={participant} />
|
|
38
|
+
<div className="str-video__participant-listing-item__media-indicator-group">
|
|
57
39
|
<MediaIndicator
|
|
58
|
-
title={t('
|
|
40
|
+
title={isAudioOn ? t('Microphone on') : t('Microphone off')}
|
|
59
41
|
className={clsx(
|
|
60
42
|
'str-video__participant-listing-item__icon',
|
|
61
|
-
|
|
43
|
+
`str-video__participant-listing-item__icon-${
|
|
44
|
+
isAudioOn ? 'mic' : 'mic-off'
|
|
45
|
+
}`,
|
|
62
46
|
)}
|
|
63
47
|
/>
|
|
64
|
-
|
|
48
|
+
<MediaIndicator
|
|
49
|
+
title={isVideoOn ? t('Camera on') : t('Camera off')}
|
|
50
|
+
className={clsx(
|
|
51
|
+
'str-video__participant-listing-item__icon',
|
|
52
|
+
`str-video__participant-listing-item__icon-${
|
|
53
|
+
isVideoOn ? 'camera' : 'camera-off'
|
|
54
|
+
}`,
|
|
55
|
+
)}
|
|
56
|
+
/>
|
|
57
|
+
{isPinnedOn && (
|
|
58
|
+
<MediaIndicator
|
|
59
|
+
title={t('Pinned')}
|
|
60
|
+
className={clsx(
|
|
61
|
+
'str-video__participant-listing-item__icon',
|
|
62
|
+
'str-video__participant-listing-item__icon-pinned',
|
|
63
|
+
)}
|
|
64
|
+
/>
|
|
65
|
+
)}
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
<MenuToggle placement="bottom-end" ToggleButton={ToggleButton}>
|
|
68
|
+
<ParticipantViewContext.Provider
|
|
69
|
+
value={{ participant, trackType: 'none' }}
|
|
70
|
+
>
|
|
71
|
+
<ParticipantActionsContextMenu />
|
|
72
|
+
</ParticipantViewContext.Provider>
|
|
73
|
+
</MenuToggle>
|
|
74
|
+
</div>
|
|
73
75
|
</div>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
);
|
|
77
|
+
},
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
CallParticipantListingItem.displayName = 'CallParticipantListingItem';
|
|
77
81
|
|
|
78
82
|
const MediaIndicator = (props: ComponentProps<'div'>) => (
|
|
79
83
|
<WithTooltip {...props} />
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
forwardRef,
|
|
4
4
|
SetStateAction,
|
|
5
5
|
useCallback,
|
|
6
|
+
useRef,
|
|
6
7
|
useState,
|
|
7
8
|
} from 'react';
|
|
8
9
|
import {
|
|
@@ -150,13 +151,21 @@ const ActiveUsersSearchResults = ({
|
|
|
150
151
|
const { useParticipants } = useCallStateHooks();
|
|
151
152
|
const participants = useParticipants({ sortBy: name });
|
|
152
153
|
|
|
154
|
+
// cache: each distinct name is normalized once
|
|
155
|
+
const normalizedNameCache = useRef<Map<string, string> | null>(null);
|
|
156
|
+
|
|
153
157
|
const activeUsersSearchFn = useCallback(
|
|
154
158
|
async (queryString: string) => {
|
|
155
159
|
const normalizedQuery = normalizeString(queryString);
|
|
156
|
-
const
|
|
157
|
-
return participants.filter((p) =>
|
|
158
|
-
|
|
159
|
-
|
|
160
|
+
const cache = (normalizedNameCache.current ??= new Map<string, string>());
|
|
161
|
+
return participants.filter((p) => {
|
|
162
|
+
let normalizedName = cache.get(p.name);
|
|
163
|
+
if (normalizedName === undefined) {
|
|
164
|
+
normalizedName = normalizeString(p.name);
|
|
165
|
+
cache.set(p.name, normalizedName);
|
|
166
|
+
}
|
|
167
|
+
return normalizedName.includes(normalizedQuery);
|
|
168
|
+
});
|
|
160
169
|
},
|
|
161
170
|
[participants],
|
|
162
171
|
);
|
|
@@ -191,8 +200,10 @@ const BlockedUsersSearchResults = ({
|
|
|
191
200
|
|
|
192
201
|
const blockedUsersSearchFn = useCallback(
|
|
193
202
|
async (queryString: string) => {
|
|
194
|
-
const
|
|
195
|
-
return blockedUsers.filter((userId) =>
|
|
203
|
+
const normalizedQuery = queryString.toLowerCase();
|
|
204
|
+
return blockedUsers.filter((userId) =>
|
|
205
|
+
userId.toLowerCase().includes(normalizedQuery),
|
|
206
|
+
);
|
|
196
207
|
},
|
|
197
208
|
[blockedUsers],
|
|
198
209
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect,
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
2
|
import { createSoundDetector } from '@stream-io/video-client';
|
|
3
3
|
import { useCallStateHooks } from '@stream-io/video-react-bindings';
|
|
4
4
|
import { Icon } from '../Icon';
|
|
@@ -6,13 +6,17 @@ import { Icon } from '../Icon';
|
|
|
6
6
|
export const AudioVolumeIndicator = () => {
|
|
7
7
|
const { useMicrophoneState } = useCallStateHooks();
|
|
8
8
|
const { isEnabled, mediaStream } = useMicrophoneState();
|
|
9
|
-
const
|
|
9
|
+
const barRef = useRef<HTMLDivElement>(null);
|
|
10
10
|
|
|
11
11
|
useEffect(() => {
|
|
12
12
|
if (!isEnabled || !mediaStream) return;
|
|
13
13
|
const disposeSoundDetector = createSoundDetector(
|
|
14
14
|
mediaStream,
|
|
15
|
-
({ audioLevel: al }) =>
|
|
15
|
+
({ audioLevel: al }) => {
|
|
16
|
+
if (barRef.current) {
|
|
17
|
+
barRef.current.style.transform = `scaleX(${al / 100})`;
|
|
18
|
+
}
|
|
19
|
+
},
|
|
16
20
|
{ detectionFrequencyInMs: 80, destroyStreamOnStop: false },
|
|
17
21
|
);
|
|
18
22
|
return () => {
|
|
@@ -25,8 +29,8 @@ export const AudioVolumeIndicator = () => {
|
|
|
25
29
|
<Icon icon={isEnabled ? 'mic' : 'mic-off'} />
|
|
26
30
|
<div className="str-video__audio-volume-indicator__bar">
|
|
27
31
|
<div
|
|
32
|
+
ref={barRef}
|
|
28
33
|
className="str-video__audio-volume-indicator__bar-value"
|
|
29
|
-
style={{ transform: `scaleX(${audioLevel / 100})` }}
|
|
30
34
|
/>
|
|
31
35
|
</div>
|
|
32
36
|
</div>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ComponentProps, useEffect, useState } from 'react';
|
|
2
|
+
import { useEffectEvent } from '@stream-io/video-react-bindings';
|
|
2
3
|
import clsx from 'clsx';
|
|
3
4
|
|
|
4
5
|
type SearchInputProps = ComponentProps<'input'> & {
|
|
@@ -20,11 +21,12 @@ export const SearchInput = ({
|
|
|
20
21
|
const [inputElement, setInputElement] = useState<HTMLInputElement | null>(
|
|
21
22
|
null,
|
|
22
23
|
);
|
|
24
|
+
const onExitSearch = useEffectEvent(exitSearch);
|
|
23
25
|
useEffect(() => {
|
|
24
26
|
if (!inputElement) return;
|
|
25
27
|
|
|
26
28
|
const handleKeyDown = (e: KeyboardEvent) => {
|
|
27
|
-
if (e.key.toLowerCase() === 'escape')
|
|
29
|
+
if (e.key.toLowerCase() === 'escape') onExitSearch();
|
|
28
30
|
};
|
|
29
31
|
|
|
30
32
|
inputElement.addEventListener('keydown', handleKeyDown);
|
|
@@ -32,7 +34,7 @@ export const SearchInput = ({
|
|
|
32
34
|
return () => {
|
|
33
35
|
inputElement.removeEventListener('keydown', handleKeyDown);
|
|
34
36
|
};
|
|
35
|
-
}, [
|
|
37
|
+
}, [inputElement]);
|
|
36
38
|
|
|
37
39
|
return (
|
|
38
40
|
<div
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ComponentPropsWithoutRef, useEffect, useState } from 'react';
|
|
1
|
+
import { ComponentPropsWithoutRef, memo, useEffect, useState } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
AudioTrackType,
|
|
4
4
|
StreamVideoParticipant,
|
|
@@ -18,11 +18,11 @@ export type AudioProps = ComponentPropsWithoutRef<'audio'> & {
|
|
|
18
18
|
trackType?: AudioTrackType;
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
export const Audio = ({
|
|
21
|
+
export const Audio = memo(function Audio({
|
|
22
22
|
participant,
|
|
23
23
|
trackType = 'audioTrack',
|
|
24
24
|
...rest
|
|
25
|
-
}: AudioProps)
|
|
25
|
+
}: AudioProps) {
|
|
26
26
|
const call = useCall();
|
|
27
27
|
const [audioElement, setAudioElement] = useState<HTMLAudioElement | null>(
|
|
28
28
|
null,
|
|
@@ -47,6 +47,6 @@ export const Audio = ({
|
|
|
47
47
|
data-track-type={trackType}
|
|
48
48
|
/>
|
|
49
49
|
);
|
|
50
|
-
};
|
|
50
|
+
});
|
|
51
51
|
|
|
52
52
|
Audio.displayName = 'Audio';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ComponentType,
|
|
3
3
|
forwardRef,
|
|
4
|
+
memo,
|
|
4
5
|
ReactElement,
|
|
5
6
|
useMemo,
|
|
6
7
|
useState,
|
|
@@ -70,8 +71,8 @@ export type ParticipantViewProps = {
|
|
|
70
71
|
className?: string;
|
|
71
72
|
} & Pick<VideoProps, 'VideoPlaceholder' | 'PictureInPicturePlaceholder'>;
|
|
72
73
|
|
|
73
|
-
export const ParticipantView =
|
|
74
|
-
function ParticipantView(
|
|
74
|
+
export const ParticipantView = memo(
|
|
75
|
+
forwardRef<HTMLDivElement, ParticipantViewProps>(function ParticipantView(
|
|
75
76
|
{
|
|
76
77
|
participant,
|
|
77
78
|
trackType = 'videoTrack',
|
|
@@ -195,7 +196,7 @@ export const ParticipantView = forwardRef<HTMLDivElement, ParticipantViewProps>(
|
|
|
195
196
|
</ParticipantViewContext.Provider>
|
|
196
197
|
</div>
|
|
197
198
|
);
|
|
198
|
-
},
|
|
199
|
+
}),
|
|
199
200
|
);
|
|
200
201
|
|
|
201
202
|
ParticipantView.displayName = 'ParticipantView';
|
|
@@ -22,9 +22,10 @@ export const BaseVideo = forwardRef<HTMLVideoElement, BaseVideoProps>(
|
|
|
22
22
|
if (stream === videoElement.srcObject) return;
|
|
23
23
|
|
|
24
24
|
videoElement.srcObject = stream;
|
|
25
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
25
26
|
if (Browsers.isSafari() || Browsers.isFirefox()) {
|
|
26
27
|
// Firefox and Safari have some timing issue
|
|
27
|
-
setTimeout(() => {
|
|
28
|
+
timeoutId = setTimeout(() => {
|
|
28
29
|
videoElement.srcObject = stream;
|
|
29
30
|
videoElement.play().catch((e) => {
|
|
30
31
|
console.error(`Failed to play stream`, e);
|
|
@@ -33,6 +34,7 @@ export const BaseVideo = forwardRef<HTMLVideoElement, BaseVideoProps>(
|
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
return () => {
|
|
37
|
+
clearTimeout(timeoutId);
|
|
36
38
|
videoElement.pause();
|
|
37
39
|
videoElement.srcObject = null;
|
|
38
40
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ComponentPropsWithoutRef,
|
|
3
3
|
ComponentType,
|
|
4
|
+
CSSProperties,
|
|
4
5
|
useEffect,
|
|
5
6
|
useLayoutEffect,
|
|
6
7
|
useState,
|
|
@@ -25,6 +26,8 @@ import {
|
|
|
25
26
|
PictureInPicturePlaceholderProps,
|
|
26
27
|
} from './DefaultPictureInPicturePlaceholder';
|
|
27
28
|
|
|
29
|
+
const ABSOLUTE_POSITION_STYLE: CSSProperties = { position: 'absolute' };
|
|
30
|
+
|
|
28
31
|
export type VideoProps = ComponentPropsWithoutRef<'video'> & {
|
|
29
32
|
/**
|
|
30
33
|
* Pass false to disable rendering video and render fallback
|
|
@@ -203,14 +206,14 @@ export const Video = ({
|
|
|
203
206
|
)}
|
|
204
207
|
{isPiP && PictureInPicturePlaceholder && (
|
|
205
208
|
<PictureInPicturePlaceholder
|
|
206
|
-
style={
|
|
209
|
+
style={ABSOLUTE_POSITION_STYLE}
|
|
207
210
|
participant={participant}
|
|
208
211
|
/>
|
|
209
212
|
)}
|
|
210
213
|
{/* TODO: add condition to "hold" the placeholder until track unmutes as well */}
|
|
211
214
|
{(hasNoVideoOrInvisible || isVideoPaused) && VideoPlaceholder && (
|
|
212
215
|
<VideoPlaceholder
|
|
213
|
-
style={
|
|
216
|
+
style={ABSOLUTE_POSITION_STYLE}
|
|
214
217
|
participant={participant}
|
|
215
218
|
ref={refs?.setVideoPlaceholderElement}
|
|
216
219
|
/>
|
|
@@ -55,13 +55,12 @@ export const ViewerLobby = ({ onJoin }: ViewerLobbyProps) => {
|
|
|
55
55
|
}, [canJoin, autoJoin, onJoin]);
|
|
56
56
|
|
|
57
57
|
useEffect(() => {
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
if (canJoinEarly) return;
|
|
59
|
+
const handle = setInterval(() => {
|
|
60
|
+
setCanJoinEarly(checkCanJoinEarly(startsAt, joinAheadTimeSeconds));
|
|
61
|
+
}, 1000);
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
}
|
|
63
|
+
return () => clearInterval(handle);
|
|
65
64
|
}, [canJoinEarly, startsAt, joinAheadTimeSeconds]);
|
|
66
65
|
|
|
67
66
|
useEffect(() => {
|
|
@@ -33,6 +33,13 @@ export function useDragToScroll(
|
|
|
33
33
|
element: HTMLElement | null,
|
|
34
34
|
options: DragToScrollOptions = {},
|
|
35
35
|
) {
|
|
36
|
+
const {
|
|
37
|
+
enabled,
|
|
38
|
+
decay = 0.95,
|
|
39
|
+
minVelocity = 0.5,
|
|
40
|
+
dragThreshold = 5,
|
|
41
|
+
} = options;
|
|
42
|
+
|
|
36
43
|
const stateRef = useRef<DragScrollState>({
|
|
37
44
|
isDragging: false,
|
|
38
45
|
isPointerActive: false,
|
|
@@ -46,9 +53,7 @@ export function useDragToScroll(
|
|
|
46
53
|
});
|
|
47
54
|
|
|
48
55
|
useEffect(() => {
|
|
49
|
-
if (!element || !
|
|
50
|
-
|
|
51
|
-
const { decay = 0.95, minVelocity = 0.5, dragThreshold = 5 } = options;
|
|
56
|
+
if (!element || !enabled) return;
|
|
52
57
|
|
|
53
58
|
const state = stateRef.current;
|
|
54
59
|
|
|
@@ -158,5 +163,5 @@ export function useDragToScroll(
|
|
|
158
163
|
|
|
159
164
|
stopMomentum();
|
|
160
165
|
};
|
|
161
|
-
}, [element,
|
|
166
|
+
}, [element, enabled, decay, minVelocity, dragThreshold]);
|
|
162
167
|
}
|
|
@@ -40,7 +40,7 @@ export const useVerticalScrollPosition = (
|
|
|
40
40
|
const resizeObserver = new ResizeObserver(scrollHandler);
|
|
41
41
|
resizeObserver.observe(scrollElement);
|
|
42
42
|
|
|
43
|
-
scrollElement.addEventListener('scroll', scrollHandler);
|
|
43
|
+
scrollElement.addEventListener('scroll', scrollHandler, { passive: true });
|
|
44
44
|
return () => {
|
|
45
45
|
scrollElement.removeEventListener('scroll', scrollHandler);
|
|
46
46
|
resizeObserver.disconnect();
|
|
@@ -84,7 +84,7 @@ export const useHorizontalScrollPosition = (
|
|
|
84
84
|
const resizeObserver = new ResizeObserver(scrollHandler);
|
|
85
85
|
resizeObserver.observe(scrollElement);
|
|
86
86
|
|
|
87
|
-
scrollElement.addEventListener('scroll', scrollHandler);
|
|
87
|
+
scrollElement.addEventListener('scroll', scrollHandler, { passive: true });
|
|
88
88
|
return () => {
|
|
89
89
|
scrollElement.removeEventListener('scroll', scrollHandler);
|
|
90
90
|
resizeObserver.disconnect();
|
|
@@ -143,13 +143,12 @@ const useCanJoinEarly = () => {
|
|
|
143
143
|
);
|
|
144
144
|
|
|
145
145
|
useEffect(() => {
|
|
146
|
-
if (
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
146
|
+
if (canJoinEarly) return;
|
|
147
|
+
const handle = setInterval(() => {
|
|
148
|
+
setCanJoinEarly(checkCanJoinEarly(startsAt, joinAheadTimeSeconds));
|
|
149
|
+
}, 1000);
|
|
150
150
|
|
|
151
|
-
|
|
152
|
-
}
|
|
151
|
+
return () => clearInterval(handle);
|
|
153
152
|
}, [canJoinEarly, startsAt, joinAheadTimeSeconds]);
|
|
154
153
|
|
|
155
154
|
return canJoinEarly;
|