Skip to content

Commit

Permalink
refactor(react-native): replace useCallControls hook with useMediaStr…
Browse files Browse the repository at this point in the history
…eamManagement (#904)

The goal of the PR is to remove the `useCallControls` hook as
`useMediaStreamManagement` covers most of the operations of it. Also:
- the `toggleAudioMuted` and `toggleVideoMuted` are moved to the hook
`useMediaStreamManagement` for the integrator's simplicity.
- the `isVideoMuted` and `isAudioMuted` is also moved to the same hook
for the integrator's simplicity so that they don't have to take out the
mute status data from `useLocalParticipants` manually(no documentation
for this so it's easy for integrators to get lost).

PS: The relevant docs are also updated.
  • Loading branch information
khushal87 authored Aug 7, 2023
1 parent fd714a9 commit ee61455
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 125 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ description: A guide on how to add/remove or replace call controls
---

import ImageShowcase from '@site/src/components/ImageShowcase';
import CallControlsViewMediaButtonOn
from '../assets/05-ui-cookbook/02-replacing-call-controls/call-controls-button-media-on.png';
import CallControlsViewMediaButtonOff
from '../assets/05-ui-cookbook/02-replacing-call-controls/call-controls-button-media-off.png';
import CallControlsViewMediaButtonOn from '../assets/05-ui-cookbook/02-replacing-call-controls/call-controls-button-media-on.png';
import CallControlsViewMediaButtonOff from '../assets/05-ui-cookbook/02-replacing-call-controls/call-controls-button-media-off.png';

The Stream Video React Native SDK allows building your own Call controls view/layout. Call Controls View generally comprises several buttons that control the call. Each button controls its area of responsibility. Our task, as integrators, is to create a component that puts these buttons together as we wish. In this example, we intend to show how to do just that.

Expand Down Expand Up @@ -151,48 +149,28 @@ const styles = StyleSheet.create({
Toggling microphone in an active call turns around publishing audio input streams and enabling the audio state. The bare-bones button to toggle audio in an active call could look like the following:

```tsx
import { useCallback } from 'react';
import { Pressable, Text } from 'react-native';
import {
SfuModels,
useLocalParticipant,
useMediaStreamManagement,
} from '@stream-io/video-react-native-sdk';
import { useMediaStreamManagement } from '@stream-io/video-react-native-sdk';

export const ToggleAudioButton = () => {
const localParticipant = useLocalParticipant();
const isAudioPublished = localParticipant?.publishedTracks.includes(
SfuModels.TrackType.AUDIO,
);

const { publishAudioStream, stopPublishingAudio } =
useMediaStreamManagement();

const toggleAudioMuted = useCallback(async () => {
if (isAudioPublished) {
stopPublishingAudio();
} else {
await publishAudioStream();
}
}, [isAudioPublished, publishAudioStream, stopPublishingAudio]);
const { isAudioMuted, toggleAudioMuted } = useMediaStreamManagement();

const audioButtonStyles = [
styles.button,
{
backgroundColor: !isAudioPublished ? '#080707dd' : 'white',
backgroundColor: !isAudioMuted ? '#080707dd' : 'white',
},
];

const audioButtonTextStyles = [
styles.mediaButtonText,
{
color: !isAudioPublished ? 'white' : '#080707dd',
color: !isAudioMuted ? 'white' : '#080707dd',
},
];

return (
<Pressable onPress={toggleAudioMuted} style={audioButtonStyles}>
{isAudioPublished ? (
{!isAudioMuted ? (
<Text style={audioButtonTextStyles}>Audio On</Text>
) : (
<Text style={audioButtonTextStyles}>Audio Off</Text>
Expand All @@ -214,7 +192,7 @@ const styles = StyleSheet.create({
mediaButtonText: {
textAlign: 'center',
},
})
});
```

We check the audio mute status by checking if `SfuModels.TrackType.AUDIO` exists in the array of types of tracks published by `localParticipant`.
Expand All @@ -225,48 +203,28 @@ To mute and unmute the local participant's audio, we use `publishAudioStream` an
Toggling video in an active call turns around publishing video input streams and enabling the video state. The bare-bones button to toggle video in an active call could look like the following:

```tsx
import { useCallback } from 'react';
import { Pressable, Text } from 'react-native';
import {
SfuModels,
useLocalParticipant,
useMediaStreamManagement,
} from '@stream-io/video-react-native-sdk';

export const ToggleVideoButton = () => {
const localParticipant = useLocalParticipant();
const isVideoPublished = localParticipant?.publishedTracks.includes(
SfuModels.TrackType.VIDEO,
);
import { useMediaStreamManagement } from '@stream-io/video-react-native-sdk';

const { publishVideoStream, stopPublishingVideo } =
useMediaStreamManagement();

const toggleVideoMuted = useCallback(async () => {
if (isVideoPublished) {
stopPublishingVideo();
} else {
await publishVideoStream();
}
}, [isVideoPublished, publishVideoStream, stopPublishingVideo]);
export const ToggleAudioButton = () => {
const { isVideoMuted, toggleVideoMuted } = useMediaStreamManagement();

const videoButtonStyles = [
styles.button,
{
backgroundColor: !isVideoPublished ? '#080707dd' : 'white',
backgroundColor: !isVideoMuted ? '#080707dd' : 'white',
},
];

const videoButtonTextStyles = [
styles.mediaButtonText,
{
color: !isVideoPublished ? 'white' : '#080707dd',
color: !isVideoMuted ? 'white' : '#080707dd',
},
];

return (
<Pressable onPress={toggleVideoMuted} style={videoButtonStyles}>
{isVideoPublished ? (
{!isVideoMuted ? (
<Text style={videoButtonTextStyles}>Video On</Text>
) : (
<Text style={videoButtonTextStyles}>Video Off</Text>
Expand All @@ -288,7 +246,7 @@ const styles = StyleSheet.create({
mediaButtonText: {
textAlign: 'center',
},
})
});
```

We check the video mute status by checking if `SfuModels.TrackType.VIDEO` exists in the array of types of tracks published by `localParticipant`.
Expand All @@ -301,10 +259,7 @@ Toggling camera face for mobile devices is an important feature. So, to built it
The `isCameraOnFrontFacingMode` is a boolean that tracks if the camera is `front` facing. The `toggleCameraFacingMode` is used to toggle camera between `front` and `back`/`environment` facing modes.

```tsx
import { Pressable, Text } from 'react-native';
import {
useMediaStreamManagement,
} from '@stream-io/video-react-native-sdk';
import { useMediaStreamManagement } from '@stream-io/video-react-native-sdk';

export const ToggleCameraFaceButton = () => {
const { isCameraOnFrontFacingMode, toggleCameraFacingMode } =
Expand Down Expand Up @@ -348,7 +303,7 @@ const styles = StyleSheet.create({
mediaButtonText: {
textAlign: 'center',
},
})
});
```

### Assembling it all together
Expand Down Expand Up @@ -376,7 +331,7 @@ const styles = StyleSheet.create({
justifyContent: 'space-evenly',
paddingVertical: 10,
},
})
});
```

#### Media Button
Expand Down Expand Up @@ -415,5 +370,5 @@ const styles = StyleSheet.create({
justifyContent: 'space-evenly',
paddingVertical: 10,
},
})
});
```
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,21 @@ import {
useI18n,
} from '@stream-io/video-react-bindings';
import { CallControlsButton } from './CallControlsButton';
import { useCallControls, usePermissionNotification } from '../../../hooks';
import { usePermissionNotification } from '../../../hooks';
import { theme } from '../../../theme';
import { Mic, MicOff } from '../../../icons';
import { Alert, StyleSheet } from 'react-native';
import { muteStatusColor } from '../../../utils';
import { useMediaStreamManagement } from '../../../providers';

export const ToggleAudioButton = () => {
const [isAwaitingApproval, setIsAwaitingApproval] = useState(false);
const { toggleAudioMuted, isAudioPublished } = useCallControls();
const { isAudioMuted, toggleAudioMuted } = useMediaStreamManagement();
const userHasSendAudioCapability = useHasPermissions(
OwnCapability.SEND_AUDIO,
);
const { t } = useI18n();

const isAudioMuted = !isAudioPublished;

usePermissionNotification({
permission: OwnCapability.SEND_AUDIO,
messageApproved: t('You can now speak.'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,14 @@ import { muteStatusColor } from '../../../utils';
import { Alert, StyleSheet } from 'react-native';
import { theme } from '../../../theme';
import { Video, VideoSlash } from '../../../icons';
import { useCallControls, usePermissionNotification } from '../../../hooks';
import { usePermissionNotification } from '../../../hooks';
import { useMediaStreamManagement } from '../../../providers';

export const ToggleVideoButton = () => {
const [isAwaitingApproval, setIsAwaitingApproval] = useState(false);
const { toggleVideoMuted, isVideoPublished } = useCallControls();
const { isVideoMuted, toggleVideoMuted } = useMediaStreamManagement();
const { t } = useI18n();

const isVideoMuted = !isVideoPublished;

const userHasSendVideoCapability = useHasPermissions(
OwnCapability.SEND_VIDEO,
);
Expand Down
2 changes: 0 additions & 2 deletions packages/react-native-sdk/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
export * from './useCallControls';
export * from './useLocalVideoStream';
export * from './useIncallManager';
export * from './usePermissionRequest';
export * from './usePermissionNotification';
export * from './push';
export * from './useAndroidKeepCallAliveEffect';
export * from './useCallControls';
51 changes: 0 additions & 51 deletions packages/react-native-sdk/src/hooks/useCallControls.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ export type MediaStreamManagementContextAPI = {
* Publishes video stream for currently selected video input (camera) device to other call participants.
*/
publishVideoStream: () => Promise<void>;
/**
* Signals whether local audio stream is muted/unmuted when in the call.
*/
isAudioMuted: boolean;
/**
* Signals whether local video stream is muted/unmuted when in the call.
*/
isVideoMuted: boolean;
/**
* Signals whether audio stream will be published when the call is joined.
*/
Expand All @@ -60,6 +68,14 @@ export type MediaStreamManagementContextAPI = {
* The latest value set will be used to decide, whether audio stream will be published when joining a call.
*/
toggleInitialVideoMuteState: () => void;
/**
* Toggles the video mute status, when in the call.
*/
toggleVideoMuted: () => void;
/**
* Toggles the audio mute status, when in the call.
*/
toggleAudioMuted: () => void;
/**
* Toggles the camera facing mode between front and back camera.
*/
Expand Down Expand Up @@ -89,9 +105,16 @@ const MediaStreamContext =
*/
export const MediaStreamManagement = ({ children }: PropsWithChildren<{}>) => {
const call = useCall();
const localParticipant = useLocalParticipant();
const callingState = useCallCallingState();
const videoDevices = useStreamVideoStoreValue((store) => store.videoDevices);
const localVideoStream = useLocalParticipant()?.videoStream;
const isAudioMuted = !localParticipant?.publishedTracks.includes(
SfuModels.TrackType.AUDIO,
);
const isVideoMuted = !localParticipant?.publishedTracks.includes(
SfuModels.TrackType.VIDEO,
);

const [isCameraOnFrontFacingMode, setIsCameraOnFrontFacingMode] =
useState(true);
Expand Down Expand Up @@ -152,6 +175,22 @@ export const MediaStreamManagement = ({ children }: PropsWithChildren<{}>) => {
}
}, [call, callingState]);

const toggleVideoMuted = useCallback(async () => {
if (isVideoMuted) {
publishVideoStream();
} else {
stopPublishingVideo();
}
}, [isVideoMuted, publishVideoStream, stopPublishingVideo]);

const toggleAudioMuted = useCallback(async () => {
if (isAudioMuted) {
publishAudioStream();
} else {
stopPublishingAudio();
}
}, [isAudioMuted, publishAudioStream, stopPublishingAudio]);

const toggleInitialAudioMuteState = useCallback(
() =>
setInitialAudioEnabled((prev) => {
Expand Down Expand Up @@ -191,9 +230,13 @@ export const MediaStreamManagement = ({ children }: PropsWithChildren<{}>) => {

const contextValue = useMemo(() => {
return {
isAudioMuted,
isVideoMuted,
initialAudioEnabled: initAudioEnabled,
initialVideoEnabled: initVideoEnabled,
isCameraOnFrontFacingMode,
toggleAudioMuted,
toggleVideoMuted,
toggleInitialAudioMuteState,
toggleInitialVideoMuteState,
toggleCameraFacingMode,
Expand All @@ -203,9 +246,13 @@ export const MediaStreamManagement = ({ children }: PropsWithChildren<{}>) => {
stopPublishingVideo,
};
}, [
isAudioMuted,
isVideoMuted,
initAudioEnabled,
initVideoEnabled,
isCameraOnFrontFacingMode,
toggleAudioMuted,
toggleVideoMuted,
toggleInitialAudioMuteState,
toggleInitialVideoMuteState,
toggleCameraFacingMode,
Expand Down

0 comments on commit ee61455

Please sign in to comment.