diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index 70818eaf343a..e1c5a7c48b86 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -210,6 +210,7 @@ function BaseModal( const modalContextValue = useMemo( () => ({ activeModalType: isVisible ? type : undefined, + default: false, }), [isVisible, type], ); diff --git a/src/components/Modal/ModalContext.ts b/src/components/Modal/ModalContext.ts index a5c2eb21ab1d..b9d76bcc18b4 100644 --- a/src/components/Modal/ModalContext.ts +++ b/src/components/Modal/ModalContext.ts @@ -5,11 +5,12 @@ type ModalContextType = { // The type of the currently displayed modal, or undefined if there is no currently displayed modal. // Note that React Native can only display one modal at a time. activeModalType?: ModalType; + default: boolean; }; // This context is meant to inform modal children that they are rendering in a modal (and what type of modal they are rendering in) // Note that this is different than ONYXKEYS.MODAL.isVisible data point in that that is a global variable for whether a modal is visible or not, // whereas this context is provided by the BaseModal component, and thus is only available to components rendered inside a modal. -const ModalContext = createContext({}); +const ModalContext = createContext({default: true}); export default ModalContext; diff --git a/src/components/ScreenWrapper.tsx b/src/components/ScreenWrapper.tsx index e264b7fd9a55..09315bfb8a8e 100644 --- a/src/components/ScreenWrapper.tsx +++ b/src/components/ScreenWrapper.tsx @@ -1,6 +1,6 @@ import {UNSTABLE_usePreventRemove, useIsFocused, useNavigation, useRoute} from '@react-navigation/native'; import type {ForwardedRef, ReactNode} from 'react'; -import React, {createContext, forwardRef, useEffect, useMemo, useRef, useState} from 'react'; +import React, {createContext, forwardRef, useContext, useEffect, useMemo, useRef, useState} from 'react'; import type {StyleProp, ViewStyle} from 'react-native'; import {Keyboard, NativeModules, PanResponder, View} from 'react-native'; import {PickerAvoidingView} from 'react-native-picker-select'; @@ -25,6 +25,7 @@ import type FocusTrapForScreenProps from './FocusTrap/FocusTrapForScreen/FocusTr import HeaderGap from './HeaderGap'; import ImportedStateIndicator from './ImportedStateIndicator'; import KeyboardAvoidingView from './KeyboardAvoidingView'; +import ModalContext from './Modal/ModalContext'; import OfflineIndicator from './OfflineIndicator'; import withNavigationFallback from './withNavigationFallback'; @@ -149,6 +150,8 @@ function ScreenWrapper( const navigation = navigationProp ?? navigationFallback; const isFocused = useIsFocused(); const {windowHeight} = useWindowDimensions(shouldUseCachedViewportHeight); + // since Modals are drawn in separate native view hierarchy we should always add paddings + const ignoreInsetsConsumption = !useContext(ModalContext).default; // We need to use isSmallScreenWidth instead of shouldUseNarrowLayout for a case where we want to show the offline indicator only on small screens // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth @@ -237,19 +240,25 @@ function ScreenWrapper( // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, []); - const {insets, paddingTop, paddingBottom, safeAreaPaddingBottomStyle} = useStyledSafeAreaInsets(); + const {insets, paddingTop, paddingBottom, safeAreaPaddingBottomStyle, unmodifiedPaddings} = useStyledSafeAreaInsets(); const paddingStyle: StyleProp = {}; const isSafeAreaTopPaddingApplied = includePaddingTop; if (includePaddingTop) { paddingStyle.paddingTop = paddingTop; } + if (includePaddingTop && ignoreInsetsConsumption) { + paddingStyle.paddingTop = unmodifiedPaddings.top; + } // We always need the safe area padding bottom if we're showing the offline indicator since it is bottom-docked. const isSafeAreaBottomPaddingApplied = includeSafeAreaPaddingBottom || (isOffline && shouldShowOfflineIndicator); if (isSafeAreaBottomPaddingApplied) { paddingStyle.paddingBottom = paddingBottom; } + if (isSafeAreaBottomPaddingApplied && ignoreInsetsConsumption) { + paddingStyle.paddingBottom = unmodifiedPaddings.bottom; + } const isAvoidingViewportScroll = useTackInputFocus(isFocused && shouldEnableMaxHeight && shouldAvoidScrollOnVirtualViewport && Browser.isMobileWebKit()); const contextValue = useMemo( diff --git a/src/components/TextPicker/TextSelectorModal.tsx b/src/components/TextPicker/TextSelectorModal.tsx index 00692fc501e6..4edc630fa0d6 100644 --- a/src/components/TextPicker/TextSelectorModal.tsx +++ b/src/components/TextPicker/TextSelectorModal.tsx @@ -11,7 +11,6 @@ import Text from '@components/Text'; import TextInput from '@components/TextInput'; import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types'; import useLocalize from '@hooks/useLocalize'; -import useSafeAreaInsets from '@hooks/useSafeAreaInsets'; import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -22,7 +21,6 @@ function TextSelectorModal({value, description = '', subtitle, onValueSelected, const styles = useThemeStyles(); const [currentValue, setValue] = useState(value); - const {top} = useSafeAreaInsets(); const inputRef = useRef(null); const inputValueRef = useRef(value); @@ -81,11 +79,10 @@ function TextSelectorModal({value, description = '', subtitle, onValueSelected, shouldUseModalPaddingStyle={false} > (null); - const {top} = useSafeAreaInsets(); const [validateCodeAction] = useOnyx(ONYXKEYS.VALIDATE_ACTION_CODE); @@ -64,10 +62,10 @@ function ValidateCodeActionModal({ >