@stream-io/video-react-sdk 0.0.92 → 0.1.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 (48) hide show
  1. package/CHANGELOG.md +26 -15
  2. package/dist/src/components/CallControls/ReactionsButton.js +3 -2
  3. package/dist/src/components/CallControls/ReactionsButton.js.map +1 -1
  4. package/dist/src/components/CallControls/RecordCallButton.js +7 -40
  5. package/dist/src/components/CallControls/RecordCallButton.js.map +1 -1
  6. package/dist/src/components/CallControls/ScreenShareButton.js +6 -46
  7. package/dist/src/components/CallControls/ScreenShareButton.js.map +1 -1
  8. package/dist/src/components/CallControls/ToggleAudioButton.js +2 -2
  9. package/dist/src/components/CallControls/ToggleAudioButton.js.map +1 -1
  10. package/dist/src/components/CallControls/ToggleVideoButton.js +2 -2
  11. package/dist/src/components/CallControls/ToggleVideoButton.js.map +1 -1
  12. package/dist/src/components/Permissions/PermissionRequests.d.ts +3 -1
  13. package/dist/src/components/Permissions/PermissionRequests.js +25 -13
  14. package/dist/src/components/Permissions/PermissionRequests.js.map +1 -1
  15. package/dist/src/hooks/index.d.ts +3 -0
  16. package/dist/src/hooks/index.js +3 -0
  17. package/dist/src/hooks/index.js.map +1 -1
  18. package/dist/src/hooks/useRequestPermission.d.ts +7 -0
  19. package/dist/src/hooks/useRequestPermission.js +52 -0
  20. package/dist/src/hooks/useRequestPermission.js.map +1 -0
  21. package/dist/src/hooks/useToggleAudioMuteState.d.ts +1 -1
  22. package/dist/src/hooks/useToggleAudioMuteState.js +14 -41
  23. package/dist/src/hooks/useToggleAudioMuteState.js.map +1 -1
  24. package/dist/src/hooks/useToggleCallRecording.d.ts +5 -0
  25. package/dist/src/hooks/useToggleCallRecording.js +43 -0
  26. package/dist/src/hooks/useToggleCallRecording.js.map +1 -0
  27. package/dist/src/hooks/useToggleScreenShare.d.ts +5 -0
  28. package/dist/src/hooks/useToggleScreenShare.js +37 -0
  29. package/dist/src/hooks/useToggleScreenShare.js.map +1 -0
  30. package/dist/src/hooks/useToggleVideoMuteState.d.ts +1 -1
  31. package/dist/src/hooks/useToggleVideoMuteState.js +14 -41
  32. package/dist/src/hooks/useToggleVideoMuteState.js.map +1 -1
  33. package/dist/version.d.ts +1 -1
  34. package/dist/version.js +1 -1
  35. package/dist/version.js.map +1 -1
  36. package/package.json +3 -3
  37. package/src/components/CallControls/ReactionsButton.tsx +5 -3
  38. package/src/components/CallControls/RecordCallButton.tsx +9 -33
  39. package/src/components/CallControls/ScreenShareButton.tsx +13 -51
  40. package/src/components/CallControls/ToggleAudioButton.tsx +5 -5
  41. package/src/components/CallControls/ToggleVideoButton.tsx +7 -5
  42. package/src/components/Permissions/PermissionRequests.tsx +49 -22
  43. package/src/hooks/index.ts +3 -0
  44. package/src/hooks/useRequestPermission.ts +48 -0
  45. package/src/hooks/useToggleAudioMuteState.ts +19 -48
  46. package/src/hooks/useToggleCallRecording.ts +39 -0
  47. package/src/hooks/useToggleScreenShare.ts +42 -0
  48. package/src/hooks/useToggleVideoMuteState.ts +19 -48
@@ -0,0 +1,48 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+ import { OwnCapability } from '@stream-io/video-client';
3
+ import { useCall, useHasPermissions } from '@stream-io/video-react-bindings';
4
+
5
+ export const useRequestPermission = (permission: OwnCapability) => {
6
+ const call = useCall();
7
+ const hasPermission = useHasPermissions(permission);
8
+ const canRequestPermission =
9
+ !!call?.permissionsContext.canRequest(permission);
10
+ const [isAwaitingPermission, setIsAwaitingPermission] = useState(false); // TODO: load with possibly pending state
11
+
12
+ useEffect(() => {
13
+ const reset = () => setIsAwaitingPermission(false);
14
+
15
+ if (hasPermission) reset();
16
+ }, [hasPermission]);
17
+
18
+ const requestPermission = useCallback(async () => {
19
+ if (isAwaitingPermission || !canRequestPermission) return false;
20
+ if (hasPermission) return true;
21
+
22
+ setIsAwaitingPermission(true);
23
+
24
+ try {
25
+ await call?.requestPermissions({
26
+ permissions: [permission],
27
+ });
28
+ } catch (error) {
29
+ setIsAwaitingPermission(false);
30
+ throw new Error(`requestPermission failed: ${error}`);
31
+ }
32
+
33
+ return false;
34
+ }, [
35
+ call,
36
+ canRequestPermission,
37
+ hasPermission,
38
+ isAwaitingPermission,
39
+ permission,
40
+ ]);
41
+
42
+ return {
43
+ requestPermission,
44
+ hasPermission,
45
+ canRequestPermission,
46
+ isAwaitingPermission,
47
+ };
48
+ };
@@ -1,62 +1,33 @@
1
- import { useCallback, useEffect, useState } from 'react';
2
- import {
3
- useCall,
4
- useHasPermissions,
5
- useLocalParticipant,
6
- } from '@stream-io/video-react-bindings';
1
+ import { useCallback, useRef } from 'react';
2
+ import { useLocalParticipant } from '@stream-io/video-react-bindings';
7
3
  import { OwnCapability, SfuModels } from '@stream-io/video-client';
