Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 10 additions & 17 deletions sample-apps/react-native/dogfood/src/components/ActiveCall.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
Expand Down Expand Up @@ -118,22 +117,17 @@ export const ActiveCall = ({

const { toggleCallRecording, isAwaitingResponse, isCallRecordingInProgress } =
useToggleCallRecording();
const { useIsCallCaptioningInProgress } = useCallStateHooks();
const isCaptioningInProgress = useIsCallCaptioningInProgress();

const CustomBottomControls = useCallback(() => {
return (
<>
{isCaptioningInProgress && <ClosedCaptions />}
<BottomControls
onParticipantInfoPress={onOpenCallParticipantsInfo}
onChatOpenHandler={onChatOpenHandler}
unreadCountIndicator={unreadCountIndicator}
toggleCallRecording={toggleCallRecording}
isCallRecordingInProgress={isCallRecordingInProgress}
isAwaitingResponse={isAwaitingResponse}
/>
</>
<BottomControls
onParticipantInfoPress={onOpenCallParticipantsInfo}
onChatOpenHandler={onChatOpenHandler}
unreadCountIndicator={unreadCountIndicator}
toggleCallRecording={toggleCallRecording}
isCallRecordingInProgress={isCallRecordingInProgress}
isAwaitingResponse={isAwaitingResponse}
/>
);
}, [
onChatOpenHandler,
Expand All @@ -142,7 +136,6 @@ export const ActiveCall = ({
toggleCallRecording,
isAwaitingResponse,
isCallRecordingInProgress,
isCaptioningInProgress,
]);

const CustomTopControls = useCallback(() => {
Expand Down Expand Up @@ -173,7 +166,7 @@ export const ActiveCall = ({
landscape={isLandscape}
layout={selectedLayout}
/>
<ParticipantsInfoList
<ParticipantsInfoListModal
isCallParticipantsInfoVisible={isCallParticipantsVisible}
setIsCallParticipantsInfoVisible={setIsCallParticipantsVisible}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 (
<View style={styles.container}>
{isSpeakingWhileMuted && (
<View style={styles.speakingLabelContainer}>
<Text style={styles.label}>You are muted. Unmute to speak.</Text>
</View>
)}
<View style={[styles.callControlsWrapper]}>
<>
<View style={styles.container} onLayout={onLayout}>
<View style={styles.left}>
<MoreActionsButton />
<MoreActionsButton
controlsContainerHeight={controlsContainerHeight}
/>
<ToggleAudioPublishingButton />
<ToggleVideoPublishingButton />
<ScreenShareToggleButton />
Expand All @@ -65,36 +65,64 @@ export const BottomControls = ({
/>
</View>
</View>
{!!controlsContainerHeight && (
<SubtitleContainer controlsContainerHeight={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 (
<View
style={[styles.subtitleContainer, { bottom: controlsContainerHeight }]}
>
<ClosedCaptions />
<View style={styles.speakingLabelContainer}>
<Text style={styles.label}>{'You are muted. Unmute to speak.'}</Text>
</View>
</View>
);
};

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',
Expand All @@ -108,6 +136,6 @@ const useStyles = (showMicLabel: boolean) => {
gap: theme.variants.spacingSizes.xs,
},
}),
[theme, showMicLabel],
[theme],
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@ export const ClosedCaptions = () => {
const { useCallClosedCaptions } = useCallStateHooks();
const closedCaptions = useCallClosedCaptions();
const styles = useStyles();

if (closedCaptions.length === 0) {
return null;
}

return (
<View style={styles.rootContainer}>
{closedCaptions.map(({ user, start_time, text }) => (
<View style={styles.closedCaptionItem} key={`${user.id}/${start_time}`}>
<Text style={styles.speakerName}>{user.name}:</Text>
<Text style={styles.speakerName}>{`${user.name}:`}</Text>
<Text style={styles.closedCaption}>{text}</Text>
</View>
))}
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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<DrawerProps> = ({
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();
Expand All @@ -63,7 +66,7 @@ export const AndroidAudioRoutePickerDrawer: React.FC<DrawerProps> = ({
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<any>(
new Animated.Value(drawerHeight + offset),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -37,22 +36,24 @@ type DrawerProps = {
showCallStats: boolean;
onClose: () => void;
options: DrawerOption[];
bottomControlsHeight: number;
};

export const BottomControlsDrawer: React.FC<DrawerProps> = ({
isVisible,
showCallStats,
onClose,
options,
bottomControlsHeight,
}) => {
const { theme } = useTheme();
const screenHeight = Dimensions.get('window').height;
const drawerHeight = screenHeight * 0.8;
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<any>(
new Animated.Value(drawerHeight + offset),
Expand Down
Loading