diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index a22317f1..d330f8ca 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -21,7 +21,7 @@ android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode" android:label="@string/app_name" android:launchMode="singleTask" - android:screenOrientation="portrait" + android:screenOrientation="unspecified" android:windowSoftInputMode="adjustResize"> diff --git a/src/components/BottomSheet.tsx b/src/components/BottomSheet.tsx index 8bf2ffd4..31de1a3e 100644 --- a/src/components/BottomSheet.tsx +++ b/src/components/BottomSheet.tsx @@ -1,5 +1,5 @@ -import React, {useState, useCallback, useRef} from 'react'; -import {View, StyleSheet, TouchableHighlight, TouchableOpacity} from 'react-native'; +import React, {useState, useCallback, useRef, useEffect} from 'react'; +import {View, StyleSheet, TouchableHighlight, TouchableOpacity, useWindowDimensions} from 'react-native'; import Animated from 'react-native-reanimated'; import {useSafeArea} from 'react-native-safe-area-context'; import BottomSheetRaw from 'reanimated-bottom-sheet'; @@ -19,7 +19,9 @@ interface ContentProps { const SheetContentsContainer = ({children, isExpanded, toggleExpanded}: ContentProps) => { const content = ( - {children} + + {children} + ); @@ -36,9 +38,6 @@ export interface BottomSheetProps { extraContent?: boolean; } -const SNAP_POINTS = ['100%', '20%']; -const SNAP_POINTS_LARGE = ['100%', '30%']; - export const BottomSheet = ({children, collapsedContent, extraContent}: BottomSheetProps) => { const bottomSheetPosition = useRef(new Animated.Value(1)); const bottomSheetRef: React.Ref = useRef(null); @@ -58,6 +57,13 @@ export const BottomSheet = ({children, collapsedContent, extraContent}: BottomSh const onOpenEnd = useCallback(() => setIsExpanded(true), []); const onCloseEnd = useCallback(() => setIsExpanded(false), []); + const {width, height} = useWindowDimensions(); + const snapPoints = [height, Math.max(width, height) * (extraContent ? 0.3 : 0.2)]; + + useEffect(() => { + bottomSheetRef.current?.snapTo(isExpanded ? 0 : 1); + }, [width, isExpanded]); + const expandedContentWrapper = ( {children} @@ -89,8 +95,6 @@ export const BottomSheet = ({children, collapsedContent, extraContent}: BottomSh ); }, [collapsedContentWrapper, expandedContentWrapper, isExpanded, toggleExpanded]); - const snapPoints = extraContent ? SNAP_POINTS_LARGE : SNAP_POINTS; - return ( <> diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 14f8bab8..6ccf4765 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -19,7 +19,7 @@ export const Header = ({isOverlay}: HeaderProps) => { }, [navigation]); return ( - + diff --git a/src/screens/home/HomeScreen.tsx b/src/screens/home/HomeScreen.tsx index c5d107dd..417532b0 100644 --- a/src/screens/home/HomeScreen.tsx +++ b/src/screens/home/HomeScreen.tsx @@ -5,6 +5,7 @@ import {useExposureStatus, useSystemStatus, SystemStatus} from 'services/Exposur import {checkNotifications, requestNotifications} from 'react-native-permissions'; import {useNetInfo} from '@react-native-community/netinfo'; import {useNavigation, DrawerActions} from '@react-navigation/native'; +import {useMaxContentWidth} from 'shared/useMaxContentWidth'; import {ExposureNotificationsDisabledView} from './views/ExposureNotificationsDisabledView'; import {BluetoothDisabledView} from './views/BluetoothDisabledView'; @@ -109,9 +110,11 @@ export const HomeScreen = () => { [showNotificationWarning, systemStatus, turnNotificationsOn], ); + const maxWidth = useMaxContentWidth(); + return ( - - + + { status={systemStatus} notificationWarning={showNotificationWarning} turnNotificationsOn={turnNotificationsOn} + maxWidth={maxWidth} /> diff --git a/src/screens/home/components/BaseHomeView.tsx b/src/screens/home/components/BaseHomeView.tsx index 69c34c61..1368c9fd 100644 --- a/src/screens/home/components/BaseHomeView.tsx +++ b/src/screens/home/components/BaseHomeView.tsx @@ -1,10 +1,9 @@ import React from 'react'; -import {StyleSheet, Dimensions, ScrollView} from 'react-native'; +import {StyleSheet, ScrollView} from 'react-native'; import {SafeAreaView} from 'react-native-safe-area-context'; import LottieView from 'lottie-react-native'; import {Box, Header} from 'components'; - -const {width: viewportWidth, height: viewportHeight} = Dimensions.get('window'); +import {useOrientation} from 'shared/useOrientation'; interface BaseHomeViewProps { children?: React.ReactNode; @@ -12,25 +11,35 @@ interface BaseHomeViewProps { } export const BaseHomeView = ({children, animationSource}: BaseHomeViewProps) => { + const { + orientation, + scaledSize: {width: viewportWidth, height: viewportHeight}, + } = useOrientation(); + return (
- {animationSource && ( - + {animationSource && orientation === 'portrait' && ( + + + )} {children} diff --git a/src/screens/home/views/OverlayView.tsx b/src/screens/home/views/OverlayView.tsx index fda0c11a..3dabb3a2 100644 --- a/src/screens/home/views/OverlayView.tsx +++ b/src/screens/home/views/OverlayView.tsx @@ -1,7 +1,7 @@ import React, {useCallback} from 'react'; import {Linking} from 'react-native'; import {useNavigation} from '@react-navigation/native'; -import {Box, InfoBlock} from 'components'; +import {Box, InfoBlock, BoxProps} from 'components'; import {useI18n, I18n} from '@shopify/react-i18n'; import {SystemStatus} from 'services/ExposureNotificationService'; @@ -56,18 +56,18 @@ const NotificationStatusOff = ({action, i18n}: {action: () => void; i18n: I18n}) ); }; -interface Props { +interface Props extends Pick { status: SystemStatus; notificationWarning: boolean; turnNotificationsOn: () => void; } -export const OverlayView = ({status, notificationWarning, turnNotificationsOn}: Props) => { +export const OverlayView = ({status, notificationWarning, turnNotificationsOn, maxWidth}: Props) => { const [i18n] = useI18n(); const navigation = useNavigation(); return ( - + diff --git a/src/screens/onboarding/Onboarding.tsx b/src/screens/onboarding/Onboarding.tsx index 36c9a568..7d5d3177 100644 --- a/src/screens/onboarding/Onboarding.tsx +++ b/src/screens/onboarding/Onboarding.tsx @@ -1,18 +1,17 @@ import React, {useCallback, useRef, useState} from 'react'; import {useNavigation} from '@react-navigation/native'; import {Box, Button, ProgressCircles, Header, LanguageToggle} from 'components'; -import {Dimensions, StyleSheet} from 'react-native'; +import {StyleSheet, useWindowDimensions} from 'react-native'; import {SafeAreaView, useSafeArea} from 'react-native-safe-area-context'; import Carousel, {CarouselStatic} from 'react-native-snap-carousel'; import {useStorage} from 'services/StorageService'; import {useI18n} from '@shopify/react-i18n'; import OnboardingBg from 'assets/onboarding-bg.svg'; +import {useMaxContentWidth} from 'shared/useMaxContentWidth'; import {Permissions} from './views/Permissions'; import {Start} from './views/Start'; -const {width: viewportWidth} = Dimensions.get('window'); - type ViewKey = 'start' | 'permissions'; const contentData: ViewKey[] = ['start', 'permissions']; @@ -23,6 +22,7 @@ const viewComponents = { export const OnboardingScreen = () => { const [i18n] = useI18n(); + const {width: viewportWidth} = useWindowDimensions(); const insets = useSafeArea(); const [currentIndex, setCurrentIndex] = useState(0); const carouselRef = useRef(null); @@ -37,10 +37,19 @@ export const OnboardingScreen = () => { }); }, [navigation, setOnboarded]); - const renderItem = useCallback(({item}: {item: ViewKey}) => { - const ItemComponent = viewComponents[item]; - return ; - }, []); + const maxWidth = useMaxContentWidth(); + + const renderItem = useCallback( + ({item}: {item: ViewKey}) => { + const ItemComponent = viewComponents[item]; + return ( + + + + ); + }, + [maxWidth], + ); const nextItem = useCallback(() => { if (carouselRef.current) { diff --git a/src/screens/tutorial/Tutorial.tsx b/src/screens/tutorial/Tutorial.tsx index bea427e7..90ec303b 100644 --- a/src/screens/tutorial/Tutorial.tsx +++ b/src/screens/tutorial/Tutorial.tsx @@ -1,5 +1,5 @@ import React, {useState, useCallback, useRef} from 'react'; -import {Dimensions, StyleSheet} from 'react-native'; +import {StyleSheet, useWindowDimensions} from 'react-native'; import Carousel, {CarouselStatic} from 'react-native-snap-carousel'; import {useNavigation} from '@react-navigation/native'; import {Box, Button, ProgressCircles, Toolbar} from 'components'; @@ -8,10 +8,9 @@ import {useI18n} from '@shopify/react-i18n'; import {TutorialContent, tutorialData, TutorialKey} from './TutorialContent'; -const {width: viewportWidth} = Dimensions.get('window'); - export const TutorialScreen = () => { const navigation = useNavigation(); + const {width: viewportWidth} = useWindowDimensions(); const carouselRef = useRef(null); const [currentStep, setCurrentStep] = useState(0); const [i18n] = useI18n(); diff --git a/src/screens/tutorial/TutorialContent.tsx b/src/screens/tutorial/TutorialContent.tsx index 0957fa62..d485d8e8 100644 --- a/src/screens/tutorial/TutorialContent.tsx +++ b/src/screens/tutorial/TutorialContent.tsx @@ -1,11 +1,9 @@ import React, {useRef, useEffect} from 'react'; -import {Dimensions, StyleSheet, ScrollView} from 'react-native'; +import {StyleSheet, ScrollView, useWindowDimensions} from 'react-native'; import {Box, Text} from 'components'; import {useI18n} from '@shopify/react-i18n'; import LottieView from 'lottie-react-native'; -const {width: viewportWidth, height: viewportHeight} = Dimensions.get('window'); - export type TutorialKey = 'step-1' | 'step-2' | 'step-3'; export const tutorialData: TutorialKey[] = ['step-1', 'step-2', 'step-3']; @@ -18,6 +16,7 @@ const animationData = { export const TutorialContent = ({item, isActiveSlide}: {item: TutorialKey; isActiveSlide: boolean}) => { const [i18n] = useI18n(); + const {width: viewportWidth, height: viewportHeight} = useWindowDimensions(); const animationRef: React.Ref = useRef(null); useEffect(() => { if (isActiveSlide) { diff --git a/src/shared/theme.ts b/src/shared/theme.ts index e6774772..47063491 100644 --- a/src/shared/theme.ts +++ b/src/shared/theme.ts @@ -169,6 +169,7 @@ const theme = { disabled: {}, }, }, + maxContentWidth: 500, }; export type Theme = typeof theme; diff --git a/src/shared/useMaxContentWidth.ts b/src/shared/useMaxContentWidth.ts new file mode 100644 index 00000000..39e95e65 --- /dev/null +++ b/src/shared/useMaxContentWidth.ts @@ -0,0 +1,10 @@ +import {useTheme} from '@shopify/restyle'; + +import {useOrientation} from './useOrientation'; +import {Theme} from './theme'; + +export const useMaxContentWidth = (): number | undefined => { + const {maxContentWidth} = useTheme(); + const {orientation} = useOrientation(); + return orientation === 'landscape' ? maxContentWidth : undefined; +}; diff --git a/src/shared/useOrientation.ts b/src/shared/useOrientation.ts new file mode 100644 index 00000000..33dc749c --- /dev/null +++ b/src/shared/useOrientation.ts @@ -0,0 +1,13 @@ +import {useWindowDimensions, ScaledSize} from 'react-native'; + +type Orientation = 'portrait' | 'landscape'; + +interface OrientationReturnValue { + orientation: Orientation; + scaledSize: ScaledSize; +} + +export const useOrientation = (): OrientationReturnValue => { + const scaledSize = useWindowDimensions(); + return {orientation: scaledSize.width > scaledSize.height ? 'landscape' : 'portrait', scaledSize}; +};