@stream-io/video-react-sdk 0.3.46 → 0.4.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 (64) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +3 -2
  3. package/dist/index.cjs.js +328 -885
  4. package/dist/index.cjs.js.map +1 -1
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.es.js +329 -870
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/src/components/CallControls/ScreenShareButton.d.ts +1 -1
  9. package/dist/src/components/Notification/SpeakingWhileMutedNotification.d.ts +3 -0
  10. package/dist/src/components/{Video → VideoPreview}/VideoPreview.d.ts +1 -9
  11. package/dist/src/components/index.d.ts +1 -1
  12. package/dist/src/core/components/ParticipantView/ParticipantView.d.ts +3 -9
  13. package/dist/src/core/components/ParticipantView/ParticipantViewContext.d.ts +9 -0
  14. package/dist/src/core/components/ParticipantView/index.d.ts +1 -0
  15. package/dist/src/core/components/StreamCall/StreamCall.d.ts +2 -11
  16. package/dist/src/core/hooks/index.d.ts +0 -2
  17. package/dist/src/core/hooks/useDevices.d.ts +0 -99
  18. package/dist/src/core/index.d.ts +0 -1
  19. package/dist/src/hooks/index.d.ts +1 -3
  20. package/dist/src/hooks/usePersistedDevicePreferences.d.ts +13 -0
  21. package/dist/src/translations/index.d.ts +2 -0
  22. package/index.ts +2 -2
  23. package/package.json +3 -3
  24. package/src/components/CallControls/CallControls.tsx +6 -8
  25. package/src/components/CallControls/ScreenShareButton.tsx +17 -13
  26. package/src/components/CallControls/ToggleAudioButton.tsx +21 -24
  27. package/src/components/CallControls/ToggleAudioOutputButton.tsx +1 -1
  28. package/src/components/CallControls/ToggleVideoButton.tsx +21 -22
  29. package/src/components/CallParticipantsList/CallParticipantsList.tsx +1 -1
  30. package/src/components/DeviceSettings/DeviceSelectorAudio.tsx +20 -26
  31. package/src/components/DeviceSettings/DeviceSelectorVideo.tsx +9 -8
  32. package/src/components/Icon/Icon.tsx +1 -1
  33. package/src/components/Notification/SpeakingWhileMutedNotification.tsx +5 -49
  34. package/src/components/VideoPreview/VideoPreview.tsx +67 -0
  35. package/src/components/index.ts +1 -1
  36. package/src/core/components/CallLayout/PaginatedGridLayout.tsx +2 -5
  37. package/src/core/components/ParticipantView/DefaultParticipantViewUI.tsx +7 -6
  38. package/src/core/components/ParticipantView/ParticipantView.tsx +2 -19
  39. package/src/core/components/ParticipantView/ParticipantViewContext.tsx +17 -0
  40. package/src/core/components/ParticipantView/index.ts +1 -0
  41. package/src/core/components/StreamCall/StreamCall.tsx +2 -28
  42. package/src/core/hooks/index.ts +0 -2
  43. package/src/core/hooks/useDevices.ts +0 -195
  44. package/src/core/index.ts +0 -1
  45. package/src/hooks/index.ts +1 -3
  46. package/src/hooks/usePersistedDevicePreferences.ts +118 -0
  47. package/src/translations/en.json +3 -0
  48. package/dist/src/core/contexts/MediaDevicesContext.d.ts +0 -180
  49. package/dist/src/core/contexts/index.d.ts +0 -1
  50. package/dist/src/core/hooks/useAudioPublisher.d.ts +0 -12
  51. package/dist/src/core/hooks/useVideoPublisher.d.ts +0 -12
  52. package/dist/src/hooks/useToggleAudioMuteState.d.ts +0 -4
  53. package/dist/src/hooks/useToggleScreenShare.d.ts +0 -5
  54. package/dist/src/hooks/useToggleVideoMuteState.d.ts +0 -4
  55. package/src/components/Video/VideoPreview.tsx +0 -152
  56. package/src/core/contexts/MediaDevicesContext.tsx +0 -416
  57. package/src/core/contexts/index.ts +0 -1
  58. package/src/core/hooks/useAudioPublisher.ts +0 -146
  59. package/src/core/hooks/useVideoPublisher.ts +0 -177
  60. package/src/hooks/useToggleAudioMuteState.ts +0 -34
  61. package/src/hooks/useToggleScreenShare.ts +0 -43
  62. package/src/hooks/useToggleVideoMuteState.ts +0 -34
  63. /package/dist/src/components/{Video → VideoPreview}/index.d.ts +0 -0
  64. /package/src/components/{Video → VideoPreview}/index.ts +0 -0
