From bcdd6cf20b3f4cd07dba2badececb491c3843bc7 Mon Sep 17 00:00:00 2001 From: Khushal Agarwal Date: Fri, 29 Sep 2023 17:51:00 +0530 Subject: [PATCH] feat(react-native): support landscape more for calls --- .../Call/CallContent/CallContent.tsx | 10 ++++-- .../Call/CallControls/CallControls.tsx | 14 +++++--- .../Call/CallLayout/CallParticipantsGrid.tsx | 8 ++++- .../CallLayout/CallParticipantsSpotlight.tsx | 8 ++++- .../Call/CallTopView/CallTopView.tsx | 6 ++-- .../react-native-sdk/src/utils/hooks/index.ts | 1 + .../src/utils/hooks/useOrientation.ts | 34 +++++++++++++++++++ .../android/app/src/main/AndroidManifest.xml | 5 ++- .../src/components/CallControlsComponent.tsx | 22 ++++++------ .../dogfood/src/hooks/useOrientation.ts | 34 +++++++++++++++++++ 10 files changed, 118 insertions(+), 24 deletions(-) create mode 100644 packages/react-native-sdk/src/utils/hooks/useOrientation.ts create mode 100644 sample-apps/react-native/dogfood/src/hooks/useOrientation.ts diff --git a/packages/react-native-sdk/src/components/Call/CallContent/CallContent.tsx b/packages/react-native-sdk/src/components/Call/CallContent/CallContent.tsx index e26b2cf040..e02dd27515 100644 --- a/packages/react-native-sdk/src/components/Call/CallContent/CallContent.tsx +++ b/packages/react-native-sdk/src/components/Call/CallContent/CallContent.tsx @@ -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, @@ -25,6 +25,7 @@ import { ParticipantViewComponentProps, } from '../../Participant'; import { useTheme } from '../../../contexts'; +import { useOrientation } from '../../../utils/hooks/useOrientation'; export type CallParticipantsComponentProps = Pick< CallParticipantsGridProps, @@ -86,6 +87,7 @@ export const CallContent = ({ const { theme: { callContent }, } = useTheme(); + const orientation = useOrientation(); const { useHasOngoingScreenShare, useRemoteParticipants, @@ -151,8 +153,12 @@ export const CallContent = ({ CallParticipantsList, }; + const landScapeStyles: ViewStyle = { + flexDirection: orientation === 'portrait' ? 'column' : 'row', + }; + return ( - + @@ -48,8 +56,6 @@ export const CallControls = ({ const styles = StyleSheet.create({ container: { - paddingVertical: 12, - flexDirection: 'row', justifyContent: 'space-evenly', zIndex: Z_INDEX.IN_FRONT, }, diff --git a/packages/react-native-sdk/src/components/Call/CallLayout/CallParticipantsGrid.tsx b/packages/react-native-sdk/src/components/Call/CallLayout/CallParticipantsGrid.tsx index e7f9a89ecc..1929b57b05 100644 --- a/packages/react-native-sdk/src/components/Call/CallLayout/CallParticipantsGrid.tsx +++ b/packages/react-native-sdk/src/components/Call/CallLayout/CallParticipantsGrid.tsx @@ -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 { @@ -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. @@ -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; @@ -71,6 +76,7 @@ export const CallParticipantsGrid = ({ { const [callTopViewHeight, setCallTopViewHeight] = useState(0); + const [callTopViewWidth, setCallTopViewWidth] = useState(0); const { theme: { colors, @@ -64,16 +65,17 @@ export const CallTopView = ({ const isCallReconnecting = callingState === CallingState.RECONNECTING; const onLayout: React.ComponentProps['onLayout'] = (event) => { - const { height } = event.nativeEvent.layout; + const { height, width } = event.nativeEvent.layout; if (setCallTopViewHeight) { setCallTopViewHeight(height); + setCallTopViewWidth(width); } }; return ( {/* Component for the background of the CallTopView. Since it has a Linear Gradient, an SVG is used to render it. */} - + {onBackPressed && ( diff --git a/packages/react-native-sdk/src/utils/hooks/index.ts b/packages/react-native-sdk/src/utils/hooks/index.ts index 75059f0674..39287fc74a 100644 --- a/packages/react-native-sdk/src/utils/hooks/index.ts +++ b/packages/react-native-sdk/src/utils/hooks/index.ts @@ -1,3 +1,4 @@ export * from './useAppStateListener'; export * from './useDebouncedValue'; export * from './usePrevious'; +export * from './useOrientation'; diff --git a/packages/react-native-sdk/src/utils/hooks/useOrientation.ts b/packages/react-native-sdk/src/utils/hooks/useOrientation.ts new file mode 100644 index 0000000000..de591ad797 --- /dev/null +++ b/packages/react-native-sdk/src/utils/hooks/useOrientation.ts @@ -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( + isPortrait() ? 'portrait' : 'landscape', + ); + + useEffect(() => { + const updateOrientation = () => { + setOrientation(isPortrait() ? 'portrait' : 'landscape'); + }; + + Dimensions.addEventListener('change', updateOrientation); + + // return () => { + // // @ts-ignore + // Dimensions.removeEventListener('change', updateOrientation); + // }; + }, []); + + return orientation; +}; diff --git a/sample-apps/react-native/dogfood/android/app/src/main/AndroidManifest.xml b/sample-apps/react-native/dogfood/android/app/src/main/AndroidManifest.xml index cd8a406e18..8721ddbdb9 100644 --- a/sample-apps/react-native/dogfood/android/app/src/main/AndroidManifest.xml +++ b/sample-apps/react-native/dogfood/android/app/src/main/AndroidManifest.xml @@ -10,7 +10,7 @@ - + @@ -32,7 +32,6 @@ android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="false" android:theme="@style/AppTheme"> @@ -70,4 +69,4 @@ - + \ No newline at end of file diff --git a/sample-apps/react-native/dogfood/src/components/CallControlsComponent.tsx b/sample-apps/react-native/dogfood/src/components/CallControlsComponent.tsx index d06af30b03..d0779e9a7a 100644 --- a/sample-apps/react-native/dogfood/src/components/CallControlsComponent.tsx +++ b/sample-apps/react-native/dogfood/src/components/CallControlsComponent.tsx @@ -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; @@ -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 ( - + { + 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( + isPortrait() ? 'portrait' : 'landscape', + ); + + useEffect(() => { + const updateOrientation = () => { + setOrientation(isPortrait() ? 'portrait' : 'landscape'); + }; + + Dimensions.addEventListener('change', updateOrientation); + + // return () => { + // // @ts-ignore + // Dimensions.removeEventListener('change', updateOrientation); + // }; + }, []); + + return orientation; +};