@stream-io/video-react-sdk 0.3.47 → 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 (63) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +1 -1
  3. package/dist/index.cjs.js +324 -882
  4. package/dist/index.cjs.js.map +1 -1
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.es.js +325 -867
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/src/components/Notification/SpeakingWhileMutedNotification.d.ts +3 -0
  9. package/dist/src/components/{Video → VideoPreview}/VideoPreview.d.ts +1 -9
  10. package/dist/src/components/index.d.ts +1 -1
  11. package/dist/src/core/components/ParticipantView/ParticipantView.d.ts +3 -9
  12. package/dist/src/core/components/ParticipantView/ParticipantViewContext.d.ts +9 -0
  13. package/dist/src/core/components/ParticipantView/index.d.ts +1 -0
  14. package/dist/src/core/components/StreamCall/StreamCall.d.ts +2 -11
  15. package/dist/src/core/hooks/index.d.ts +0 -2
  16. package/dist/src/core/hooks/useDevices.d.ts +0 -99
  17. package/dist/src/core/index.d.ts +0 -1
  18. package/dist/src/hooks/index.d.ts +1 -3
  19. package/dist/src/hooks/usePersistedDevicePreferences.d.ts +13 -0
  20. package/dist/src/translations/index.d.ts +2 -0
  21. package/index.ts +2 -2
  22. package/package.json +3 -3
  23. package/src/components/CallControls/CallControls.tsx +6 -8
  24. package/src/components/CallControls/ScreenShareButton.tsx +14 -10
  25. package/src/components/CallControls/ToggleAudioButton.tsx +21 -24
  26. package/src/components/CallControls/ToggleAudioOutputButton.tsx +1 -1
  27. package/src/components/CallControls/ToggleVideoButton.tsx +21 -22
  28. package/src/components/CallParticipantsList/CallParticipantsList.tsx +1 -1
  29. package/src/components/DeviceSettings/DeviceSelectorAudio.tsx +20 -26
  30. package/src/components/DeviceSettings/DeviceSelectorVideo.tsx +9 -8
  31. package/src/components/Icon/Icon.tsx +1 -1
  32. package/src/components/Notification/SpeakingWhileMutedNotification.tsx +5 -49
  33. package/src/components/VideoPreview/VideoPreview.tsx +67 -0
  34. package/src/components/index.ts +1 -1
  35. package/src/core/components/CallLayout/PaginatedGridLayout.tsx +2 -5
  36. package/src/core/components/ParticipantView/DefaultParticipantViewUI.tsx +7 -6
  37. package/src/core/components/ParticipantView/ParticipantView.tsx +2 -19
  38. package/src/core/components/ParticipantView/ParticipantViewContext.tsx +17 -0
  39. package/src/core/components/ParticipantView/index.ts +1 -0
  40. package/src/core/components/StreamCall/StreamCall.tsx +2 -28
  41. package/src/core/hooks/index.ts +0 -2
  42. package/src/core/hooks/useDevices.ts +0 -195
  43. package/src/core/index.ts +0 -1
  44. package/src/hooks/index.ts +1 -3
  45. package/src/hooks/usePersistedDevicePreferences.ts +118 -0
  46. package/src/translations/en.json +3 -0
  47. package/dist/src/core/contexts/MediaDevicesContext.d.ts +0 -180
  48. package/dist/src/core/contexts/index.d.ts +0 -1
  49. package/dist/src/core/hooks/useAudioPublisher.d.ts +0 -12
  50. package/dist/src/core/hooks/useVideoPublisher.d.ts +0 -12
  51. package/dist/src/hooks/useToggleAudioMuteState.d.ts +0 -4
  52. package/dist/src/hooks/useToggleScreenShare.d.ts +0 -5
  53. package/dist/src/hooks/useToggleVideoMuteState.d.ts +0 -4
  54. package/src/components/Video/VideoPreview.tsx +0 -152
  55. package/src/core/contexts/MediaDevicesContext.tsx +0 -416
  56. package/src/core/contexts/index.ts +0 -1
  57. package/src/core/hooks/useAudioPublisher.ts +0 -146
  58. package/src/core/hooks/useVideoPublisher.ts +0 -177
  59. package/src/hooks/useToggleAudioMuteState.ts +0 -34
  60. package/src/hooks/useToggleScreenShare.ts +0 -43
  61. package/src/hooks/useToggleVideoMuteState.ts +0 -34
  62. /package/dist/src/components/{Video → VideoPreview}/index.d.ts +0 -0
  63. /package/src/components/{Video → VideoPreview}/index.ts +0 -0