8
4
 
9
5
  import { useMediaDevices } from '../core';
6
+ import { useRequestPermission } from './useRequestPermission';
10
7
 
11
8
  export const useToggleAudioMuteState = () => {
12
9
  const { publishAudioStream, stopPublishingAudio } = useMediaDevices();
13
10
  const localParticipant = useLocalParticipant();
14
- const call = useCall();
15
- const hasPermission = useHasPermissions(OwnCapability.SEND_AUDIO);
16
- const [isAwaitingApproval, setIsAwaitingApproval] = useState(false);
17
11
 
18
- const isAudioMute = !localParticipant?.publishedTracks.includes(
19
- SfuModels.TrackType.AUDIO,
12
+ const { isAwaitingPermission, requestPermission } = useRequestPermission(
13
+ OwnCapability.SEND_AUDIO,
20
14
  );
21
15
 
22
- useEffect(() => {
23
- if (hasPermission) {
24
- setIsAwaitingApproval(false);
25
- }
26
- }, [hasPermission]);
16
+ // to keep the toggle function as stable as possible
17
+ const isAudioMutedReference = useRef(false);
18
+
19
+ isAudioMutedReference.current = !localParticipant?.publishedTracks.includes(
20
+ SfuModels.TrackType.AUDIO,
21
+ );
27
22
 
28
23
  const toggleAudioMuteState = useCallback(async () => {
29
- if (
30
- !hasPermission &&
31
- call &&
32
- call.permissionsContext.canRequest(OwnCapability.SEND_AUDIO)
33
- ) {
34
- setIsAwaitingApproval(true);
35
- await call
36
- .requestPermissions({
37
- permissions: [OwnCapability.SEND_AUDIO],
38
- })
39
- .catch((reason) => {
40
- console.log('RequestPermissions failed', reason);
41
- });
42
- return;
43
- }
44
- if (isAudioMute) {
45
- if (hasPermission) {
46
- await publishAudioStream();
47
- } else {
48
- console.log('Cannot publish audio stream. Insufficient permissions.');
49
- }
50
- } else {
51
- stopPublishingAudio();
24
+ if (isAudioMutedReference.current) {
25
+ const canPublish = await requestPermission();
26
+ if (canPublish) return publishAudioStream();
52
27
  }
53
- }, [
54
- call,
55
- hasPermission,
56
- isAudioMute,
57
- publishAudioStream,
58
- stopPublishingAudio,
59
- ]);
60
-
61
- return { toggleAudioMuteState, isAwaitingApproval };
28
+
29
+ if (!isAudioMutedReference.current) stopPublishingAudio();
30
+ }, [publishAudioStream, requestPermission, stopPublishingAudio]);
31
+
32
+ return { toggleAudioMuteState, isAwaitingPermission };
62
33
  };
