Skip to content

Commit

Permalink
feat(react-native): support landscape more for calls
Browse files Browse the repository at this point in the history
  • Loading branch information
khushal87 committed Sep 29, 2023
1 parent ffe2875 commit bcdd6cf
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useEffect, useRef, useState } from 'react';
import { StyleSheet, View } from 'react-native';
import { StyleSheet, View, ViewStyle } from 'react-native';
import {
CallTopView as DefaultCallTopView,
CallTopViewProps,
Expand All @@ -25,6 +25,7 @@ import {
ParticipantViewComponentProps,
} from '../../Participant';
import { useTheme } from '../../../contexts';
import { useOrientation } from '../../../utils/hooks/useOrientation';

export type CallParticipantsComponentProps = Pick<
CallParticipantsGridProps,
Expand Down Expand Up @@ -86,6 +87,7 @@ export const CallContent = ({
const {
theme: { callContent },
} = useTheme();
const orientation = useOrientation();
const {
useHasOngoingScreenShare,
useRemoteParticipants,
Expand Down Expand Up @@ -151,8 +153,12 @@ export const CallContent = ({
CallParticipantsList,
};

const landScapeStyles: ViewStyle = {
flexDirection: orientation === 'portrait' ? 'column' : 'row',
};

return (
<View style={[styles.container, callContent.container]}>
<View style={[styles.container, callContent.container, landScapeStyles]}>
<View style={[styles.container, callContent.callParticipantsContainer]}>
<View
style={[styles.view, callContent.topContainer]}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React from 'react';
import { StyleSheet, View, ViewProps } from 'react-native';
import { StyleSheet, View, ViewProps, ViewStyle } from 'react-native';
import { ToggleAudioPublishingButton } from './ToggleAudioPublishingButton';
import { ToggleVideoPublishingButton } from './ToggleVideoPublishingButton';
import { ToggleCameraFaceButton } from './ToggleCameraFaceButton';
import { Z_INDEX } from '../../../constants';
import { HangUpCallButton } from './HangupCallButton';
import { useTheme } from '../../../contexts/ThemeContext';
import { useOrientation } from '../../../utils/hooks/useOrientation';

/**
* Props for the CallControls Component.
Expand All @@ -29,13 +30,20 @@ export const CallControls = ({
const {
theme: { colors, callControls },
} = useTheme();
const orientation = useOrientation();
const landScapeStyles: ViewStyle = {
flexDirection: orientation === 'landscape' ? 'column-reverse' : 'row',
paddingHorizontal: orientation === 'landscape' ? 12 : 0,
paddingVertical: orientation === 'portrait' ? 12 : 0,
};
return (
<View
style={[
styles.container,
{ backgroundColor: colors.static_grey },
style,
callControls.container,
landScapeStyles,
style,
]}
>
<ToggleVideoPublishingButton />
Expand All @@ -48,8 +56,6 @@ export const CallControls = ({

const styles = StyleSheet.create({
container: {
paddingVertical: 12,
flexDirection: 'row',
justifyContent: 'space-evenly',
zIndex: Z_INDEX.IN_FRONT,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { StyleSheet, View } from 'react-native';
import { StyleSheet, View, ViewStyle } from 'react-native';
import { useCallStateHooks } from '@stream-io/video-react-bindings';
import { useDebouncedValue } from '../../../utils/hooks/useDebouncedValue';
import {
Expand All @@ -9,6 +9,7 @@ import {
} from '../CallParticipantsList/CallParticipantsList';
import { ComponentTestIds } from '../../../constants/TestIds';
import { useTheme } from '../../../contexts/ThemeContext';
import { useOrientation } from '../../../utils/hooks/useOrientation';

/**
* Props for the CallParticipantsGrid component.
Expand Down Expand Up @@ -48,6 +49,10 @@ export const CallParticipantsGrid = ({
// we debounce the participants arrays to avoid unnecessary rerenders that happen when participant tracks are all subscribed simultaneously
const remoteParticipants = useDebouncedValue(_remoteParticipants, 300);
const allParticipants = useDebouncedValue(_allParticipants, 300);
const orientation = useOrientation();
const landScapeStyles: ViewStyle = {
flexDirection: orientation === 'landscape' ? 'row' : 'column',
};

const showFloatingView =
remoteParticipants.length > 0 && remoteParticipants.length < 3;
Expand All @@ -71,6 +76,7 @@ export const CallParticipantsGrid = ({
<View
style={[
styles.container,
landScapeStyles,
{ backgroundColor: colors.dark_gray },
callParticipantsGrid.container,
]}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
StreamVideoParticipant,
} from '@stream-io/video-client';
import { useCallStateHooks } from '@stream-io/video-react-bindings';
import { StyleSheet, View } from 'react-native';
import { StyleSheet, View, ViewStyle } from 'react-native';
import { useDebouncedValue } from '../../../utils/hooks/useDebouncedValue';
import { ComponentTestIds } from '../../../constants/TestIds';
import {
Expand All @@ -18,6 +18,7 @@ import {
ParticipantViewComponentProps,
} from '../../Participant';
import { useTheme } from '../../../contexts/ThemeContext';
import { useOrientation } from '../../../utils/hooks/useOrientation';

/**
* Props for the CallParticipantsSpotlight component.
Expand Down Expand Up @@ -57,6 +58,7 @@ export const CallParticipantsSpotlight = ({
const [participantInSpotlight, ...otherParticipants] = allParticipants;
const isScreenShareOnSpotlight = hasScreenShare(participantInSpotlight);
const isUserAloneInCall = _allParticipants?.length === 1;
const orientation = useOrientation();

const participantViewProps: ParticipantViewComponentProps = {
ParticipantLabel,
Expand All @@ -70,12 +72,16 @@ export const CallParticipantsSpotlight = ({
...participantViewProps,
ParticipantView,
};
const landScapeStyles: ViewStyle = {
flexDirection: orientation === 'landscape' ? 'row' : 'column',
};

return (
<View
testID={ComponentTestIds.CALL_PARTICIPANTS_SPOTLIGHT}
style={[
styles.container,
landScapeStyles,
{
backgroundColor: colors.dark_gray,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export const CallTopView = ({
ParticipantsInfoBadge = DefaultParticipantsInfoBadge,
}: CallTopViewProps) => {
const [callTopViewHeight, setCallTopViewHeight] = useState<number>(0);
const [callTopViewWidth, setCallTopViewWidth] = useState<number>(0);
const {
theme: {
colors,
Expand All @@ -64,16 +65,17 @@ export const CallTopView = ({
const isCallReconnecting = callingState === CallingState.RECONNECTING;

const onLayout: React.ComponentProps<typeof View>['onLayout'] = (event) => {
const { height } = event.nativeEvent.layout;
const { height, width } = event.nativeEvent.layout;
if (setCallTopViewHeight) {
setCallTopViewHeight(height);
setCallTopViewWidth(width);
}
};

return (
<View style={[styleProp, callTopView.container]}>
{/* Component for the background of the CallTopView. Since it has a Linear Gradient, an SVG is used to render it. */}
<TopViewBackground height={callTopViewHeight} width={'100%'} />
<TopViewBackground height={callTopViewHeight} width={callTopViewWidth} />
<View style={[styles.content, callTopView.content]} onLayout={onLayout}>
<View style={styles.leftElement}>
{onBackPressed && (
Expand Down
1 change: 1 addition & 0 deletions packages/react-native-sdk/src/utils/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './useAppStateListener';
export * from './useDebouncedValue';
export * from './usePrevious';
export * from './useOrientation';
34 changes: 34 additions & 0 deletions packages/react-native-sdk/src/utils/hooks/useOrientation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useEffect, useState } from 'react';
import { Dimensions } from 'react-native';

export type Orientation = 'portrait' | 'landscape';

const isPortrait = () => {
const dimensions = Dimensions.get('screen');
return dimensions.height >= dimensions.width;
};

/**
* A hook that returns device orientation.
* @returns 'portrait' : 'landscape'
*/
export const useOrientation = () => {
const [orientation, setOrientation] = useState<Orientation>(
isPortrait() ? 'portrait' : 'landscape',
);

useEffect(() => {
const updateOrientation = () => {
setOrientation(isPortrait() ? 'portrait' : 'landscape');
};

Dimensions.addEventListener('change', updateOrientation);

// return () => {
// // @ts-ignore
// Dimensions.removeEventListener('change', updateOrientation);
// };
}, []);

return orientation;
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<!-- Request legacy Bluetooth permissions on older devices. -->
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
Expand All @@ -32,7 +32,6 @@
android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="false" android:theme="@style/AppTheme">
<activity android:name=".MainActivity" android:label="@string/app_name"
android:screenOrientation="portrait"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:launchMode="singleTask" android:windowSoftInputMode="adjustResize"
android:exported="true">
Expand Down Expand Up @@ -70,4 +69,4 @@

<service android:name="io.wazo.callkeep.RNCallKeepBackgroundMessagingService" />
</application>
</manifest>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import {
ToggleVideoPublishingButton,
} from '@stream-io/video-react-native-sdk';
import React from 'react';
import { StyleSheet, View } from 'react-native';
import { StyleSheet, View, ViewStyle } from 'react-native';
import { appTheme } from '../theme';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { Z_INDEX } from '../constants';
import { useOrientation } from '../hooks/useOrientation';

export type CallControlsComponentProps = {
onChatOpenHandler?: () => void;
Expand All @@ -24,16 +25,17 @@ export const CallControlsComponent = ({
unreadCountIndicator,
}: CallControlsComponentProps) => {
const { bottom } = useSafeAreaInsets();
const orientation = useOrientation();
const landScapeStyles: ViewStyle = {
flexDirection: orientation === 'landscape' ? 'column-reverse' : 'row',
paddingHorizontal: orientation === 'landscape' ? 12 : 0,
paddingVertical: orientation === 'portrait' ? 12 : 0,
paddingBottom:
orientation === 'portrait' ? Math.max(bottom, appTheme.spacing.lg) : 0,
};

return (
<View
style={[
styles.callControlsWrapper,
{
paddingBottom: Math.max(bottom, appTheme.spacing.lg),
},
]}
>
<View style={[styles.callControlsWrapper, landScapeStyles]}>
<ReactionButton />
<ChatButton
onPressHandler={onChatOpenHandler}
Expand All @@ -49,9 +51,7 @@ export const CallControlsComponent = ({

const styles = StyleSheet.create({
callControlsWrapper: {
flexDirection: 'row',
justifyContent: 'space-evenly',
paddingVertical: appTheme.spacing.md,
zIndex: Z_INDEX.IN_FRONT,
backgroundColor: appTheme.colors.static_grey,
},
Expand Down
34 changes: 34 additions & 0 deletions sample-apps/react-native/dogfood/src/hooks/useOrientation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useEffect, useState } from 'react';
import { Dimensions } from 'react-native';

export type Orientation = 'portrait' | 'landscape';

const isPortrait = () => {
const dimensions = Dimensions.get('screen');
return dimensions.height >= dimensions.width;
};

/**
* A hook that returns device orientation.
* @returns 'portrait' : 'landscape'
*/
export const useOrientation = () => {
const [orientation, setOrientation] = useState<Orientation>(
isPortrait() ? 'portrait' : 'landscape',
);

useEffect(() => {
const updateOrientation = () => {
setOrientation(isPortrait() ? 'portrait' : 'landscape');
};

Dimensions.addEventListener('change', updateOrientation);

// return () => {
// // @ts-ignore
// Dimensions.removeEventListener('change', updateOrientation);
// };
}, []);

return orientation;
};

0 comments on commit bcdd6cf

Please sign in to comment.