@@ -1,5 +1,8 @@
1
1
  import { PropsWithChildren } from 'react';
2
2
  export type SpeakingWhileMutedNotificationProps = {
3
+ /**
4
+ * Text message displayed by the notification.
5
+ */
3
6
  text?: string;
4
7
  };
5
8
  export declare const SpeakingWhileMutedNotification: ({ children, text, }: PropsWithChildren<SpeakingWhileMutedNotificationProps>) => import("react/jsx-runtime").JSX.Element;
@@ -1,7 +1,4 @@
1
1
  import { ComponentType } from 'react';
2
- type VideoErrorPreviewProps = {
3
- message?: string;
4
- };
5
2
  export type VideoPreviewProps = {
6
3
  /**
7
4
  * Component rendered when user turns off the video.
@@ -19,10 +16,5 @@ export type VideoPreviewProps = {
19
16
  * Component rendered above the BaseVideo until the video is ready (meaning until the play event is emitted).
20
17
  */
21
18
  StartingCameraPreview?: ComponentType;
22
- /**
23
- * Component rendered when the video stream could not be retrieved.
24
- */
25
- VideoErrorPreview?: ComponentType<VideoErrorPreviewProps>;
26
19
  };
27
- export declare const VideoPreview: ({ mirror, DisabledVideoPreview, NoCameraPreview, StartingCameraPreview, VideoErrorPreview, }: VideoPreviewProps) => import("react/jsx-runtime").JSX.Element;
28
- export {};
20
+ export declare const VideoPreview: ({ mirror, DisabledVideoPreview, NoCameraPreview, StartingCameraPreview, }: VideoPreviewProps) => import("react/jsx-runtime").JSX.Element;
@@ -14,4 +14,4 @@ export * from './Permissions';
14
14
  export * from './StreamTheme';
15
15
  export * from './Search';
16
16
  export * from './Tooltip';
17
- export * from './Video';
17
+ export * from './VideoPreview';
@@ -1,17 +1,11 @@
1
1
  import { ComponentType, ReactElement } from 'react';
2
- import { StreamVideoLocalParticipant, StreamVideoParticipant, VideoTrackType } from '@stream-io/video-client';
2
+ import { StreamVideoParticipant, VideoTrackType } from '@stream-io/video-client';
3
3
  import { VideoProps } from '../Video';
4
- export type ParticipantViewContextValue = Required<Pick<ParticipantViewProps, 'participant' | 'trackType'>> & {
5
- participantViewElement: HTMLDivElement | null;
6
- videoElement: HTMLVideoElement | null;
7
- videoPlaceholderElement: HTMLDivElement | null;
8
- };
9
- export declare const useParticipantViewContext: () => ParticipantViewContextValue;
10
4
  export type ParticipantViewProps = {
11
5
  /**
12
6
  * The participant whose video/audio stream we want to play.
13
7
  */
14
- participant: StreamVideoParticipant | StreamVideoLocalParticipant;
8
+ participant: StreamVideoParticipant;
15
9
  /**
16
10
  * Override the default UI for rendering participant information/actions.
17
11
  * pass `null` if you wish to not render anything
@@ -42,7 +36,7 @@ export declare const ParticipantView: import("react").ForwardRefExoticComponent<
42
36
  /**
43
37
  * The participant whose video/audio stream we want to play.
44
38
  */
45
- participant: StreamVideoParticipant | StreamVideoLocalParticipant;
39
+ participant: StreamVideoParticipant;
46
40
  /**
47
41
  * Override the default UI for rendering participant information/actions.
48
42
  * pass `null` if you wish to not render anything
@@ -0,0 +1,9 @@
1
+ /// <reference types="react" />
2
+ import { ParticipantViewProps } from './ParticipantView';
3
+ export type ParticipantViewContextValue = Required<Pick<ParticipantViewProps, 'participant' | 'trackType'>> & {
4
+ participantViewElement: HTMLDivElement | null;
5
+ videoElement: HTMLVideoElement | null;
6
+ videoPlaceholderElement: HTMLDivElement | null;
7
+ };
8
+ export declare const ParticipantViewContext: import("react").Context<ParticipantViewContextValue | undefined>;
9
+ export declare const useParticipantViewContext: () => ParticipantViewContextValue;
@@ -1,2 +1,3 @@
1
1
  export * from './ParticipantView';
2
+ export * from './ParticipantViewContext';
2
3
  export * from './DefaultParticipantViewUI';
@@ -1,11 +1,2 @@
1
- import { PropsWithChildren } from 'react';
2
- import { Call } from '@stream-io/video-client';
3
- import { MediaDevicesProviderProps } from '../../contexts';
4
- export type StreamCallProps = {
5
- call: Call;
6
- /**
7
- * An optional props to pass to the `MediaDevicesProvider`.
8
- */
9
- mediaDevicesProviderProps?: MediaDevicesProviderProps;
10
- };
11
- export declare const StreamCall: ({ children, call, mediaDevicesProviderProps, }: PropsWithChildren<StreamCallProps>) => import("react/jsx-runtime").JSX.Element;
1
+ /// <reference types="react" />
2
+ export declare const StreamCall: (props: import("react").PropsWithChildren<import("@stream-io/video-react-bindings").StreamCallProviderProps>) => import("react/jsx-runtime").JSX.Element;
@@ -1,4 +1,2 @@
1
- export * from './useAudioPublisher';
2
1
  export * from './useDevices';
