From 537cd202da087d667e5078f3af48d976e7eb519f Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Fri, 14 Mar 2025 15:11:36 +0100 Subject: [PATCH 01/19] fix: extract `stickToBottom` style to global stylesheet --- src/components/FixedFooter.tsx | 4 ++-- src/components/Form/FormWrapper.tsx | 5 ++--- src/components/NavigationBar/index.android.tsx | 6 +++--- src/styles/index.ts | 7 +++++++ 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/components/FixedFooter.tsx b/src/components/FixedFooter.tsx index fe9179fa88b4d..8f167059a2518 100644 --- a/src/components/FixedFooter.tsx +++ b/src/components/FixedFooter.tsx @@ -29,7 +29,7 @@ function FixedFooter({style, children, addBottomSafeAreaPadding = false, shouldS // If the footer should stick to the bottom, we use absolute positioning instead of flex. // In this case, we need to use style.bottom instead of style.paddingBottom. if (shouldStickToBottom) { - return {position: 'absolute', left: 0, right: 0, bottom: addBottomSafeAreaPadding ? totalPaddingBottom : styles.pb5.paddingBottom}; + return [styles.stickToBottom, {bottom: addBottomSafeAreaPadding ? totalPaddingBottom : styles.pb5.paddingBottom}]; } // If the footer should not stick to the bottom, we use flex and add the safe area padding in styles.paddingBottom. @@ -39,7 +39,7 @@ function FixedFooter({style, children, addBottomSafeAreaPadding = false, shouldS // Otherwise, we just use the default bottom padding. return styles.pb5; - }, [addBottomSafeAreaPadding, paddingBottom, shouldStickToBottom, styles.pb5]); + }, [addBottomSafeAreaPadding, paddingBottom, shouldStickToBottom, styles.pb5, styles.stickToBottom]); if (!children) { return null; diff --git a/src/components/Form/FormWrapper.tsx b/src/components/Form/FormWrapper.tsx index dfafa05b53b29..f5388712d41a3 100644 --- a/src/components/Form/FormWrapper.tsx +++ b/src/components/Form/FormWrapper.tsx @@ -130,10 +130,8 @@ function FormWrapper({ submitButtonStyles, shouldSubmitButtonStickToBottom ? [ + styles.stickToBottom, { - position: 'absolute', - left: 0, - right: 0, bottom: styles.pb5.paddingBottom + paddingBottom, }, style, @@ -169,6 +167,7 @@ function FormWrapper({ styles.mh0, styles.mt5, styles.pb5.paddingBottom, + styles.stickToBottom, submitButtonStyles, submitButtonText, submitFlexEnabled, diff --git a/src/components/NavigationBar/index.android.tsx b/src/components/NavigationBar/index.android.tsx index a7e8fe9ccd073..d34b28593d6fa 100644 --- a/src/components/NavigationBar/index.android.tsx +++ b/src/components/NavigationBar/index.android.tsx @@ -2,12 +2,12 @@ import {useMemo} from 'react'; import {View} from 'react-native'; import useSafeAreaPaddings from '@hooks/useSafeAreaPaddings'; import useStyleUtils from '@hooks/useStyleUtils'; -import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; /** NavigationBar renders a semi-translucent background behind the three-button navigation bar on Android. */ function NavigationBar() { - const theme = useTheme(); + const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {insets} = useSafeAreaPaddings(); @@ -15,7 +15,7 @@ function NavigationBar() { const isSoftKeyNavigation = navigationBarType === CONST.NAVIGATION_BAR_TYPE.SOFT_KEYS; - return isSoftKeyNavigation ? : null; + return isSoftKeyNavigation ? : null; } NavigationBar.displayName = 'NavigationBar'; diff --git a/src/styles/index.ts b/src/styles/index.ts index 6650743c56595..b977ad3e1cae8 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -5498,6 +5498,13 @@ const styles = (theme: ThemeColors) => expenseWidgetRadius: { borderRadius: variables.componentBorderRadiusNormal, }, + + stickToBottom: { + position: 'absolute', + bottom: 0, + left: 0, + right: 0, + }, } satisfies Styles); type ThemeStyles = ReturnType; From 300abcf1ce72e9e3b0fa3667545513432572b245 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Fri, 14 Mar 2025 15:13:43 +0100 Subject: [PATCH 02/19] fix: make offline indicator translucent on edge-to-edge mode --- src/components/OfflineIndicator.tsx | 11 +++++++---- src/components/ScreenWrapper.tsx | 6 +++++- src/styles/index.ts | 4 ++++ 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/components/OfflineIndicator.tsx b/src/components/OfflineIndicator.tsx index 5b6a28156dc2b..bee9b3e628130 100644 --- a/src/components/OfflineIndicator.tsx +++ b/src/components/OfflineIndicator.tsx @@ -21,18 +21,21 @@ type OfflineIndicatorProps = { /** Whether to add bottom safe area padding to the view. */ addBottomSafeAreaPadding?: boolean; + + /** Whether to make the indicator translucent. */ + isTranslucent?: boolean; }; -function OfflineIndicator({style, containerStyles, addBottomSafeAreaPadding = false}: OfflineIndicatorProps) { +function OfflineIndicator({style, containerStyles: containerStylesProp, addBottomSafeAreaPadding = false, isTranslucent = false}: OfflineIndicatorProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); const {isOffline} = useNetwork(); const {shouldUseNarrowLayout} = useResponsiveLayout(); - const computedStyles = useBottomSafeSafeAreaPaddingStyle({ + const containerStyles = useBottomSafeSafeAreaPaddingStyle({ addBottomSafeAreaPadding, - style: containerStyles ?? (shouldUseNarrowLayout ? styles.offlineIndicatorMobile : styles.offlineIndicator), + style: containerStylesProp ?? (shouldUseNarrowLayout ? styles.offlineIndicatorMobile : styles.offlineIndicator), }); if (!isOffline) { @@ -40,7 +43,7 @@ function OfflineIndicator({style, containerStyles, addBottomSafeAreaPadding = fa } return ( - + , ) { @@ -371,7 +375,7 @@ function ScreenWrapper( <> {/* Since import state is tightly coupled to the offline state, it is safe to display it when showing offline indicator */} diff --git a/src/styles/index.ts b/src/styles/index.ts index b977ad3e1cae8..a6e49de8ede98 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -5499,6 +5499,10 @@ const styles = (theme: ThemeColors) => borderRadius: variables.componentBorderRadiusNormal, }, + navigationBarBG: { + backgroundColor: theme.navigationBarBackgroundColor, + }, + stickToBottom: { position: 'absolute', bottom: 0, From 0e81308d857f16a88d9cb714c55aeb406ee3d81b Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Fri, 14 Mar 2025 15:45:24 +0100 Subject: [PATCH 03/19] fix: make OfflineIndicator bottom sticky --- src/components/ScreenWrapper.tsx | 14 +++++++++++++- src/styles/utils/index.ts | 10 ++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/components/ScreenWrapper.tsx b/src/components/ScreenWrapper.tsx index 4f0073efa9000..be951b5c548da 100644 --- a/src/components/ScreenWrapper.tsx +++ b/src/components/ScreenWrapper.tsx @@ -12,6 +12,7 @@ import useEnvironment from '@hooks/useEnvironment'; import useInitialDimensions from '@hooks/useInitialWindowDimensions'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useSafeAreaPaddings from '@hooks/useSafeAreaPaddings'; +import useStyleUtils from '@hooks/useStyleUtils'; import useTackInputFocus from '@hooks/useTackInputFocus'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; @@ -195,6 +196,7 @@ function ScreenWrapper( const {isSmallScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout(); const {initialHeight} = useInitialDimensions(); const styles = useThemeStyles(); + const StyleUtils = useStyleUtils(); const {isDevelopment} = useEnvironment(); const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false); const maxHeight = shouldEnableMaxHeight ? windowHeight : undefined; @@ -319,14 +321,23 @@ function ScreenWrapper( paddingBottom: includeSafeAreaPaddingBottom ? paddingBottom : undefined, }; }, [ignoreInsetsConsumption, includeSafeAreaPaddingBottom, paddingBottom, unmodifiedPaddings.bottom]); + const bottomContentStyle = useMemo( () => (enableEdgeToEdgeBottomSafeAreaPadding ? edgeToEdgeBottomContentStyle : legacyBottomContentStyle), [enableEdgeToEdgeBottomSafeAreaPadding, edgeToEdgeBottomContentStyle, legacyBottomContentStyle], ); - const addMobileOfflineIndicatorBottomSafeAreaPadding = enableEdgeToEdgeBottomSafeAreaPadding ? !bottomContent : !includeSafeAreaPaddingBottom; + const shouldUseStickyMobileOfflineIndicator = enableEdgeToEdgeBottomSafeAreaPadding && !bottomContent; + const addMobileOfflineIndicatorBottomSafeAreaPadding = enableEdgeToEdgeBottomSafeAreaPadding ? !shouldUseStickyMobileOfflineIndicator : !includeSafeAreaPaddingBottom; const addWidescreenOfflineIndicatorBottomSafeAreaPadding = enableEdgeToEdgeBottomSafeAreaPadding ? !bottomContent : true; + const navigationBarType = useMemo(() => StyleUtils.getNavigationBarType(insets), [StyleUtils, insets]); + const isSoftKeyNavigation = navigationBarType === CONST.NAVIGATION_BAR_TYPE.SOFT_KEYS; + const mobileOfflineIndicatorContainerStyle = useMemo( + () => (shouldUseStickyMobileOfflineIndicator ? StyleUtils.getEdgeToEdgeMobileOfflineIndicatorStyle(isSoftKeyNavigation, paddingBottom) : styles.offlineIndicatorMobile), + [StyleUtils, shouldUseStickyMobileOfflineIndicator, paddingBottom, styles.offlineIndicatorMobile, isSoftKeyNavigation], + ); + const isAvoidingViewportScroll = useTackInputFocus(isFocused && shouldEnableMaxHeight && shouldAvoidScrollOnVirtualViewport && isMobileWebKit()); const contextValue = useMemo( () => ({didScreenTransitionEnd, isSafeAreaTopPaddingApplied, isSafeAreaBottomPaddingApplied: includeSafeAreaPaddingBottom}), @@ -374,6 +385,7 @@ function ScreenWrapper( {isSmallScreenWidth && shouldShowOfflineIndicator && ( <> ({ isTaskCompleted ? [styles.textSupporting, styles.textLineThrough] : [], {marginTop: (iconHeight - variables.fontSizeNormalHeight) / 2}, ], + + getEdgeToEdgeMobileOfflineIndicatorStyle: (isSoftKeyNavigation: boolean, paddingBottom: number): StyleProp => ({ + position: 'absolute', + bottom: isSoftKeyNavigation ? paddingBottom : 0, + left: 0, + right: 0, + paddingHorizontal: 20, + paddingTop: 5, + paddingBottom: isSoftKeyNavigation ? 5 : paddingBottom + 5, + }), }); type StyleUtilsType = ReturnType; From e88ee6fc4b26c8083053a46a2ef968054e74e87d Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Fri, 14 Mar 2025 15:45:41 +0100 Subject: [PATCH 04/19] fix: use bottom safe area padding for navigation bar height instead of inset --- src/components/NavigationBar/index.android.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/NavigationBar/index.android.tsx b/src/components/NavigationBar/index.android.tsx index d34b28593d6fa..3f066b806a384 100644 --- a/src/components/NavigationBar/index.android.tsx +++ b/src/components/NavigationBar/index.android.tsx @@ -9,13 +9,12 @@ import CONST from '@src/CONST'; function NavigationBar() { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); - const {insets} = useSafeAreaPaddings(); + const {insets, paddingBottom} = useSafeAreaPaddings(); const navigationBarType = useMemo(() => StyleUtils.getNavigationBarType(insets), [StyleUtils, insets]); - const isSoftKeyNavigation = navigationBarType === CONST.NAVIGATION_BAR_TYPE.SOFT_KEYS; - return isSoftKeyNavigation ? : null; + return isSoftKeyNavigation ? : null; } NavigationBar.displayName = 'NavigationBar'; From 9eb3b3e8c8839fb75f3d86e2afb28c657302fb72 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Fri, 14 Mar 2025 16:13:15 +0100 Subject: [PATCH 05/19] feat: add more customization to `useBottomSafeSafeAreaPaddingStyle` --- .../useBottomSafeSafeAreaPaddingStyle.ts | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/hooks/useBottomSafeSafeAreaPaddingStyle.ts b/src/hooks/useBottomSafeSafeAreaPaddingStyle.ts index de56f88d8ec0c..00004b8393847 100644 --- a/src/hooks/useBottomSafeSafeAreaPaddingStyle.ts +++ b/src/hooks/useBottomSafeSafeAreaPaddingStyle.ts @@ -1,6 +1,7 @@ import {useMemo} from 'react'; import {StyleSheet} from 'react-native'; import type {StyleProp, ViewStyle} from 'react-native'; +import useNetwork from './useNetwork'; import useSafeAreaPaddings from './useSafeAreaPaddings'; /** The parameters for the useBottomSafeSafeAreaPaddingStyle hook. */ @@ -8,9 +9,14 @@ type UseBottomSafeAreaPaddingStyleParams = { /** Whether to add bottom safe area padding to the content. */ addBottomSafeAreaPadding?: boolean; + /** Whether to add bottom safe area padding to the content. */ + addOfflineIndicatorBottomSafeAreaPadding?: boolean; + /** The style to adapt and add bottom safe area padding to. */ style?: StyleProp; + styleProperty?: 'paddingBottom' | 'bottom'; + /** The additional padding to add to the bottom of the content. */ additionalPaddingBottom?: number; }; @@ -23,17 +29,22 @@ type UseBottomSafeAreaPaddingStyleParams = { */ function useBottomSafeSafeAreaPaddingStyle(params?: UseBottomSafeAreaPaddingStyleParams) { const {paddingBottom: safeAreaPaddingBottom} = useSafeAreaPaddings(true); + const {isOffline} = useNetwork(); - const {addBottomSafeAreaPadding, style, additionalPaddingBottom} = params ?? {}; + const {addBottomSafeAreaPadding, addOfflineIndicatorBottomSafeAreaPadding, style, styleProperty = 'paddingBottom', additionalPaddingBottom = 0} = params ?? {}; return useMemo>(() => { - let totalPaddingBottom: number | string = additionalPaddingBottom ?? 0; + let totalPaddingBottom: number | string = additionalPaddingBottom; // Add the safe area padding to the total padding if the flag is enabled if (addBottomSafeAreaPadding) { totalPaddingBottom += safeAreaPaddingBottom; } + if (addOfflineIndicatorBottomSafeAreaPadding && isOffline) { + totalPaddingBottom += safeAreaPaddingBottom; + } + // If there is no bottom safe area or additional padding, return the style as is if (totalPaddingBottom === 0) { return style; @@ -52,12 +63,13 @@ function useBottomSafeSafeAreaPaddingStyle(params?: UseBottomSafeAreaPaddingStyl return style; } - return [style, {paddingBottom: totalPaddingBottom}]; + // The user of this hook can decide which style property to use for applying the padding. + return [style, {[styleProperty]: totalPaddingBottom}]; } // If no style is provided, return the padding as an object return {paddingBottom: totalPaddingBottom}; - }, [addBottomSafeAreaPadding, style, additionalPaddingBottom, safeAreaPaddingBottom]); + }, [additionalPaddingBottom, addBottomSafeAreaPadding, addOfflineIndicatorBottomSafeAreaPadding, isOffline, style, safeAreaPaddingBottom, styleProperty]); } export default useBottomSafeSafeAreaPaddingStyle; From 819e2027a1ba6c059398a3ce9785a71135f5ef95 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Fri, 14 Mar 2025 16:13:33 +0100 Subject: [PATCH 06/19] refactor: use `useBottomSafeSafeAreaPaddingStyle` instead of custom logic --- src/components/FixedFooter.tsx | 36 +++++++++++++++------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/components/FixedFooter.tsx b/src/components/FixedFooter.tsx index 8f167059a2518..1b31c3ee25494 100644 --- a/src/components/FixedFooter.tsx +++ b/src/components/FixedFooter.tsx @@ -2,7 +2,7 @@ import type {ReactNode} from 'react'; import React, {useMemo} from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; import {View} from 'react-native'; -import useSafeAreaPaddings from '@hooks/useSafeAreaPaddings'; +import useBottomSafeSafeAreaPaddingStyle from '@hooks/useBottomSafeSafeAreaPaddingStyle'; import useThemeStyles from '@hooks/useThemeStyles'; type FixedFooterProps = { @@ -15,31 +15,27 @@ type FixedFooterProps = { /** Whether to add bottom safe area padding to the content. */ addBottomSafeAreaPadding?: boolean; + /** Whether to add bottom safe area padding to the content. */ + addOfflineIndicatorBottomSafeAreaPadding?: boolean; + /** Whether to stick the footer to the bottom of the screen. */ shouldStickToBottom?: boolean; }; -function FixedFooter({style, children, addBottomSafeAreaPadding = false, shouldStickToBottom = false}: FixedFooterProps) { +function FixedFooter({style, children, addBottomSafeAreaPadding = false, addOfflineIndicatorBottomSafeAreaPadding = false, shouldStickToBottom = false}: FixedFooterProps) { const styles = useThemeStyles(); - const {paddingBottom} = useSafeAreaPaddings(true); - - const footerStyle = useMemo>(() => { - const totalPaddingBottom = styles.pb5.paddingBottom + paddingBottom; - - // If the footer should stick to the bottom, we use absolute positioning instead of flex. - // In this case, we need to use style.bottom instead of style.paddingBottom. - if (shouldStickToBottom) { - return [styles.stickToBottom, {bottom: addBottomSafeAreaPadding ? totalPaddingBottom : styles.pb5.paddingBottom}]; - } - - // If the footer should not stick to the bottom, we use flex and add the safe area padding in styles.paddingBottom. - if (addBottomSafeAreaPadding) { - return {paddingBottom: totalPaddingBottom}; - } - // Otherwise, we just use the default bottom padding. - return styles.pb5; - }, [addBottomSafeAreaPadding, paddingBottom, shouldStickToBottom, styles.pb5, styles.stickToBottom]); + const bottomSafeAreaPaddingStyle = useBottomSafeSafeAreaPaddingStyle({ + addBottomSafeAreaPadding, + addOfflineIndicatorBottomSafeAreaPadding, + additionalPaddingBottom: styles.pb5.paddingBottom, + styleProperty: shouldStickToBottom ? 'bottom' : 'paddingBottom', + }); + + const footerStyle = useMemo>( + () => [shouldStickToBottom && styles.stickToBottom, bottomSafeAreaPaddingStyle], + [bottomSafeAreaPaddingStyle, shouldStickToBottom, styles.stickToBottom], + ); if (!children) { return null; From 4bdb45801517751830d3b1034678a2095d070b87 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Fri, 14 Mar 2025 16:14:05 +0100 Subject: [PATCH 07/19] feat: add additional prop for applying extra offline indicator padding --- src/components/BlockingViews/BlockingView.tsx | 6 +++++- .../BlockingViews/FullPageNotFoundView.tsx | 5 +++++ .../FullPageOfflineBlockingView.tsx | 6 +++++- src/components/EmptyStateComponent/index.tsx | 2 ++ src/components/EmptyStateComponent/types.ts | 3 +++ src/components/Form/FormProvider.tsx | 3 +++ src/components/Form/FormWrapper.tsx | 6 ++++++ src/components/OfflineIndicator.tsx | 12 +++++++++++- src/components/ScrollView.tsx | 18 ++++++++++++++++-- src/components/SectionList/BaseSectionList.tsx | 4 ++-- src/components/SectionList/types.ts | 3 +++ .../SelectionList/BaseSelectionList.tsx | 14 +++++--------- src/components/SelectionList/types.ts | 3 +++ 13 files changed, 69 insertions(+), 16 deletions(-) diff --git a/src/components/BlockingViews/BlockingView.tsx b/src/components/BlockingViews/BlockingView.tsx index 6fc43cd9b236e..f1404013c6083 100644 --- a/src/components/BlockingViews/BlockingView.tsx +++ b/src/components/BlockingViews/BlockingView.tsx @@ -51,6 +51,9 @@ type BaseBlockingViewProps = { /** Whether to add bottom safe area padding to the view. */ addBottomSafeAreaPadding?: boolean; + + /** Whether to add bottom safe area padding to the content. */ + addOfflineIndicatorBottomSafeAreaPadding?: boolean; }; type BlockingViewIconProps = { @@ -100,6 +103,7 @@ function BlockingView({ contentFitImage, containerStyle: containerStyleProp, addBottomSafeAreaPadding = false, + addOfflineIndicatorBottomSafeAreaPadding = false, }: BlockingViewProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -137,7 +141,7 @@ function BlockingView({ ); }, [styles, subtitleText, shouldEmbedLinkWithSubtitle, CustomSubtitle]); - const containerStyle = useBottomSafeSafeAreaPaddingStyle({addBottomSafeAreaPadding, style: containerStyleProp}); + const containerStyle = useBottomSafeSafeAreaPaddingStyle({addBottomSafeAreaPadding, addOfflineIndicatorBottomSafeAreaPadding, style: containerStyleProp}); return ( diff --git a/src/components/BlockingViews/FullPageNotFoundView.tsx b/src/components/BlockingViews/FullPageNotFoundView.tsx index b9afe868878cc..6c6a0b0a09c26 100644 --- a/src/components/BlockingViews/FullPageNotFoundView.tsx +++ b/src/components/BlockingViews/FullPageNotFoundView.tsx @@ -53,6 +53,9 @@ type FullPageNotFoundViewProps = { /** Whether to add bottom safe area padding to the view. */ addBottomSafeAreaPadding?: boolean; + + /** Whether to add bottom safe area padding to the content. */ + addOfflineIndicatorBottomSafeAreaPadding?: boolean; }; // eslint-disable-next-line rulesdir/no-negated-variables @@ -71,6 +74,7 @@ function FullPageNotFoundView({ subtitleStyle, shouldDisplaySearchRouter, addBottomSafeAreaPadding = true, + addOfflineIndicatorBottomSafeAreaPadding = false, }: FullPageNotFoundViewProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -98,6 +102,7 @@ function FullPageNotFoundView({ onLinkPress={onLinkPress} subtitleStyle={subtitleStyle} addBottomSafeAreaPadding={addBottomSafeAreaPadding} + addOfflineIndicatorBottomSafeAreaPadding={addOfflineIndicatorBottomSafeAreaPadding} /> diff --git a/src/components/BlockingViews/FullPageOfflineBlockingView.tsx b/src/components/BlockingViews/FullPageOfflineBlockingView.tsx index dc5c210b3178a..5bfb08a88adc3 100644 --- a/src/components/BlockingViews/FullPageOfflineBlockingView.tsx +++ b/src/components/BlockingViews/FullPageOfflineBlockingView.tsx @@ -9,9 +9,12 @@ import BlockingView from './BlockingView'; type FullPageOfflineBlockingViewProps = ChildrenProps & { /** Whether to add bottom safe area padding to the view. */ addBottomSafeAreaPadding?: boolean; + + /** Whether to add bottom safe area padding to the content. */ + addOfflineIndicatorBottomSafeAreaPadding?: boolean; }; -function FullPageOfflineBlockingView({children, addBottomSafeAreaPadding = true}: FullPageOfflineBlockingViewProps) { +function FullPageOfflineBlockingView({children, addBottomSafeAreaPadding = true, addOfflineIndicatorBottomSafeAreaPadding = false}: FullPageOfflineBlockingViewProps) { const {translate} = useLocalize(); const {isOffline} = useNetwork(); @@ -25,6 +28,7 @@ function FullPageOfflineBlockingView({children, addBottomSafeAreaPadding = true} title={translate('common.youAppearToBeOffline')} subtitle={translate('common.thisFeatureRequiresInternet')} addBottomSafeAreaPadding={addBottomSafeAreaPadding} + addOfflineIndicatorBottomSafeAreaPadding={addOfflineIndicatorBottomSafeAreaPadding} /> ); } diff --git a/src/components/EmptyStateComponent/index.tsx b/src/components/EmptyStateComponent/index.tsx index d3a715ad87487..93d0926b8043b 100644 --- a/src/components/EmptyStateComponent/index.tsx +++ b/src/components/EmptyStateComponent/index.tsx @@ -30,6 +30,7 @@ function EmptyStateComponent({ showsVerticalScrollIndicator, minModalHeight = 400, addBottomSafeAreaPadding = false, + addOfflineIndicatorBottomSafeAreaPadding = false, }: EmptyStateComponentProps) { const styles = useThemeStyles(); const [videoAspectRatio, setVideoAspectRatio] = useState(VIDEO_ASPECT_RATIO); @@ -90,6 +91,7 @@ function EmptyStateComponent({ contentContainerStyle={[{minHeight: minModalHeight}, styles.flexGrow1, styles.flexShrink0, containerStyles]} style={styles.flex1} addBottomSafeAreaPadding={addBottomSafeAreaPadding} + addOfflineIndicatorBottomSafeAreaPadding={addOfflineIndicatorBottomSafeAreaPadding} > = { /** Whether to add bottom safe area padding to the view. */ addBottomSafeAreaPadding?: boolean; + + /** Whether to add bottom safe area padding to the content. */ + addOfflineIndicatorBottomSafeAreaPadding?: boolean; }; type MediaType = SharedProps & { diff --git a/src/components/Form/FormProvider.tsx b/src/components/Form/FormProvider.tsx index 6c8d3f989e166..5145bd9754e68 100644 --- a/src/components/Form/FormProvider.tsx +++ b/src/components/Form/FormProvider.tsx @@ -79,6 +79,9 @@ type FormProviderProps = FormProps {scrollViewContent()} @@ -230,6 +235,7 @@ function FormWrapper({ contentContainerStyle={styles.flexGrow1} keyboardShouldPersistTaps="handled" addBottomSafeAreaPadding={addBottomSafeAreaPadding} + addOfflineIndicatorBottomSafeAreaPadding={addOfflineIndicatorBottomSafeAreaPadding} ref={formRef} > {scrollViewContent()} diff --git a/src/components/OfflineIndicator.tsx b/src/components/OfflineIndicator.tsx index bee9b3e628130..9ada3c1e1bcc3 100644 --- a/src/components/OfflineIndicator.tsx +++ b/src/components/OfflineIndicator.tsx @@ -22,11 +22,20 @@ type OfflineIndicatorProps = { /** Whether to add bottom safe area padding to the view. */ addBottomSafeAreaPadding?: boolean; + /** Whether to add bottom safe area padding to the content. */ + addOfflineIndicatorBottomSafeAreaPadding?: boolean; + /** Whether to make the indicator translucent. */ isTranslucent?: boolean; }; -function OfflineIndicator({style, containerStyles: containerStylesProp, addBottomSafeAreaPadding = false, isTranslucent = false}: OfflineIndicatorProps) { +function OfflineIndicator({ + style, + containerStyles: containerStylesProp, + addBottomSafeAreaPadding = false, + addOfflineIndicatorBottomSafeAreaPadding = false, + isTranslucent = false, +}: OfflineIndicatorProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -35,6 +44,7 @@ function OfflineIndicator({style, containerStyles: containerStylesProp, addBotto const containerStyles = useBottomSafeSafeAreaPaddingStyle({ addBottomSafeAreaPadding, + addOfflineIndicatorBottomSafeAreaPadding, style: containerStylesProp ?? (shouldUseNarrowLayout ? styles.offlineIndicatorMobile : styles.offlineIndicator), }); diff --git a/src/components/ScrollView.tsx b/src/components/ScrollView.tsx index 8a2a4519e0d4f..f1a11cf9499b2 100644 --- a/src/components/ScrollView.tsx +++ b/src/components/ScrollView.tsx @@ -8,13 +8,27 @@ import useBottomSafeSafeAreaPaddingStyle from '@hooks/useBottomSafeSafeAreaPaddi type ScrollViewProps = RNScrollViewProps & { /** Whether to add bottom safe area padding to the content. */ addBottomSafeAreaPadding?: boolean; + + /** Whether to add bottom safe area padding to the content. */ + addOfflineIndicatorBottomSafeAreaPadding?: boolean; }; function ScrollView( - {children, scrollIndicatorInsets, contentContainerStyle: contentContainerStyleProp, addBottomSafeAreaPadding = false, ...props}: ScrollViewProps, + { + children, + scrollIndicatorInsets, + contentContainerStyle: contentContainerStyleProp, + addBottomSafeAreaPadding = false, + addOfflineIndicatorBottomSafeAreaPadding = false, + ...props + }: ScrollViewProps, ref: ForwardedRef, ) { - const contentContainerStyle = useBottomSafeSafeAreaPaddingStyle({addBottomSafeAreaPadding, style: contentContainerStyleProp}); + const contentContainerStyle = useBottomSafeSafeAreaPaddingStyle({ + addBottomSafeAreaPadding, + addOfflineIndicatorBottomSafeAreaPadding, + style: contentContainerStyleProp, + }); return ( ( - {addBottomSafeAreaPadding = false, contentContainerStyle: contentContainerStyleProp, ...restProps}: SectionListProps, + {addBottomSafeAreaPadding = false, addOfflineIndicatorBottomSafeAreaPadding = false, contentContainerStyle: contentContainerStyleProp, ...restProps}: SectionListProps, ref: SectionListRef, ) { - const contentContainerStyle = useBottomSafeSafeAreaPaddingStyle({addBottomSafeAreaPadding, style: contentContainerStyleProp}); + const contentContainerStyle = useBottomSafeSafeAreaPaddingStyle({addBottomSafeAreaPadding, addOfflineIndicatorBottomSafeAreaPadding, style: contentContainerStyleProp}); return ( = RNSectionListProps & { /** Whether to add bottom safe area padding to the content. */ addBottomSafeAreaPadding?: boolean; + + /** Whether to add bottom safe area padding to the content. */ + addOfflineIndicatorBottomSafeAreaPadding?: boolean; }; type SectionListRef = ForwardedRef>; diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index c1e3818a12e28..3375979a96d87 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -16,7 +16,6 @@ import Text from '@components/Text'; import TextInput from '@components/TextInput'; import useActiveElementRole from '@hooks/useActiveElementRole'; import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager'; -import useBottomSafeSafeAreaPaddingStyle from '@hooks/useBottomSafeSafeAreaPaddingStyle'; import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; import useKeyboardState from '@hooks/useKeyboardState'; import useLocalize from '@hooks/useLocalize'; @@ -116,7 +115,7 @@ function BaseSelectionList( listItemWrapperStyle, shouldIgnoreFocus = false, scrollEventThrottle, - contentContainerStyle: contentContainerStyleProp, + contentContainerStyle, shouldHighlightSelectedItem = false, shouldKeepFocusedItemAtTopOfViewableArea = false, shouldDebounceScrolling = false, @@ -129,6 +128,7 @@ function BaseSelectionList( isScreenFocused = false, shouldSubscribeToArrowKeyEvents = true, addBottomSafeAreaPadding = false, + addOfflineIndicatorBottomSafeAreaPadding = false, }: SelectionListProps, ref: ForwardedRef, ) { @@ -831,13 +831,6 @@ function BaseSelectionList( [footerContent, includeSafeAreaPaddingBottom, isKeyboardShown, safeAreaPaddingBottomStyle], ); - // If the default confirm button is visible and it is bottom-sticky, - // we need to add additional padding bottom to the content container. - const contentContainerStyle = useBottomSafeSafeAreaPaddingStyle({ - addBottomSafeAreaPadding: false, // Bottom safe area padding is already applied in the SectionList - style: contentContainerStyleProp, - }); - const shouldHideContentBottomSafeAreaPadding = showConfirmButton || !!footerContent; // TODO: test _every_ component that uses SelectionList @@ -900,6 +893,7 @@ function BaseSelectionList( onEndReachedThreshold={onEndReachedThreshold} scrollEventThrottle={scrollEventThrottle} addBottomSafeAreaPadding={!shouldHideContentBottomSafeAreaPadding && addBottomSafeAreaPadding} + addOfflineIndicatorBottomSafeAreaPadding={!shouldHideContentBottomSafeAreaPadding && addOfflineIndicatorBottomSafeAreaPadding} contentContainerStyle={contentContainerStyle} CellRendererComponent={shouldPreventActiveCellVirtualization ? FocusAwareCellRendererComponent : undefined} /> @@ -910,6 +904,7 @@ function BaseSelectionList(