diff --git a/sample-apps/react-native/dogfood/src/components/ActiveCall.tsx b/sample-apps/react-native/dogfood/src/components/ActiveCall.tsx
index c5206c9737..3115611681 100644
--- a/sample-apps/react-native/dogfood/src/components/ActiveCall.tsx
+++ b/sample-apps/react-native/dogfood/src/components/ActiveCall.tsx
@@ -23,7 +23,7 @@ import {
StyleSheet,
View,
} from 'react-native';
-import { ParticipantsInfoList } from './ParticipantsInfoList';
+import { ParticipantsInfoListModal } from './ParticipantsInfoListModal';
import { BottomControls } from './CallControlls/BottomControls';
import { useOrientation } from '../hooks/useOrientation';
import { Z_INDEX } from '../constants';
@@ -32,7 +32,6 @@ import { useLayout } from '../contexts/LayoutContext';
import { useAppGlobalStoreValue } from '../contexts/AppContext';
import DeviceInfo from 'react-native-device-info';
import Toast from 'react-native-toast-message';
-import { ClosedCaptions } from './ClosedCaptions';
type ActiveCallProps = {
onHangupCallHandler?: () => void;
@@ -118,22 +117,17 @@ export const ActiveCall = ({
const { toggleCallRecording, isAwaitingResponse, isCallRecordingInProgress } =
useToggleCallRecording();
- const { useIsCallCaptioningInProgress } = useCallStateHooks();
- const isCaptioningInProgress = useIsCallCaptioningInProgress();
const CustomBottomControls = useCallback(() => {
return (
- <>
- {isCaptioningInProgress && }
-
- >
+
);
}, [
onChatOpenHandler,
@@ -142,7 +136,6 @@ export const ActiveCall = ({
toggleCallRecording,
isAwaitingResponse,
isCallRecordingInProgress,
- isCaptioningInProgress,
]);
const CustomTopControls = useCallback(() => {
@@ -173,7 +166,7 @@ export const ActiveCall = ({
landscape={isLandscape}
layout={selectedLayout}
/>
-
diff --git a/sample-apps/react-native/dogfood/src/components/CallControlls/BottomControls.tsx b/sample-apps/react-native/dogfood/src/components/CallControlls/BottomControls.tsx
index 988dccbc85..68a49471af 100644
--- a/sample-apps/react-native/dogfood/src/components/CallControlls/BottomControls.tsx
+++ b/sample-apps/react-native/dogfood/src/components/CallControlls/BottomControls.tsx
@@ -6,13 +6,13 @@ import {
useCallStateHooks,
useTheme,
} from '@stream-io/video-react-native-sdk';
-import React, { useMemo } from 'react';
-import { StyleSheet, Text, View } from 'react-native';
-import { BOTTOM_CONTROLS_HEIGHT, Z_INDEX } from '../../constants';
+import React, { useMemo, useState } from 'react';
+import { LayoutChangeEvent, StyleSheet, Text, View } from 'react-native';
import { MoreActionsButton } from './MoreActionsButton';
import { ParticipantsButton } from './ParticipantsButton';
import { ChatButton } from './ChatButton';
import { RecordCallButton } from './RecordCallButton';
+import { ClosedCaptions } from './ClosedCaptions';
export type BottomControlsProps = Pick<
CallContentProps,
@@ -34,20 +34,20 @@ export const BottomControls = ({
isAwaitingResponse,
isCallRecordingInProgress,
}: BottomControlsProps) => {
- const { useMicrophoneState } = useCallStateHooks();
- const { isSpeakingWhileMuted } = useMicrophoneState();
- const styles = useStyles(isSpeakingWhileMuted);
+ const styles = useStyles();
+ const [controlsContainerHeight, setControlsContainerHeight] = useState(0);
+
+ const onLayout = (event: LayoutChangeEvent) => {
+ setControlsContainerHeight(event.nativeEvent.layout.height);
+ };
return (
-
- {isSpeakingWhileMuted && (
-
- You are muted. Unmute to speak.
-
- )}
-
+ <>
+
-
+
@@ -65,36 +65,64 @@ export const BottomControls = ({
/>
+ {!!controlsContainerHeight && (
+
+ )}
+ >
+ );
+};
+
+// speaking while muted and caption controls - aka subtitle on top of video
+const SubtitleContainer = ({
+ controlsContainerHeight,
+}: {
+ controlsContainerHeight: number;
+}) => {
+ const styles = useStyles();
+ const { useIsCallCaptioningInProgress, useMicrophoneState } =
+ useCallStateHooks();
+ const isCaptioningInProgress = useIsCallCaptioningInProgress();
+ const { isSpeakingWhileMuted } = useMicrophoneState();
+ if (!isCaptioningInProgress || !isSpeakingWhileMuted) {
+ return null;
+ }
+ return (
+
+
+
+ {'You are muted. Unmute to speak.'}
+
);
};
-const useStyles = (showMicLabel: boolean) => {
+const useStyles = () => {
const { theme } = useTheme();
return useMemo(
() =>
StyleSheet.create({
container: {
- paddingVertical: !showMicLabel ? theme.variants.spacingSizes.md : 0,
+ paddingTop: theme.variants.spacingSizes.sm,
+ paddingBottom: theme.variants.spacingSizes.md,
paddingHorizontal: theme.variants.spacingSizes.md,
- backgroundColor: theme.colors.sheetPrimary,
- height: BOTTOM_CONTROLS_HEIGHT,
+ flexDirection: 'row',
+ justifyContent: 'flex-start',
+ },
+ subtitleContainer: {
+ position: 'absolute',
+ left: 0,
+ right: 0,
},
speakingLabelContainer: {
backgroundColor: theme.colors.sheetPrimary,
- width: '100%',
},
label: {
textAlign: 'center',
color: theme.colors.textPrimary,
},
- callControlsWrapper: {
- display: 'flex',
- flexDirection: 'row',
- justifyContent: 'flex-start',
- zIndex: Z_INDEX.IN_FRONT,
- },
left: {
flex: 2.5,
flexDirection: 'row',
@@ -108,6 +136,6 @@ const useStyles = (showMicLabel: boolean) => {
gap: theme.variants.spacingSizes.xs,
},
}),
- [theme, showMicLabel],
+ [theme],
);
};
diff --git a/sample-apps/react-native/dogfood/src/components/ClosedCaptions.tsx b/sample-apps/react-native/dogfood/src/components/CallControlls/ClosedCaptions.tsx
similarity index 81%
rename from sample-apps/react-native/dogfood/src/components/ClosedCaptions.tsx
rename to sample-apps/react-native/dogfood/src/components/CallControlls/ClosedCaptions.tsx
index 09c10c83ca..f675c40beb 100644
--- a/sample-apps/react-native/dogfood/src/components/ClosedCaptions.tsx
+++ b/sample-apps/react-native/dogfood/src/components/CallControlls/ClosedCaptions.tsx
@@ -6,11 +6,16 @@ export const ClosedCaptions = () => {
const { useCallClosedCaptions } = useCallStateHooks();
const closedCaptions = useCallClosedCaptions();
const styles = useStyles();
+
+ if (closedCaptions.length === 0) {
+ return null;
+ }
+
return (
{closedCaptions.map(({ user, start_time, text }) => (
- {user.name}:
+ {`${user.name}:`}
{text}
))}
@@ -25,17 +30,13 @@ const useStyles = () => {
StyleSheet.create({
rootContainer: {
backgroundColor: theme.colors.sheetPrimary,
- padding: theme.variants.spacingSizes.md,
+ padding: theme.variants.spacingSizes.sm,
width: '100%',
- minHeight: 55,
},
closedCaptionItem: {
flexDirection: 'row',
flexWrap: 'wrap',
- flexShrink: 1,
- gap: theme.variants.spacingSizes.xs,
- alignItems: 'flex-start',
- marginBottom: 4,
+ columnGap: theme.variants.spacingSizes.xs,
},
speakerName: {
color: theme.colors.textSecondary,
diff --git a/sample-apps/react-native/dogfood/src/components/AndroidAudioRoutePickerDrawer.tsx b/sample-apps/react-native/dogfood/src/components/CallControlls/MoreActionsButton/AndroidAudioRoutePickerDrawer.tsx
similarity index 92%
rename from sample-apps/react-native/dogfood/src/components/AndroidAudioRoutePickerDrawer.tsx
rename to sample-apps/react-native/dogfood/src/components/CallControlls/MoreActionsButton/AndroidAudioRoutePickerDrawer.tsx
index 90d2186474..0052cd1cb1 100644
--- a/sample-apps/react-native/dogfood/src/components/AndroidAudioRoutePickerDrawer.tsx
+++ b/sample-apps/react-native/dogfood/src/components/CallControlls/MoreActionsButton/AndroidAudioRoutePickerDrawer.tsx
@@ -20,7 +20,7 @@ import {
useWindowDimensions,
View,
} from 'react-native';
-import { BOTTOM_CONTROLS_HEIGHT } from '../constants';
+
import { SafeAreaView } from 'react-native-safe-area-context';
type DrawerProps = {
@@ -31,20 +31,23 @@ type DrawerProps = {
const endpointNameToIconImage = (endPointName: AudioDeviceEndpointType) => {
switch (endPointName) {
case 'Speaker':
- return require('../assets/audio-routes/volume_up_24dp.png');
+ return require('../../../assets/audio-routes/volume_up_24dp.png');
case 'Earpiece':
- return require('../assets/audio-routes/call_24dp.png');
+ return require('../../../assets/audio-routes/call_24dp.png');
case 'Wired Headset':
- return require('../assets/audio-routes/headphones_24dp.png');
+ return require('../../../assets/audio-routes/headphones_24dp.png');
default:
- return require('../assets/audio-routes/bluetooth_connected_24dp.png');
+ return require('../../../assets/audio-routes/bluetooth_connected_24dp.png');
}
};
-export const AndroidAudioRoutePickerDrawer: React.FC = ({
- isVisible,
- onClose,
-}) => {
+type AndroidAudioRoutePickerDrawerProps = DrawerProps & {
+ bottomControlsHeight: number;
+};
+
+export const AndroidAudioRoutePickerDrawer: React.FC<
+ AndroidAudioRoutePickerDrawerProps
+> = ({ isVisible, onClose, bottomControlsHeight }) => {
const screenHeight = useWindowDimensions().height;
const drawerHeight = screenHeight * 0.8;
const styles = useStyles();
@@ -63,7 +66,7 @@ export const AndroidAudioRoutePickerDrawer: React.FC = ({
const selectedAudioDeviceName = audioDeviceStatus?.selectedDevice;
// negative offset is needed so the drawer component start above the bottom controls
- const offset = -BOTTOM_CONTROLS_HEIGHT;
+ const offset = -bottomControlsHeight;
const translateY = useRef(
new Animated.Value(drawerHeight + offset),
diff --git a/sample-apps/react-native/dogfood/src/components/BottomControlsDrawer.tsx b/sample-apps/react-native/dogfood/src/components/CallControlls/MoreActionsButton/BottomControlsDrawer.tsx
similarity index 96%
rename from sample-apps/react-native/dogfood/src/components/BottomControlsDrawer.tsx
rename to sample-apps/react-native/dogfood/src/components/CallControlls/MoreActionsButton/BottomControlsDrawer.tsx
index 0136282373..cd11fa9f0b 100644
--- a/sample-apps/react-native/dogfood/src/components/BottomControlsDrawer.tsx
+++ b/sample-apps/react-native/dogfood/src/components/CallControlls/MoreActionsButton/BottomControlsDrawer.tsx
@@ -19,10 +19,9 @@ import {
TouchableWithoutFeedback,
View,
} from 'react-native';
-import { BOTTOM_CONTROLS_HEIGHT } from '../constants';
-import RaiseHand from '../assets/RaiseHand';
-import { CallStats } from './CallStats';
-import { VideoFilters } from './VideoEffects';
+import RaiseHand from '../../../assets/RaiseHand';
+import { CallStats } from '../../CallStats';
+import { VideoFilters } from '../../VideoEffects';
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
export type DrawerOption = {
@@ -37,6 +36,7 @@ type DrawerProps = {
showCallStats: boolean;
onClose: () => void;
options: DrawerOption[];
+ bottomControlsHeight: number;
};
export const BottomControlsDrawer: React.FC = ({
@@ -44,6 +44,7 @@ export const BottomControlsDrawer: React.FC = ({
showCallStats,
onClose,
options,
+ bottomControlsHeight,
}) => {
const { theme } = useTheme();
const screenHeight = Dimensions.get('window').height;
@@ -51,8 +52,8 @@ export const BottomControlsDrawer: React.FC = ({
const styles = useStyles();
const call = useCall();
- // negative offset is needed so the drawer component start above the bottom controls
- const offset = -BOTTOM_CONTROLS_HEIGHT;
+ // negative offset to position the drawer component above the bottom controls
+ const offset = -bottomControlsHeight;
const translateY = useRef(
new Animated.Value(drawerHeight + offset),
diff --git a/sample-apps/react-native/dogfood/src/components/CallControlls/MoreActionsButton.tsx b/sample-apps/react-native/dogfood/src/components/CallControlls/MoreActionsButton/index.tsx
similarity index 88%
rename from sample-apps/react-native/dogfood/src/components/CallControlls/MoreActionsButton.tsx
rename to sample-apps/react-native/dogfood/src/components/CallControlls/MoreActionsButton/index.tsx
index 084dfd1d56..cd306e2cd0 100644
--- a/sample-apps/react-native/dogfood/src/components/CallControlls/MoreActionsButton.tsx
+++ b/sample-apps/react-native/dogfood/src/components/CallControlls/MoreActionsButton/index.tsx
@@ -20,31 +20,35 @@ import {
View,
} from 'react-native';
import { IconWrapper } from '@stream-io/video-react-native-sdk/src/icons';
-import MoreActions from '../../assets/MoreActions';
-import { BottomControlsDrawer, DrawerOption } from '../BottomControlsDrawer';
-import Feedback from '../../assets/Feedback';
-import FeedbackModal from '../FeedbackModal';
+import MoreActions from '../../../assets/MoreActions';
+import { BottomControlsDrawer, DrawerOption } from './BottomControlsDrawer';
+import Feedback from '../../../assets/Feedback';
+import FeedbackModal from '../../FeedbackModal';
import {
ThemeMode,
useAppGlobalStoreSetState,
useAppGlobalStoreValue,
-} from '../../contexts/AppContext';
-import LightDark from '../../assets/LightDark';
-import Stats from '../../assets/Stats';
-import ClosedCaptions from '../../assets/ClosedCaptions';
-import Screenshot from '../../assets/Screenshot';
-import Hearing from '../../assets/Hearing';
-import { AudioOutput } from '../../assets/AudioOutput';
-import { AndroidAudioRoutePickerDrawer } from '../AndroidAudioRoutePickerDrawer';
+} from '../../../contexts/AppContext';
+import LightDark from '../../../assets/LightDark';
+import Stats from '../../../assets/Stats';
+import ClosedCaptions from '../../../assets/ClosedCaptions';
+import Screenshot from '../../../assets/Screenshot';
+import Hearing from '../../../assets/Hearing';
+import { AudioOutput } from '../../../assets/AudioOutput';
+import { AndroidAudioRoutePickerDrawer } from './AndroidAudioRoutePickerDrawer';
/**
* The props for the More Actions Button in the Call Controls.
*/
-export type MoreActionsButtonProps = {
+type MoreActionsButtonProps = {
/**
* Handler to be called when the more actions button is pressed.
*/
onPressHandler?: () => void;
+ /**
+ * The height of the bottom controls container.
+ */
+ controlsContainerHeight: number;
};
/**
@@ -54,6 +58,7 @@ export type MoreActionsButtonProps = {
*/
export const MoreActionsButton = ({
onPressHandler,
+ controlsContainerHeight,
}: MoreActionsButtonProps) => {
const {
theme: { colors, variants, moreActionsButton, defaults },
@@ -309,23 +314,27 @@ export const MoreActionsButton = ({
style={moreActionsButton}
color={buttonColor}
>
- {Platform.OS === 'android' && (
+ {Platform.OS === 'android' && !!controlsContainerHeight && (
{
setIsAndroidAudioRoutePickerDrawerVisible(false);
}}
/>
)}
- {
- setShowCallStats(false);
- setIsDrawerVisible(false);
- }}
- options={options}
- showCallStats={showCallStats}
- />
+ {!!controlsContainerHeight && (
+ {
+ setShowCallStats(false);
+ setIsDrawerVisible(false);
+ }}
+ options={options}
+ showCallStats={showCallStats}
+ />
+ )}
setFeedbackModalVisible(false)}
diff --git a/sample-apps/react-native/dogfood/src/components/ParticipantsInfoList.tsx b/sample-apps/react-native/dogfood/src/components/ParticipantsInfoListModal.tsx
similarity index 99%
rename from sample-apps/react-native/dogfood/src/components/ParticipantsInfoList.tsx
rename to sample-apps/react-native/dogfood/src/components/ParticipantsInfoListModal.tsx
index d2280ae06c..01a979068b 100644
--- a/sample-apps/react-native/dogfood/src/components/ParticipantsInfoList.tsx
+++ b/sample-apps/react-native/dogfood/src/components/ParticipantsInfoListModal.tsx
@@ -35,7 +35,7 @@ import { ButtonTestIds } from '../constants/TestIds';
import { useAppGlobalStoreValue } from '../contexts/AppContext';
import { SafeAreaView } from 'react-native-safe-area-context';
-export interface ParticipantsInfoListProps {
+interface ParticipantsInfoListProps {
/**
* Boolean that decides whether the CallParticipantsInfo modal should be open or not.
*/
@@ -53,7 +53,7 @@ export interface ParticipantsInfoListProps {
* their mute states, video states, screen share states, etc.
* Mute all participants, invite participants, etc.
**/
-export const ParticipantsInfoList = ({
+export const ParticipantsInfoListModal = ({
isCallParticipantsInfoVisible,
setIsCallParticipantsInfoVisible,
}: ParticipantsInfoListProps) => {
diff --git a/sample-apps/react-native/dogfood/src/constants/index.ts b/sample-apps/react-native/dogfood/src/constants/index.ts
index acdf552504..3e5414f059 100644
--- a/sample-apps/react-native/dogfood/src/constants/index.ts
+++ b/sample-apps/react-native/dogfood/src/constants/index.ts
@@ -1,7 +1,6 @@
export const BUTTON_HEIGHT = 50;
export const INPUT_HEIGHT = 50;
export const AVATAR_SIZE = 50;
-export const BOTTOM_CONTROLS_HEIGHT = 76;
export const FEEDBACK_MODAL_MAX_WIDTH = 385;
export const Z_INDEX = {