3
- export * from './useVideoPublisher';
4
2
  export * from './useTrackElementVisibility';
@@ -1,100 +1 @@
1
- import { Observable } from 'rxjs';
2
1
  export declare const useHasBrowserPermissions: (permissionName: PermissionName) => boolean;
3
- /**
4
- * Observes changes in connected devices and maintains an up-to-date array of connected MediaDeviceInfo objects.
5
- * @param observeDevices
6
- * @category Device Management
7
- */
8
- export declare const useDevices: (observeDevices: () => Observable<MediaDeviceInfo[]>) => MediaDeviceInfo[];
9
- /**
10
- * Observes changes and maintains an array of connected video input devices
11
- * @category Device Management
12
- */
13
- export declare const useVideoDevices: () => MediaDeviceInfo[];
14
- /**
15
- * Observes changes and maintains an array of connected audio input devices
16
- * @category Device Management
17
- */
18
- export declare const useAudioInputDevices: () => MediaDeviceInfo[];
19
- /**
20
- * Observes changes and maintains an array of connected audio output devices
21
- * @category Device Management
22
- */
23
- export declare const useAudioOutputDevices: () => MediaDeviceInfo[];
24
- /**
25
- * Verifies that newly selected device id exists among the registered devices.
26
- * If the selected device id is not found among existing devices, switches to the default device.
27
- * The media devices are observed only if a given permission ('camera' resp. 'microphone') is granted in browser.
28
- * Regardless of current permissions settings, an intent to observe devices will take place in Firefox.
29
- * This is due to the fact that Firefox does not allow to query for 'camera' and 'microphone' permissions.
30
- * @param canObserve
31
- * @param devices$
32
- * @param switchToDefaultDevice
33
- * @param selectedDeviceId
34
- * @category Device Management
35
- */
36
- export declare const useDeviceFallback: (canObserve: boolean, devices$: Observable<MediaDeviceInfo[]>, switchToDefaultDevice: () => void, selectedDeviceId?: string) => void;
37
- /**
38
- * Verifies that newly selected video device id exists among the registered devices.
39
- * If the selected device id is not found among existing devices, switches to the default video device.
40
- * The media devices are observed only if 'camera' permission is granted in browser.
41
- * It is integrators responsibility to instruct users how to enable required permissions.
42
- * Regardless of current permissions settings, an intent to observe devices will take place in Firefox.
43
- * This is due to the fact that Firefox does not allow to query for 'camera' and 'microphone' permissions.
44
- * @param switchToDefaultDevice
45
- * @param canObserve
46
- * @param selectedDeviceId
47
- * @category Device Management
48
- */
49
- export declare const useVideoDeviceFallback: (switchToDefaultDevice: () => void, canObserve: boolean, selectedDeviceId?: string) => void;
50
- /**
51
- * Verifies that newly selected audio input device id exists among the registered devices.
52
- * If the selected device id is not found among existing devices, switches to the default audio input device.
53
- * The media devices are observed only if 'microphone' permission is granted in browser.
54
- * It is integrators responsibility to instruct users how to enable required permissions.
55
- * Regardless of current permissions settings, an intent to observe devices will take place in Firefox.
56
- * This is due to the fact that Firefox does not allow to query for 'camera' and 'microphone' permissions.
57
- * @param switchToDefaultDevice
58
- * @param canObserve
59
- * @param selectedDeviceId
60
- * @category Device Management
61
- */
62
- export declare const useAudioInputDeviceFallback: (switchToDefaultDevice: () => void, canObserve: boolean, selectedDeviceId?: string) => void;
63
- /**
64
- * Verifies that newly selected audio output device id exists among the registered devices.
65
- * If the selected device id is not found among existing devices, switches to the default audio output device.
66
- * The media devices are observed only if 'microphone' permission is granted in browser.
67
- * It is integrators responsibility to instruct users how to enable required permissions.
68
- * Regardless of current permissions settings, an intent to observe devices will take place in Firefox.
69
- * This is due to the fact that Firefox does not allow to query for 'camera' and 'microphone' permissions.
70
- * @param switchToDefaultDevice
71
- * @param canObserve
72
- * @param selectedDeviceId
73
- * @category Device Management
74
- */
75
- export declare const useAudioOutputDeviceFallback: (switchToDefaultDevice: () => void, canObserve: boolean, selectedDeviceId?: string) => void;
76
- /**
77
- * Observes devices of certain kind are made unavailable and executes onDisconnect callback.
78
- * @param observeDevices
79
- * @param onDisconnect
80
- * @category Device Management
81
- */
82
- export declare const useOnUnavailableDevices: (observeDevices: Observable<MediaDeviceInfo[]>, onDisconnect: () => void) => void;
83
- /**
84
- * Observes disconnect of all video devices and executes onDisconnect callback.
85
- * @param onDisconnect
86
- * @category Device Management
87
- */
88
- export declare const useOnUnavailableVideoDevices: (onDisconnect: () => void) => void;
89
- /**
90
- * Observes disconnect of all audio input devices and executes onDisconnect callback.
91
- * @param onDisconnect
92
- * @category Device Management
93
- */
94
- export declare const useOnUnavailableAudioInputDevices: (onDisconnect: () => void) => void;
95
- /**
96
- * Observes disconnect of all audio output devices and executes onDisconnect callback.
97
- * @param onDisconnect
98
- * @category Device Management
99
- */
100
- export declare const useOnUnavailableAudioOutputDevices: (onDisconnect: () => void) => void;
@@ -1,3 +1,2 @@
1
1
  export * from './components';