@@ -1,177 +0,0 @@
1
- import { useCallback, useEffect, useRef } from 'react';
2
- import { map } from 'rxjs/operators';
3
- import {
4
- CallingState,
5
- getVideoStream,
6
- OwnCapability,
7
- SfuModels,
8
- VideoSettingsCameraFacingEnum,
9
- watchForAddedDefaultVideoDevice,
10
- watchForDisconnectedVideoDevice,
11
- } from '@stream-io/video-client';
12
- import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
13
- import { useDebugPreferredVideoCodec } from '../../components/Debug/useIsDebugMode';
14
- import { useHasBrowserPermissions } from './useDevices';
15
-
16
- /**
17
- * @internal
18
- */
19
- export type VideoPublisherInit = {
20
- initialVideoMuted?: boolean;
21
- videoDeviceId?: string;
22
- };
23
-
24
- /**
25
- * @internal
26
- * @category Device Management
27
- */
28
- export const useVideoPublisher = ({
29
- initialVideoMuted,
30
- videoDeviceId,
31
- }: VideoPublisherInit) => {
32
- const call = useCall();
33
- const {
34
- useCallState,
35
- useCallCallingState,
36
- useLocalParticipant,
37
- useCallSettings,
38
- } = useCallStateHooks();
39
- const callState = useCallState();
40
- const callingState = useCallCallingState();
41
- const participant = useLocalParticipant();
42
- const hasBrowserPermissionVideoInput = useHasBrowserPermissions(
43
- 'camera' as PermissionName,
44
- );
45
- const { localParticipant$ } = callState;
46
-
47
- const preferredCodec = useDebugPreferredVideoCodec();
48
- const isPublishingVideo = participant?.publishedTracks.includes(
49
- SfuModels.TrackType.VIDEO,
50
- );
51
-
52
- const settings = useCallSettings();
53
- const videoSettings = settings?.video;
54
- const targetResolution = videoSettings?.target_resolution;
55
- const publishVideoStream = useCallback(async () => {
56
- if (!call) return;
57
- if (!call.permissionsContext.hasPermission(OwnCapability.SEND_VIDEO)) {
58
- throw new Error(`No permission to publish video`);
59
- }
60
- try {
61
- const videoStream = await getVideoStream({
62
- deviceId: videoDeviceId,
63
- width: targetResolution?.width,
64
- height: targetResolution?.height,
65
- facingMode: toFacingMode(videoSettings?.camera_facing),
66
- });
67
- await call.publishVideoStream(videoStream, { preferredCodec });
68
- } catch (e) {
69
- console.log('Failed to publish video stream', e);
70
- }
71
- }, [
72
- call,
73
- preferredCodec,
74
- targetResolution?.height,
75
- targetResolution?.width,
76
- videoDeviceId,
77
- videoSettings?.camera_facing,
78
- ]);
79
-
80
- const lastVideoDeviceId = useRef(videoDeviceId);
81
- useEffect(() => {
82
- if (
83
- callingState === CallingState.JOINED &&
84
- videoDeviceId !== lastVideoDeviceId.current
85
- ) {
86
- lastVideoDeviceId.current = videoDeviceId;
87
- publishVideoStream().catch((e) => {
88
- console.error('Failed to publish video stream', e);
89
- });
90
- }
91
- }, [publishVideoStream, videoDeviceId, callingState]);
92
-
93
- const initialPublishRun = useRef(false);
94
- useEffect(() => {
95
- if (
96
- callingState === CallingState.JOINED &&
97
- !initialPublishRun.current &&
98
- !initialVideoMuted
99
- ) {
100
- // automatic publishing should happen only when joining the call
101
- // from the lobby, and the video is not muted
102
- publishVideoStream().catch((e) => {
103
- console.error('Failed to publish video stream', e);
104
- });
105
- initialPublishRun.current = true;
106
- }
107
- }, [callingState, initialVideoMuted, publishVideoStream]);
108
-
109
- useEffect(() => {
110
- if (!localParticipant$ || !hasBrowserPermissionVideoInput) return;
111
- const subscription = watchForDisconnectedVideoDevice(
112
- localParticipant$.pipe(map((p) => p?.videoDeviceId)),
113
- ).subscribe(async () => {
114
- if (!call) return;
115
- call.setVideoDevice(undefined);
116
- await call.stopPublish(SfuModels.TrackType.VIDEO);
117
- });
118
- return () => {
119
- subscription.unsubscribe();
120
- };
121
- }, [hasBrowserPermissionVideoInput, localParticipant$, call]);
122
-
123
- useEffect(() => {
124
- if (!participant?.videoStream || !call || !isPublishingVideo) return;
125
-
126
- const [track] = participant.videoStream.getVideoTracks();
127
- const selectedVideoDeviceId = track.getSettings().deviceId;
128
-
129
- const republishDefaultDevice = watchForAddedDefaultVideoDevice().subscribe(
130
- async () => {
131
- if (
132
- !(
133
- call &&
134
- participant.videoStream &&
135
- selectedVideoDeviceId === 'default'
136
- )
137
- )
138
- return;
139
- // We need to stop the original track first in order
140
- // we can retrieve the new default device stream
141
- track.stop();
142
- const videoStream = await getVideoStream({
143
- deviceId: 'default',
144
- });
145
- await call.publishVideoStream(videoStream);
146
- },
147
- );
148
-
149
- const handleTrackEnded = async () => {
150
- if (selectedVideoDeviceId === videoDeviceId) {
151
- const videoStream = await getVideoStream({
152
- deviceId: videoDeviceId,
153
- });
154
- await call.publishVideoStream(videoStream);
155
- }
156
- };
157
-
158
- track.addEventListener('ended', handleTrackEnded);
159
- return () => {
160
- track.removeEventListener('ended', handleTrackEnded);
161
- republishDefaultDevice.unsubscribe();
162
- };
163
- }, [videoDeviceId, call, participant?.videoStream, isPublishingVideo]);
164
-
165
- return publishVideoStream;
166
- };
167
-
168
- const toFacingMode = (value: VideoSettingsCameraFacingEnum | undefined) => {
169
- switch (value) {
170
- case VideoSettingsCameraFacingEnum.FRONT:
171
- return 'user';
172
- case VideoSettingsCameraFacingEnum.BACK:
173
- return 'environment';
174
- default:
175
- return undefined;
176
- }
177
- };
@@ -1,34 +0,0 @@
1
- import { useCallback, useRef } from 'react';
2
- import { useCallStateHooks } from '@stream-io/video-react-bindings';
3
- import { OwnCapability, SfuModels } from '@stream-io/video-client';
4
-
5
- import { useMediaDevices } from '../core';
6
- import { useRequestPermission } from './useRequestPermission';
7
-
8
- export const useToggleAudioMuteState = () => {
9
- const { publishAudioStream, stopPublishingAudio } = useMediaDevices();
10
- const { useLocalParticipant } = useCallStateHooks();
11
- const localParticipant = useLocalParticipant();
12
-
13
- const { isAwaitingPermission, requestPermission } = useRequestPermission(
14
- OwnCapability.SEND_AUDIO,
15
- );
16
-
17
- // to keep the toggle function as stable as possible
18
- const isAudioMutedReference = useRef(false);
19
-
20
- isAudioMutedReference.current = !localParticipant?.publishedTracks.includes(
21
- SfuModels.TrackType.AUDIO,
22
- );
23
-
24
- const toggleAudioMuteState = useCallback(async () => {
25
- if (isAudioMutedReference.current) {
26
- const canPublish = await requestPermission();
27
- if (canPublish) return publishAudioStream();
28
- }
29
-
30
- if (!isAudioMutedReference.current) await stopPublishingAudio();
31
- }, [publishAudioStream, requestPermission, stopPublishingAudio]);
32
-
33
- return { toggleAudioMuteState, isAwaitingPermission };
34
- };
@@ -1,43 +0,0 @@
1
- import { useCallback, useRef } from 'react';
2
- import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
3
- import {
4
- getScreenShareStream,
5
- OwnCapability,
6
- SfuModels,
7
- } from '@stream-io/video-client';
8
- import { useRequestPermission } from './useRequestPermission';
9
-
10
- export const useToggleScreenShare = () => {
11
- const { useLocalParticipant } = useCallStateHooks();
12
- const localParticipant = useLocalParticipant();
13
- const call = useCall();
14
- const isScreenSharingReference = useRef(false);
15
- const { isAwaitingPermission, requestPermission } = useRequestPermission(
16
- OwnCapability.SCREENSHARE,
17
- );
18
-
19
- const isScreenSharing = !!localParticipant?.publishedTracks.includes(
20
- SfuModels.TrackType.SCREEN_SHARE,
21
- );
22
-
23
- isScreenSharingReference.current = isScreenSharing;
24
-
25
- const toggleScreenShare = useCallback(async () => {
26
- if (!isScreenSharingReference.current) {
27
- const canPublish = await requestPermission();
28
- if (!canPublish) return;
29
-
30
- const stream = await getScreenShareStream().catch((e) => {
31
- console.log(`Can't share screen: ${e}`);
32
- });
33
-
34
- if (stream) {
35
- return call?.publishScreenShareStream(stream);
36
- }
37
- }
38
-
39
- await call?.stopPublish(SfuModels.TrackType.SCREEN_SHARE);
40
- }, [call, requestPermission]);
41
-
42
- return { toggleScreenShare, isAwaitingPermission, isScreenSharing };
43
- };
@@ -1,34 +0,0 @@
1
- import { useCallback, useRef } from 'react';
2
- import { useCallStateHooks } from '@stream-io/video-react-bindings';
3
- import { OwnCapability, SfuModels } from '@stream-io/video-client';
4
-
5
- import { useMediaDevices } from '../core';
6
- import { useRequestPermission } from './useRequestPermission';
7
-
8
- export const useToggleVideoMuteState = () => {
9
- const { publishVideoStream, stopPublishingVideo } = useMediaDevices();
10
- const { useLocalParticipant } = useCallStateHooks();
11
- const localParticipant = useLocalParticipant();
12
-
13
- const { isAwaitingPermission, requestPermission } = useRequestPermission(
14
- OwnCapability.SEND_VIDEO,
15
- );
16
-
17
- // to keep the toggle function as stable as possible
18
- const isVideoMutedReference = useRef(false);
19
-
20
- isVideoMutedReference.current = !localParticipant?.publishedTracks.includes(
21
- SfuModels.TrackType.VIDEO,
22
- );
23
-
24
- const toggleVideoMuteState = useCallback(async () => {
25
- if (isVideoMutedReference.current) {
26
- const canPublish = await requestPermission();
27
- if (canPublish) return publishVideoStream();
28
- }
29
-
30
- if (!isVideoMutedReference.current) await stopPublishingVideo();
31
- }, [publishVideoStream, requestPermission, stopPublishingVideo]);
32
-
33
- return { toggleVideoMuteState, isAwaitingPermission };
34
- };
File without changes