@@ -0,0 +1,39 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+
3
+ import {
4
+ useCall,
5
+ useIsCallRecordingInProgress,
6
+ } from '@stream-io/video-react-bindings';
7
+
8
+ export const useToggleCallRecording = () => {
9
+ const call = useCall();
10
+ const isCallRecordingInProgress = useIsCallRecordingInProgress();
11
+ const [isAwaitingResponse, setIsAwaitingResponse] = useState(false);
12
+
13
+ // TODO: add permissions
14
+
15
+ useEffect(() => {
16
+ // we wait until call.recording_started/stopped event to flips the
17
+ // `isCallRecordingInProgress` state variable.
18
+ // Once the flip happens, we remove the loading indicator
19
+ setIsAwaitingResponse((isAwaiting) => {
20
+ if (isAwaiting) return false;
21
+ return isAwaiting;
22
+ });
23
+ }, [isCallRecordingInProgress]);
24
+
25
+ const toggleCallRecording = useCallback(async () => {
26
+ try {
27
+ setIsAwaitingResponse(true);
28
+ if (isCallRecordingInProgress) {
29
+ await call?.stopRecording();
30
+ } else {
31
+ await call?.startRecording();
32
+ }
33
+ } catch (e) {
34
+ console.error(`Failed start recording`, e);
35
+ }
36
+ }, [call, isCallRecordingInProgress]);
37
+
38
+ return { toggleCallRecording, isAwaitingResponse, isCallRecordingInProgress };
39
+ };
@@ -0,0 +1,42 @@
1
+ import { useCallback, useRef } from 'react';
2
+ import { useCall, useLocalParticipant } from '@stream-io/video-react-bindings';
3
+ import {
4
+ OwnCapability,
5
+ SfuModels,
6
+ getScreenShareStream,
7
+ } from '@stream-io/video-client';
8
+ import { useRequestPermission } from './useRequestPermission';
9
+
10
+ export const useToggleScreenShare = () => {
11
+ const localParticipant = useLocalParticipant();
12
+ const call = useCall();
13
+ const isScreenSharingReference = useRef(false);
14
+ const { isAwaitingPermission, requestPermission } = useRequestPermission(
15
+ OwnCapability.SCREENSHARE,
16
+ );
17
+
18
+ const isScreenSharing = !!localParticipant?.publishedTracks.includes(
19
+ SfuModels.TrackType.SCREEN_SHARE,
20
+ );
21
+
22
+ isScreenSharingReference.current = isScreenSharing;
23
+
24
+ const toggleScreenShare = useCallback(async () => {
25
+ if (!isScreenSharingReference.current) {
26
+ const canPublish = await requestPermission();
27
+ if (!canPublish) return;
28
+
29
+ const stream = await getScreenShareStream().catch((e) => {
30
+ console.log(`Can't share screen: ${e}`);
31
+ });
32
+
33
+ if (stream) {
34
+ return call?.publishScreenShareStream(stream);
35
+ }
36
+ }
37
+
38
+ call?.stopPublish(SfuModels.TrackType.SCREEN_SHARE);
39
+ }, [call, requestPermission]);
40
+
41
+ return { toggleScreenShare, isAwaitingPermission, isScreenSharing };
42
+ };
@@ -1,62 +1,33 @@
1
- import { useCallback, useEffect, useState } from 'react';
2
- import {
3
- useCall,
4
- useHasPermissions,
5
- useLocalParticipant,
6
- } from '@stream-io/video-react-bindings';
1
+ import { useCallback, useRef } from 'react';
2
+ import { useLocalParticipant } from '@stream-io/video-react-bindings';
7
3
  import { OwnCapability, SfuModels } from '@stream-io/video-client';
8
4
 
9
5
  import { useMediaDevices } from '../core';
6
+ import { useRequestPermission } from './useRequestPermission';
10
7
 
11
8
  export const useToggleVideoMuteState = () => {
12
9
  const { publishVideoStream, stopPublishingVideo } = useMediaDevices();
13
10
  const localParticipant = useLocalParticipant();
14
- const call = useCall();
15
- const hasPermission = useHasPermissions(OwnCapability.SEND_VIDEO);
16
- const [isAwaitingApproval, setIsAwaitingApproval] = useState(false);
17
11
 
18
- const isVideoMute = !localParticipant?.publishedTracks.includes(
19
- SfuModels.TrackType.VIDEO,
12
+ const { isAwaitingPermission, requestPermission } = useRequestPermission(
13
+ OwnCapability.SEND_VIDEO,
20
14
  );
21
15
 
22
- useEffect(() => {
23
- if (hasPermission) {
24
- setIsAwaitingApproval(false);
25
- }
26
- }, [hasPermission]);
16
+ // to keep the toggle function as stable as possible
17
+ const isVideoMutedReference = useRef(false);
18
+
19
+ isVideoMutedReference.current = !localParticipant?.publishedTracks.includes(
20
+ SfuModels.TrackType.VIDEO,
21
+ );
27
22
 
28
23
  const toggleVideoMuteState = useCallback(async () => {
29
- if (
30
- !hasPermission &&
31
- call &&
32
- call.permissionsContext.canRequest(OwnCapability.SEND_VIDEO)
33
- ) {
34
- setIsAwaitingApproval(true);
35
- await call
36
- .requestPermissions({
37
- permissions: [OwnCapability.SEND_VIDEO],
38
- })
39
- .catch((reason) => {
40
- console.log('RequestPermissions failed', reason);
41
- });
42
- return;
43
- }
44
- if (isVideoMute) {
45
- if (hasPermission) {
46
- await publishVideoStream();
47
- } else {
48
- console.log('Cannot publish video. Insufficient permissions.');
49
- }
50
- } else {
51
- stopPublishingVideo();
24
+ if (isVideoMutedReference.current) {
25
+ const canPublish = await requestPermission();
26
+ if (canPublish) return publishVideoStream();
52
27
  }
53
- }, [
54
- call,
55
- hasPermission,
56
- isVideoMute,
57
- publishVideoStream,
58
- stopPublishingVideo,
59
- ]);
60
-
61
- return { toggleVideoMuteState, isAwaitingApproval };
28
+
29
+ if (!isVideoMutedReference.current) stopPublishingVideo();
30
+ }, [publishVideoStream, requestPermission, stopPublishingVideo]);
31
+
32
+ return { toggleVideoMuteState, isAwaitingPermission };
62
33
  };