2
- export * from './contexts';
3
2
  export * from './hooks';
@@ -1,7 +1,5 @@
1
1
  export * from './useFloatingUIPreset';
2
+ export * from './usePersistedDevicePreferences';
2
3
  export * from './useScrollPosition';
3
- export * from './useToggleAudioMuteState';
4
- export * from './useToggleVideoMuteState';
5
- export * from './useToggleScreenShare';
6
4
  export * from './useToggleCallRecording';
7
5
  export * from './useRequestPermission';
@@ -0,0 +1,13 @@
1
+ export type LocalDevicePreference = {
2
+ selectedDeviceId: string;
3
+ muted: boolean;
4
+ };
5
+ export type LocalDevicePreferences = {
6
+ [type in 'mic' | 'camera' | 'speaker']: LocalDevicePreference;
7
+ };
8
+ /**
9
+ * This hook will apply and persist the device preferences from local storage.
10
+ *
11
+ * @param key the key to use for local storage.
12
+ */
13
+ export declare const usePersistedDevicePreferences: (key?: string) => void;
@@ -36,6 +36,8 @@ export declare const translations: {
36
36
  Me: string;
37
37
  Unknown: string;
38
38
  "Toggle device menu": string;
39
+ "You are presenting your screen": string;
40
+ "Stop Screen Sharing": string;
39
41
  Allow: string;
40
42
  Revoke: string;
41
43
  Dismiss: string;
package/index.ts CHANGED
@@ -11,8 +11,8 @@ export * from './src/translations';
11
11
  export {
12
12
  useHorizontalScrollPosition,
13
13
  useVerticalScrollPosition,
14
- useToggleAudioMuteState,
15
- useToggleVideoMuteState,
14
+ useRequestPermission,
15
+ usePersistedDevicePreferences,
16
16
  } from './src/hooks';
17
17
 
18
18
  const [major, minor, patch] = (
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-io/video-react-sdk",
3
- "version": "0.3.47",
3
+ "version": "0.4.0",
4
4
  "packageManager": "yarn@3.2.4",
5
5
  "main": "./dist/index.cjs.js",
6
6
  "module": "./dist/index.es.js",
@@ -31,8 +31,8 @@
31
31
  "@floating-ui/react": "^0.22.0",
32
32
  "@nivo/core": "^0.80.0",
33
33
  "@nivo/line": "^0.80.0",
34
- "@stream-io/video-client": "^0.3.36",
35
- "@stream-io/video-react-bindings": "^0.2.37",
34
+ "@stream-io/video-client": "^0.4.0",
35
+ "@stream-io/video-react-bindings": "^0.3.0",
36
36
  "clsx": "^2.0.0",
37
37
  "prop-types": "^15.8.1",
38
38
  "rxjs": "~7.8.1"
@@ -1,12 +1,10 @@
1
- import {
2
- CallStatsButton,
3
- CancelCallButton,
4
- RecordCallButton,
5
- ScreenShareButton,
6
- ToggleAudioPublishingButton,
7
- ToggleVideoPublishingButton,
8
- } from './index';
9
1
  import { SpeakingWhileMutedNotification } from '../Notification';
2
+ import { RecordCallButton } from './RecordCallButton';
3
+ import { CallStatsButton } from './CallStatsButton';
4
+ import { ScreenShareButton } from './ScreenShareButton';
5
+ import { ToggleAudioPublishingButton } from './ToggleAudioButton';
6
+ import { ToggleVideoPublishingButton } from './ToggleVideoButton';
7
+ import { CancelCallButton } from './CancelCallButton';
10
8
 
11
9
  export type CallControlsProps = {
12
10
  onLeave?: () => void;
@@ -1,29 +1,27 @@
1
1
  import { OwnCapability } from '@stream-io/video-client';
2
2
  import {
3
3
  Restricted,
4
- useCall,
5
4
  useCallStateHooks,
6
5
  useI18n,
7
6
  } from '@stream-io/video-react-bindings';
8
7
  import { CompositeButton, IconButton } from '../Button/';
9
8
  import { PermissionNotification } from '../Notification';
10
- import { useToggleScreenShare } from '../../hooks';
9
+ import { useRequestPermission } from '../../hooks';
11
10
 
12
11
  export type ScreenShareButtonProps = {
13
12
  caption?: string;
14
13
  };
15
14
 
16
15
  export const ScreenShareButton = (props: ScreenShareButtonProps) => {
17
- const call = useCall();
18
- const { useHasOngoingScreenShare } = useCallStateHooks();
19
- const isSomeoneScreenSharing = useHasOngoingScreenShare();
20
-
21
16
  const { t } = useI18n();
22
17
  const { caption = t('Screen Share') } = props;
23
18
 
24
- const { toggleScreenShare, isAwaitingPermission, isScreenSharing } =
25
- useToggleScreenShare();
19
+ const { useHasOngoingScreenShare, useScreenShareState } = useCallStateHooks();
20
+ const isSomeoneScreenSharing = useHasOngoingScreenShare();
21
+ const { hasPermission, requestPermission, isAwaitingPermission } =
22
+ useRequestPermission(OwnCapability.SCREENSHARE);
26
23
 
24
+ const { screenShare, isMute: isScreenSharing } = useScreenShareState();
27
25
  return (
28
26
  <Restricted requiredGrants={[OwnCapability.SCREENSHARE]}>
29
27
  <PermissionNotification
@@ -37,8 +35,14 @@ export const ScreenShareButton = (props: ScreenShareButtonProps) => {
37
35
  <IconButton
38
36
  icon={isScreenSharing ? 'screen-share-on' : 'screen-share-off'}
39
37
  title={t('Share screen')}
40
- disabled={(!isScreenSharing && isSomeoneScreenSharing) || !call}
41
- onClick={toggleScreenShare}
38
+ disabled={!isScreenSharing && isSomeoneScreenSharing}
39
+ onClick={async () => {
40
+ if (!hasPermission) {
41
+ await requestPermission();
42
+ } else {
43
+ await screenShare.toggle();
44
+ }
45
+ }}
42
46
  />
43
47
  </CompositeButton>
44
48
  </PermissionNotification>
@@ -1,16 +1,14 @@
1
1
  import { ComponentType } from 'react';
2
- import { OwnCapability, SfuModels } from '@stream-io/video-client';
2
+ import { OwnCapability } from '@stream-io/video-client';
3
3
  import {
4
4
  Restricted,
5
5
  useCallStateHooks,
6
6
  useI18n,
7
7
  } from '@stream-io/video-react-bindings';
8
-
9
- import { useMediaDevices } from '../../core';
10
8
  import { DeviceSelectorAudioInput } from '../DeviceSettings';
11
9
  import { CompositeButton, IconButton } from '../Button';
12
10
  import { PermissionNotification } from '../Notification';
13
- import { useToggleAudioMuteState } from '../../hooks';
11
+ import { useRequestPermission } from '../../hooks';
14
12
 
15
13
  export type ToggleAudioPreviewButtonProps = {
16
14
  caption?: string;
@@ -20,20 +18,17 @@ export type ToggleAudioPreviewButtonProps = {
20
18
  export const ToggleAudioPreviewButton = (
21
19
  props: ToggleAudioPreviewButtonProps,
22
20
  ) => {
23
- const { initialAudioEnabled, toggleInitialAudioMuteState } =
24
- useMediaDevices();
25
21
  const { t } = useI18n();
26
22
  const { caption = t('Mic'), Menu = DeviceSelectorAudioInput } = props;
27
23
 
24
+ const { useMicrophoneState } = useCallStateHooks();
25
+ const { microphone, isMute } = useMicrophoneState();
26
+
28
27
  return (
29
- <CompositeButton
30
- Menu={Menu}
31
- active={!initialAudioEnabled}
32
- caption={caption || t('Mic')}
33
- >
28
+ <CompositeButton Menu={Menu} active={isMute} caption={caption || t('Mic')}>
34
29
  <IconButton
35
- icon={initialAudioEnabled ? 'mic' : 'mic-off'}
36
- onClick={toggleInitialAudioMuteState}
30
+ icon={!isMute ? 'mic' : 'mic-off'}
31
+ onClick={() => microphone.toggle()}
37
32
  />
38
33
  </CompositeButton>
39
34
  );
@@ -47,18 +42,14 @@ export type ToggleAudioPublishingButtonProps = {
47
42
  export const ToggleAudioPublishingButton = (
48
43
  props: ToggleAudioPublishingButtonProps,
49
44
  ) => {
50
- const { useLocalParticipant } = useCallStateHooks();
51
- const localParticipant = useLocalParticipant();
52
45
  const { t } = useI18n();
53
-
54
46
  const { caption = t('Mic'), Menu = DeviceSelectorAudioInput } = props;
55
47
 
56
- const isAudioMute = !localParticipant?.publishedTracks.includes(
57
- SfuModels.TrackType.AUDIO,
58
- );
48
+ const { hasPermission, requestPermission, isAwaitingPermission } =
49
+ useRequestPermission(OwnCapability.SEND_AUDIO);
59
50
 
60
- const { toggleAudioMuteState: handleClick, isAwaitingPermission } =
61
- useToggleAudioMuteState();
51
+ const { useMicrophoneState } = useCallStateHooks();
52
+ const { microphone, isMute } = useMicrophoneState();
62
53
 
63
54
  return (
64
55
  <Restricted requiredGrants={[OwnCapability.SEND_AUDIO]}>
@@ -69,10 +60,16 @@ export const ToggleAudioPublishingButton = (
69
60
  messageAwaitingApproval={t('Awaiting for an approval to speak.')}
70
61
  messageRevoked={t('You can no longer speak.')}
71
62
  >
72
- <CompositeButton Menu={Menu} active={isAudioMute} caption={caption}>
63
+ <CompositeButton Menu={Menu} active={isMute} caption={caption}>
73
64
  <IconButton
74
- icon={isAudioMute ? 'mic-off' : 'mic'}
75
- onClick={handleClick}
65
+ icon={isMute ? 'mic-off' : 'mic'}
66
+ onClick={async () => {
67
+ if (!hasPermission) {
68
+ await requestPermission();
69
+ } else {
70
+ await microphone.toggle();
71
+ }
72
+ }}
76
73
  />
77
74
  </CompositeButton>
78
75
  </PermissionNotification>
@@ -15,7 +15,7 @@ export const ToggleAudioOutputButton = (
15
15
  const { caption = t('Speakers'), Menu = DeviceSelectorAudioOutput } = props;
16
16
 
17
17
  return (
18
- <CompositeButton Menu={Menu} active caption={caption}>
18
+ <CompositeButton Menu={Menu} caption={caption}>
19
19
  <IconButton icon="speaker" />
20
20
  </CompositeButton>
21
21
  );
@@ -5,12 +5,11 @@ import {
5
5
  useI18n,
6
6
  } from '@stream-io/video-react-bindings';
7
7
 
8
- import { OwnCapability, SfuModels } from '@stream-io/video-client';
8
+ import { OwnCapability } from '@stream-io/video-client';
9
9
  import { CompositeButton, IconButton } from '../Button/';
10
- import { useMediaDevices } from '../../core';
11
10
  import { DeviceSelectorVideo } from '../DeviceSettings';
12
11
  import { PermissionNotification } from '../Notification';
13
- import { useToggleVideoMuteState } from '../../hooks';
12
+ import { useRequestPermission } from '../../hooks';
14
13
 
15
14
  export type ToggleVideoPreviewButtonProps = {
16
15
  caption?: string;
@@ -20,19 +19,17 @@ export type ToggleVideoPreviewButtonProps = {
20
19
  export const ToggleVideoPreviewButton = (
21
20
  props: ToggleVideoPreviewButtonProps,
22
21
  ) => {
23
- const { toggleInitialVideoMuteState, initialVideoState } = useMediaDevices();
24
22
  const { t } = useI18n();
25
23
  const { caption = t('Video'), Menu = DeviceSelectorVideo } = props;
26
24
 
25
+ const { useCameraState } = useCallStateHooks();
26
+ const { camera, isMute } = useCameraState();
27
+
27
28
  return (
28
- <CompositeButton
29
- Menu={Menu}
30
- active={!initialVideoState.enabled}
31
- caption={caption}
32
- >
29
+ <CompositeButton Menu={Menu} active={isMute} caption={caption}>
33
30
  <IconButton
34
- icon={initialVideoState.enabled ? 'camera' : 'camera-off'}
35
- onClick={toggleInitialVideoMuteState}
31
+ icon={!isMute ? 'camera' : 'camera-off'}
32
+ onClick={() => camera.toggle()}
36
33
  />
37
34
  </CompositeButton>
38
35
  );
@@ -46,18 +43,14 @@ type ToggleVideoPublishingButtonProps = {
46
43
  export const ToggleVideoPublishingButton = (
47
44
  props: ToggleVideoPublishingButtonProps,
48
45
  ) => {
49
- const { useLocalParticipant } = useCallStateHooks();
50
- const localParticipant = useLocalParticipant();
51
46
  const { t } = useI18n();
52
-
53
47
  const { caption = t('Video'), Menu = DeviceSelectorVideo } = props;
54
48
 
55
- const isVideoMute = !localParticipant?.publishedTracks.includes(
56
- SfuModels.TrackType.VIDEO,
57
- );
49
+ const { hasPermission, requestPermission, isAwaitingPermission } =
50
+ useRequestPermission(OwnCapability.SEND_VIDEO);
58
51
 
59
- const { toggleVideoMuteState: handleClick, isAwaitingPermission } =
60
- useToggleVideoMuteState();
52
+ const { useCameraState } = useCallStateHooks();
53
+ const { camera, isMute } = useCameraState();
61
54
 
62
55
  return (
63
56
  <Restricted requiredGrants={[OwnCapability.SEND_VIDEO]}>
@@ -70,10 +63,16 @@ export const ToggleVideoPublishingButton = (
70
63
  )}
71
64
  messageRevoked={t('You can no longer share your video.')}
72
65
  >
73
- <CompositeButton Menu={Menu} active={isVideoMute} caption={caption}>
66
+ <CompositeButton Menu={Menu} active={isMute} caption={caption}>
74
67
  <IconButton
75
- icon={isVideoMute ? 'camera-off' : 'camera'}
76
- onClick={handleClick}
68
+ icon={isMute ? 'camera-off' : 'camera'}
69
+ onClick={async () => {
70
+ if (!hasPermission) {
71
+ await requestPermission();
72
+ } else {
73
+ await camera.toggle();
74
+ }
75
+ }}
77
76
  />
78
77
  </CompositeButton>
79
78
  </PermissionNotification>
@@ -17,7 +17,7 @@ import {
17
17
  OwnCapability,
18
18
  StreamVideoParticipant,
19
19
  } from '@stream-io/video-client';
20
- import { clsx } from 'clsx';
20
+ import clsx from 'clsx';
21
21
 
22
22
  import { BlockedUserListing } from './BlockedUserListing';
23
23
  import {
@@ -1,28 +1,25 @@
1
+ import { useCallStateHooks, useI18n } from '@stream-io/video-react-bindings';
1
2
  import { DeviceSelector } from './DeviceSelector';
2
- import {
3
- useMediaDevices,
4
- useAudioInputDevices,
5
- useAudioOutputDevices,
6
- } from '../../core';
7
3
 
8
4
  export type DeviceSelectorAudioInputProps = {
9
5
  title?: string;
10
6
  };
11
7
 
12
8
  export const DeviceSelectorAudioInput = ({
13
- title = 'Select a Mic',
9
+ title,
14
10
  }: DeviceSelectorAudioInputProps) => {
15
- const { selectedAudioInputDeviceId, switchDevice } = useMediaDevices();
16
- const audioInputDevices = useAudioInputDevices();
11
+ const { t } = useI18n();
12
+ const { useMicrophoneState } = useCallStateHooks();
13
+ const { microphone, selectedDevice, devices } = useMicrophoneState();
17
14
 
18
15
  return (
19
16
  <DeviceSelector
20
- devices={audioInputDevices}
21
- selectedDeviceId={selectedAudioInputDeviceId}
22
- onChange={(deviceId) => {
23
- switchDevice('audioinput', deviceId);
17
+ devices={devices || []}
18
+ selectedDeviceId={selectedDevice}
19
+ onChange={async (deviceId) => {
20
+ await microphone.select(deviceId);
24
21
  }}
25
- title={title}
22
+ title={title || t('Select a Mic')}
26
23
  />
27
24
  );
28
25
  };
@@ -32,26 +29,23 @@ export type DeviceSelectorAudioOutputProps = {
32
29
  };
33
30
 
34
31
  export const DeviceSelectorAudioOutput = ({
35
- title = 'Select Speakers',
32
+ title,
36
33
  }: DeviceSelectorAudioOutputProps) => {
37
- const {
38
- isAudioOutputChangeSupported,
39
- selectedAudioOutputDeviceId,
40
- switchDevice,
41
- } = useMediaDevices();
42
-
43
- const audioOutputDevices = useAudioOutputDevices();
34
+ const { t } = useI18n();
35
+ const { useSpeakerState } = useCallStateHooks();
36
+ const { speaker, selectedDevice, devices, isDeviceSelectionSupported } =
37
+ useSpeakerState();
44
38
 
45
- if (!isAudioOutputChangeSupported) return null;
39
+ if (!isDeviceSelectionSupported) return null;
46
40
 
47
41
  return (
48
42
  <DeviceSelector
49
- devices={audioOutputDevices}
50
- selectedDeviceId={selectedAudioOutputDeviceId}
43
+ devices={devices}
44
+ selectedDeviceId={selectedDevice}
51
45
  onChange={(deviceId) => {
52
- switchDevice('audiooutput', deviceId);
46
+ speaker.select(deviceId);
53
47
  }}
54
- title={title}
48
+ title={title || t('Select Speakers')}
55
49
  />
56
50
  );
57
51
  };
@@ -1,22 +1,23 @@
1
1
  import { DeviceSelector } from './DeviceSelector';
2
- import { useMediaDevices, useVideoDevices } from '../../core';
2
+ import { useCallStateHooks, useI18n } from '@stream-io/video-react-bindings';
3
3
 
4
4
  export type DeviceSelectorVideoProps = {
5
5
  title?: string;
6
6
  };
7
7
 
8
8
  export const DeviceSelectorVideo = ({ title }: DeviceSelectorVideoProps) => {
9
- const { selectedVideoDeviceId, switchDevice } = useMediaDevices();
10
- const videoDevices = useVideoDevices();
9
+ const { t } = useI18n();
10
+ const { useCameraState } = useCallStateHooks();
11
+ const { camera, devices, selectedDevice } = useCameraState();
11
12
 
12
13
  return (
13
14
  <DeviceSelector
14
- devices={videoDevices}
15
- selectedDeviceId={selectedVideoDeviceId}
16
- onChange={(deviceId) => {
17
- switchDevice('videoinput', deviceId);
15
+ devices={devices || []}
16
+ selectedDeviceId={selectedDevice}
17
+ onChange={async (deviceId) => {
18
+ await camera.select(deviceId);
18
19
  }}
19
- title={title || 'Select a Camera'}
20
+ title={title || t('Select a Camera')}
20
21
  />
21
22
  );
22
23
  };
@@ -1,4 +1,4 @@
1
- import { clsx } from 'clsx';
1
+ import clsx from 'clsx';
2
2
 
3
3
  export type IconProps = {
4
4
  icon: string;