From cd65e371ed08172f294baa97e59716acd9dd0fd8 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Wed, 22 Jan 2025 17:23:13 +0100 Subject: [PATCH 01/80] refactor: rename StyleUtils safe area padding function --- src/components/AutoCompleteSuggestions/index.tsx | 2 +- .../GrowlNotificationContainer/index.native.tsx | 2 +- src/components/Modal/BaseModal.tsx | 2 +- src/components/PopoverWithoutOverlay/index.tsx | 2 +- src/components/SafeAreaConsumer/index.tsx | 2 +- src/pages/ErrorPage/UpdateRequiredView.tsx | 2 +- src/pages/signin/SignInPage.tsx | 2 +- src/styles/utils/index.ts | 6 +++--- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/AutoCompleteSuggestions/index.tsx b/src/components/AutoCompleteSuggestions/index.tsx index 8d91525c25bc..6cc8ef9a5a1d 100644 --- a/src/components/AutoCompleteSuggestions/index.tsx +++ b/src/components/AutoCompleteSuggestions/index.tsx @@ -64,7 +64,7 @@ function AutoCompleteSuggestions({measureParentContainerAndReportCu const StyleUtils = useStyleUtils(); const insets = useSafeAreaInsets(); const {keyboardHeight, isKeyboardAnimatingRef} = useKeyboardState(); - const {paddingBottom: bottomInset, paddingTop: topInset} = StyleUtils.getSafeAreaPadding(insets ?? undefined); + const {paddingBottom: bottomInset, paddingTop: topInset} = StyleUtils.getPlatformSafeAreaPadding(insets ?? undefined); useEffect(() => { const container = containerRef.current; diff --git a/src/components/GrowlNotification/GrowlNotificationContainer/index.native.tsx b/src/components/GrowlNotification/GrowlNotificationContainer/index.native.tsx index 0ca7756b0e0e..f7ddf2767402 100644 --- a/src/components/GrowlNotification/GrowlNotificationContainer/index.native.tsx +++ b/src/components/GrowlNotification/GrowlNotificationContainer/index.native.tsx @@ -11,7 +11,7 @@ function GrowlNotificationContainer({children, translateY}: GrowlNotificationCon const insets = useSafeAreaInsets(); const animatedStyles = useAnimatedStyle(() => styles.growlNotificationTranslateY(translateY)); - return {children}; + return {children}; } GrowlNotificationContainer.displayName = 'GrowlNotificationContainer'; diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index 723366608c5a..5dca70d3b86a 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -203,7 +203,7 @@ function BaseModal( paddingBottom: safeAreaPaddingBottom, paddingLeft: safeAreaPaddingLeft, paddingRight: safeAreaPaddingRight, - } = StyleUtils.getSafeAreaPadding(safeAreaInsets); + } = StyleUtils.getPlatformSafeAreaPadding(safeAreaInsets); const modalPaddingStyles = shouldUseModalPaddingStyle ? StyleUtils.getModalPaddingStyles({ diff --git a/src/components/PopoverWithoutOverlay/index.tsx b/src/components/PopoverWithoutOverlay/index.tsx index 7d58ad6d22be..28a3f0a85bc1 100644 --- a/src/components/PopoverWithoutOverlay/index.tsx +++ b/src/components/PopoverWithoutOverlay/index.tsx @@ -77,7 +77,7 @@ function PopoverWithoutOverlay( paddingBottom: safeAreaPaddingBottom, paddingLeft: safeAreaPaddingLeft, paddingRight: safeAreaPaddingRight, - } = useMemo(() => StyleUtils.getSafeAreaPadding(insets), [StyleUtils, insets]); + } = useMemo(() => StyleUtils.getPlatformSafeAreaPadding(insets), [StyleUtils, insets]); const modalPaddingStyles = useMemo( () => diff --git a/src/components/SafeAreaConsumer/index.tsx b/src/components/SafeAreaConsumer/index.tsx index 416021242225..8cc8d531905b 100644 --- a/src/components/SafeAreaConsumer/index.tsx +++ b/src/components/SafeAreaConsumer/index.tsx @@ -16,7 +16,7 @@ function SafeAreaConsumer({children}: SafeAreaConsumerProps) { {(safeAreaInsets) => { const insets = StyleUtils.getSafeAreaInsets(safeAreaInsets); - const {paddingTop, paddingBottom} = StyleUtils.getSafeAreaPadding(insets); + const {paddingTop, paddingBottom} = StyleUtils.getPlatformSafeAreaPadding(insets); return children({ paddingTop, diff --git a/src/pages/ErrorPage/UpdateRequiredView.tsx b/src/pages/ErrorPage/UpdateRequiredView.tsx index 750a3c891b0f..c4f1272654fd 100644 --- a/src/pages/ErrorPage/UpdateRequiredView.tsx +++ b/src/pages/ErrorPage/UpdateRequiredView.tsx @@ -25,7 +25,7 @@ function UpdateRequiredView() { const isStandaloneNewAppProduction = isProduction && !NativeModules.HybridAppModule; return ( - +
diff --git a/src/pages/signin/SignInPage.tsx b/src/pages/signin/SignInPage.tsx index 221ca93701be..44fd8776b43f 100644 --- a/src/pages/signin/SignInPage.tsx +++ b/src/pages/signin/SignInPage.tsx @@ -302,7 +302,7 @@ function SignInPage({shouldEnableMaxHeight = true}: SignInPageInnerProps, ref: F Date: Wed, 22 Jan 2025 18:54:53 +0100 Subject: [PATCH 02/80] refactor: simplify code in `useStyledSafeAreaInsets` hook --- src/hooks/useStyledSafeAreaInsets.ts | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/hooks/useStyledSafeAreaInsets.ts b/src/hooks/useStyledSafeAreaInsets.ts index b5a7bd2413c4..e320b6df466a 100644 --- a/src/hooks/useStyledSafeAreaInsets.ts +++ b/src/hooks/useStyledSafeAreaInsets.ts @@ -34,27 +34,29 @@ import useStyleUtils from './useStyleUtils'; function useStyledSafeAreaInsets() { const StyleUtils = useStyleUtils(); const insets = useSafeAreaInsets(); - const {paddingTop, paddingBottom} = StyleUtils.getSafeAreaPadding(insets); + const {paddingTop: platformPaddingTop, paddingBottom: platformPaddingBottom} = StyleUtils.getPlatformSafeAreaPadding(insets); const screenWrapperStatusContext = useContext(ScreenWrapperStatusContext); - const isSafeAreaTopPaddingApplied = screenWrapperStatusContext?.isSafeAreaTopPaddingApplied ?? false; - const isSafeAreaBottomPaddingApplied = screenWrapperStatusContext?.isSafeAreaBottomPaddingApplied ?? false; + const isTopAlreadyApplied = screenWrapperStatusContext?.isSafeAreaTopPaddingApplied ?? false; + const isBottomAlreadyApplied = screenWrapperStatusContext?.isSafeAreaBottomPaddingApplied ?? false; const adaptedInsets = { ...insets, - top: isSafeAreaTopPaddingApplied ? 0 : insets?.top, - bottom: isSafeAreaBottomPaddingApplied ? 0 : insets?.bottom, + top: isTopAlreadyApplied ? 0 : insets?.top, + bottom: isBottomAlreadyApplied ? 0 : insets?.bottom, }; - const adaptedPaddingBottom = isSafeAreaBottomPaddingApplied ? 0 : paddingBottom; - const safeAreaPaddingBottomStyle = useMemo(() => ({paddingBottom: adaptedPaddingBottom}), [adaptedPaddingBottom]); + const paddingTop = isTopAlreadyApplied ? 0 : platformPaddingTop; + const paddingBottom = isBottomAlreadyApplied ? 0 : platformPaddingBottom; + + const safeAreaPaddingBottomStyle = useMemo(() => ({paddingBottom}), [paddingBottom]); return { - paddingTop: isSafeAreaTopPaddingApplied ? 0 : paddingTop, - paddingBottom: adaptedPaddingBottom, + paddingTop, + paddingBottom, unmodifiedPaddings: { - top: paddingTop, - bottom: paddingBottom, + top: platformPaddingTop, + bottom: platformPaddingBottom, }, insets: adaptedInsets, safeAreaPaddingBottomStyle, From 0fbfb2779ff3ece248f1afdf5d40c62bdf47cbcc Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Wed, 22 Jan 2025 18:55:21 +0100 Subject: [PATCH 03/80] feat: implement android navigation bar type specific bottom safe area insets --- src/CONST.ts | 10 +++++++++ .../getNavigationBarType/index.android.ts | 22 +++++++++++++++++++ .../utils/getNavigationBarType/index.ios.ts | 16 ++++++++++++++ .../utils/getNavigationBarType/index.ts | 8 +++++++ .../utils/getNavigationBarType/types.ts | 7 ++++++ src/styles/utils/index.ts | 21 +++++++++++++++++- 6 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 src/styles/utils/getNavigationBarType/index.android.ts create mode 100644 src/styles/utils/getNavigationBarType/index.ios.ts create mode 100644 src/styles/utils/getNavigationBarType/index.ts create mode 100644 src/styles/utils/getNavigationBarType/types.ts diff --git a/src/CONST.ts b/src/CONST.ts index 38eff4ae2bc1..74a926fad5db 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1457,6 +1457,16 @@ const CONST = { LIGHT: 'light', DARK: 'dark', }, + NAVIGATION_BAR_TYPE: { + STATIC_SOFT_KEYS: 'static-soft-keys', + HIDDEN_SOFT_KEYS_OR_NONE: 'hidden-soft-keys-or-none', + GESTURE_BAR: 'gesture-bar', + }, + // Currently, in Android there is no native API to detect the type of navigation bar (soft keys vs. gesture). + // According to this source, the navigation bar on (standard) Android devices is always 48dpi (device independent pixels) tall. + // To leave room to detect soft-key navigation bars on non-standard Android devices, + // we set this height threshold to 30, since gesture bars are will never be taller than 30dpi. + NAVIGATION_BAR_ANDROID_SOFT_KEYS_MINIMUM_HEIGHT_THRESHOLD: 30, TRANSACTION: { DEFAULT_MERCHANT: 'Expense', UNKNOWN_MERCHANT: 'Unknown Merchant', diff --git a/src/styles/utils/getNavigationBarType/index.android.ts b/src/styles/utils/getNavigationBarType/index.android.ts new file mode 100644 index 000000000000..d8feea7dd9b9 --- /dev/null +++ b/src/styles/utils/getNavigationBarType/index.android.ts @@ -0,0 +1,22 @@ +import type {EdgeInsets} from 'react-native-safe-area-context'; +import CONST from '@src/CONST'; +import type {NavigationBarType} from './types'; + +function getNavigationBarType(insets?: EdgeInsets): NavigationBarType { + const bottomInset = insets?.bottom ?? 0; + + // If the bottom safe area inset is greater than the defined Android minimum soft keys height threshold, we consider it as a static soft keys. + if (bottomInset > CONST.NAVIGATION_BAR_ANDROID_SOFT_KEYS_MINIMUM_HEIGHT_THRESHOLD) { + return 'static-soft-keys'; + } + + // If the bottom safe area inset is greater than 0, we consider it as a gesture bar. + if (bottomInset > 0) { + return 'gesture-bar'; + } + + // If the bottom safe area inset is 0, we consider it as hidden soft keys or no navigation bar (e.g. physical buttons). + return 'hidden-soft-keys-or-none'; +} + +export default getNavigationBarType; diff --git a/src/styles/utils/getNavigationBarType/index.ios.ts b/src/styles/utils/getNavigationBarType/index.ios.ts new file mode 100644 index 000000000000..cffd31e53e9c --- /dev/null +++ b/src/styles/utils/getNavigationBarType/index.ios.ts @@ -0,0 +1,16 @@ +import type {EdgeInsets} from 'react-native-safe-area-context'; +import type {NavigationBarType} from './types'; + +function getNavigationBarType(insets?: EdgeInsets): NavigationBarType { + const bottomInset = insets?.bottom ?? 0; + + // On iOS, if there is a bottom safe area inset, it means the device uses a gesture bar. + if (bottomInset > 0) { + return 'gesture-bar'; + } + + // If there is no bottom safe area inset, the device uses a physical navigation button. + return 'hidden-soft-keys-or-none'; +} + +export default getNavigationBarType; diff --git a/src/styles/utils/getNavigationBarType/index.ts b/src/styles/utils/getNavigationBarType/index.ts new file mode 100644 index 000000000000..7a58c27762e6 --- /dev/null +++ b/src/styles/utils/getNavigationBarType/index.ts @@ -0,0 +1,8 @@ +import type {NavigationBarType} from './types'; + +function getNavigationBarType(): NavigationBarType { + // On web, there is no navigation bar. + return 'hidden-soft-keys-or-none'; +} + +export default getNavigationBarType; diff --git a/src/styles/utils/getNavigationBarType/types.ts b/src/styles/utils/getNavigationBarType/types.ts new file mode 100644 index 000000000000..a8ca1452b546 --- /dev/null +++ b/src/styles/utils/getNavigationBarType/types.ts @@ -0,0 +1,7 @@ +import type {ValueOf} from 'type-fest'; +import type CONST from '@src/CONST'; + +type NavigationBarType = ValueOf; + +// eslint-disable-next-line import/prefer-default-export +export type {NavigationBarType}; diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index a5d3d3803ff6..39db3338eaad 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -24,6 +24,7 @@ import createReportActionContextMenuStyleUtils from './generators/ReportActionCo import createTooltipStyleUtils from './generators/TooltipStyleUtils'; import getContextMenuItemStyles from './getContextMenuItemStyles'; import getHighResolutionInfoWrapperStyle from './getHighResolutionInfoWrapperStyle'; +import getNavigationBarType from './getNavigationBarType/index.android'; import getNavigationModalCardStyle from './getNavigationModalCardStyles'; import getSafeAreaInsets from './getSafeAreaInsets'; import getSignInBgStyles from './getSignInBgStyles'; @@ -328,6 +329,12 @@ type SafeAreaPadding = { paddingBottom: number; paddingLeft: number; paddingRight: number; + + /** + * If we the device has a gesture bar (soft keys or gesture bar), this is the height of the gesture bar. + * Since `paddingBottom` is set to 0 on devices with gesture bar, this value is used to add padding to the bottom of the screen if necessary. + */ + gestureBarHeight: number; }; /** @@ -335,6 +342,17 @@ type SafeAreaPadding = { */ function getPlatformSafeAreaPadding(insets?: EdgeInsets, insetsPercentageProp?: number): SafeAreaPadding { const platform = getPlatform(); + const navigationBarType = getNavigationBarType(insets); + + // If the navigation bar is a gesture bar, we want `paddingBottom` to be 0, + // while providing the `gestureBarHeight` as an extra property + let platformBottomInset = insets?.bottom ?? 0; + let gestureBarHeight = 0; + if (navigationBarType === 'gesture-bar') { + gestureBarHeight = platformBottomInset; + platformBottomInset = 0; + } + let insetsPercentage = insetsPercentageProp; if (insetsPercentage == null) { switch (platform) { @@ -351,9 +369,10 @@ function getPlatformSafeAreaPadding(insets?: EdgeInsets, insetsPercentageProp?: return { paddingTop: insets?.top ?? 0, - paddingBottom: (insets?.bottom ?? 0) * insetsPercentage, + paddingBottom: platformBottomInset * insetsPercentage, paddingLeft: (insets?.left ?? 0) * insetsPercentage, paddingRight: (insets?.right ?? 0) * insetsPercentage, + gestureBarHeight, }; } From 124cc53a06ba9a69a3933c77af880ffbc87edba7 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Wed, 22 Jan 2025 18:57:16 +0100 Subject: [PATCH 04/80] fix: add source url --- src/CONST.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CONST.ts b/src/CONST.ts index 74a926fad5db..c1d4880ecdf5 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1463,6 +1463,7 @@ const CONST = { GESTURE_BAR: 'gesture-bar', }, // Currently, in Android there is no native API to detect the type of navigation bar (soft keys vs. gesture). + // https://forums.solar2d.com/t/height-of-android-navigation-bar-solved/353073 // According to this source, the navigation bar on (standard) Android devices is always 48dpi (device independent pixels) tall. // To leave room to detect soft-key navigation bars on non-standard Android devices, // we set this height threshold to 30, since gesture bars are will never be taller than 30dpi. From e602c2f9b9730dfa1ef030292b5d56a7e1213155 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Wed, 22 Jan 2025 19:53:04 +0100 Subject: [PATCH 05/80] fix: use CONST values and add comments --- src/CONST.ts | 13 ++++++++----- .../utils/getNavigationBarType/index.android.ts | 9 +++++---- src/styles/utils/getNavigationBarType/index.ios.ts | 5 +++-- src/styles/utils/getNavigationBarType/index.ts | 3 ++- src/styles/utils/index.ts | 2 +- 5 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index c1d4880ecdf5..8fb1a057cecf 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1458,15 +1458,18 @@ const CONST = { DARK: 'dark', }, NAVIGATION_BAR_TYPE: { - STATIC_SOFT_KEYS: 'static-soft-keys', - HIDDEN_SOFT_KEYS_OR_NONE: 'hidden-soft-keys-or-none', + // We consider there to be no navigation bar in one of these cases: + // 1. The device has physical navigation buttons + // 2. The device uses gesture navigation without a gesture bar. + // 3. The device uses hidden (auto-hiding) soft keys. + NONE: 'none', + SOFT_KEYS: 'soft-keys', GESTURE_BAR: 'gesture-bar', }, // Currently, in Android there is no native API to detect the type of navigation bar (soft keys vs. gesture). - // https://forums.solar2d.com/t/height-of-android-navigation-bar-solved/353073 - // According to this source, the navigation bar on (standard) Android devices is always 48dpi (device independent pixels) tall. + // The navigation bar on (standard) Android devices is around 40-48dpi (device independent pixels) tall. // To leave room to detect soft-key navigation bars on non-standard Android devices, - // we set this height threshold to 30, since gesture bars are will never be taller than 30dpi. + // we set this height threshold to 30dpi, since gesture bars will never be taller than that. NAVIGATION_BAR_ANDROID_SOFT_KEYS_MINIMUM_HEIGHT_THRESHOLD: 30, TRANSACTION: { DEFAULT_MERCHANT: 'Expense', diff --git a/src/styles/utils/getNavigationBarType/index.android.ts b/src/styles/utils/getNavigationBarType/index.android.ts index d8feea7dd9b9..9a8e53e11fa2 100644 --- a/src/styles/utils/getNavigationBarType/index.android.ts +++ b/src/styles/utils/getNavigationBarType/index.android.ts @@ -7,16 +7,17 @@ function getNavigationBarType(insets?: EdgeInsets): NavigationBarType { // If the bottom safe area inset is greater than the defined Android minimum soft keys height threshold, we consider it as a static soft keys. if (bottomInset > CONST.NAVIGATION_BAR_ANDROID_SOFT_KEYS_MINIMUM_HEIGHT_THRESHOLD) { - return 'static-soft-keys'; + return CONST.NAVIGATION_BAR_TYPE.SOFT_KEYS; } // If the bottom safe area inset is greater than 0, we consider it as a gesture bar. if (bottomInset > 0) { - return 'gesture-bar'; + return CONST.NAVIGATION_BAR_TYPE.GESTURE_BAR; } - // If the bottom safe area inset is 0, we consider it as hidden soft keys or no navigation bar (e.g. physical buttons). - return 'hidden-soft-keys-or-none'; + // If the bottom safe area inset is 0, we consider the device to have no navigation bar (or it being hidden by default). + // This could be mean either hidden soft keys, gesture navigation without a gesture bar or physical buttons. + return CONST.NAVIGATION_BAR_TYPE.NONE; } export default getNavigationBarType; diff --git a/src/styles/utils/getNavigationBarType/index.ios.ts b/src/styles/utils/getNavigationBarType/index.ios.ts index cffd31e53e9c..a53e0d05da94 100644 --- a/src/styles/utils/getNavigationBarType/index.ios.ts +++ b/src/styles/utils/getNavigationBarType/index.ios.ts @@ -1,4 +1,5 @@ import type {EdgeInsets} from 'react-native-safe-area-context'; +import CONST from '@src/CONST'; import type {NavigationBarType} from './types'; function getNavigationBarType(insets?: EdgeInsets): NavigationBarType { @@ -6,11 +7,11 @@ function getNavigationBarType(insets?: EdgeInsets): NavigationBarType { // On iOS, if there is a bottom safe area inset, it means the device uses a gesture bar. if (bottomInset > 0) { - return 'gesture-bar'; + return CONST.NAVIGATION_BAR_TYPE.GESTURE_BAR; } // If there is no bottom safe area inset, the device uses a physical navigation button. - return 'hidden-soft-keys-or-none'; + return CONST.NAVIGATION_BAR_TYPE.NONE; } export default getNavigationBarType; diff --git a/src/styles/utils/getNavigationBarType/index.ts b/src/styles/utils/getNavigationBarType/index.ts index 7a58c27762e6..90639fa31847 100644 --- a/src/styles/utils/getNavigationBarType/index.ts +++ b/src/styles/utils/getNavigationBarType/index.ts @@ -1,8 +1,9 @@ +import CONST from '@src/CONST'; import type {NavigationBarType} from './types'; function getNavigationBarType(): NavigationBarType { // On web, there is no navigation bar. - return 'hidden-soft-keys-or-none'; + return CONST.NAVIGATION_BAR_TYPE.NONE; } export default getNavigationBarType; diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 39db3338eaad..8bba9580bc86 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -348,7 +348,7 @@ function getPlatformSafeAreaPadding(insets?: EdgeInsets, insetsPercentageProp?: // while providing the `gestureBarHeight` as an extra property let platformBottomInset = insets?.bottom ?? 0; let gestureBarHeight = 0; - if (navigationBarType === 'gesture-bar') { + if (navigationBarType === CONST.NAVIGATION_BAR_TYPE.GESTURE_BAR) { gestureBarHeight = platformBottomInset; platformBottomInset = 0; } From 490e560cbf33f1a0a572592412a454c05fb26358 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Wed, 22 Jan 2025 20:04:04 +0100 Subject: [PATCH 06/80] update comment about navigation bar height threshold --- src/CONST.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 8fb1a057cecf..42c3907ecb5d 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1467,9 +1467,9 @@ const CONST = { GESTURE_BAR: 'gesture-bar', }, // Currently, in Android there is no native API to detect the type of navigation bar (soft keys vs. gesture). - // The navigation bar on (standard) Android devices is around 40-48dpi (device independent pixels) tall. + // The navigation bar on (standard) Android devices is around 30-50dpi tall. (Samsung: 40dpi, Huawei: ~34dpi) // To leave room to detect soft-key navigation bars on non-standard Android devices, - // we set this height threshold to 30dpi, since gesture bars will never be taller than that. + // we set this height threshold to 30dpi, since gesture bars will never be taller than that. (Samsung & Huawei: ~14-15dpi) NAVIGATION_BAR_ANDROID_SOFT_KEYS_MINIMUM_HEIGHT_THRESHOLD: 30, TRANSACTION: { DEFAULT_MERCHANT: 'Expense', From e17118e6d67bed5c740c9bfe65999145a3868d9f Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Mon, 17 Feb 2025 18:27:52 +0100 Subject: [PATCH 07/80] fix: revert navigation bar changes in `getPlatformSafeAreaPadding` --- src/styles/utils/index.ts | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 8bba9580bc86..6154ed9e2c73 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -329,12 +329,6 @@ type SafeAreaPadding = { paddingBottom: number; paddingLeft: number; paddingRight: number; - - /** - * If we the device has a gesture bar (soft keys or gesture bar), this is the height of the gesture bar. - * Since `paddingBottom` is set to 0 on devices with gesture bar, this value is used to add padding to the bottom of the screen if necessary. - */ - gestureBarHeight: number; }; /** @@ -342,16 +336,6 @@ type SafeAreaPadding = { */ function getPlatformSafeAreaPadding(insets?: EdgeInsets, insetsPercentageProp?: number): SafeAreaPadding { const platform = getPlatform(); - const navigationBarType = getNavigationBarType(insets); - - // If the navigation bar is a gesture bar, we want `paddingBottom` to be 0, - // while providing the `gestureBarHeight` as an extra property - let platformBottomInset = insets?.bottom ?? 0; - let gestureBarHeight = 0; - if (navigationBarType === CONST.NAVIGATION_BAR_TYPE.GESTURE_BAR) { - gestureBarHeight = platformBottomInset; - platformBottomInset = 0; - } let insetsPercentage = insetsPercentageProp; if (insetsPercentage == null) { @@ -369,10 +353,9 @@ function getPlatformSafeAreaPadding(insets?: EdgeInsets, insetsPercentageProp?: return { paddingTop: insets?.top ?? 0, - paddingBottom: platformBottomInset * insetsPercentage, + paddingBottom: insets?.bottom ?? 0 * insetsPercentage, paddingLeft: (insets?.left ?? 0) * insetsPercentage, paddingRight: (insets?.right ?? 0) * insetsPercentage, - gestureBarHeight, }; } @@ -1270,6 +1253,7 @@ const staticStyleUtils = { getBorderRadiusStyle, getHighResolutionInfoWrapperStyle, getItemBackgroundColorStyle, + getNavigationBarType, }; const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ From 9c23b7c50649f05e60731c99c4c4b6893c9af20d Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Mon, 17 Feb 2025 20:04:53 +0100 Subject: [PATCH 08/80] feat: disable bottom safe area padding handling completely --- src/components/ScreenWrapper.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/ScreenWrapper.tsx b/src/components/ScreenWrapper.tsx index ee543765169d..424da2e16c6b 100644 --- a/src/components/ScreenWrapper.tsx +++ b/src/components/ScreenWrapper.tsx @@ -30,6 +30,8 @@ import ModalContext from './Modal/ModalContext'; import OfflineIndicator from './OfflineIndicator'; import withNavigationFallback from './withNavigationFallback'; +const includeSafeAreaPaddingBottom = false; + type ScreenWrapperChildrenProps = { insets: EdgeInsets; safeAreaPaddingBottomStyle?: { @@ -124,7 +126,6 @@ function ScreenWrapper( shouldEnableMinHeight = false, includePaddingTop = true, keyboardAvoidingViewBehavior = 'padding', - includeSafeAreaPaddingBottom = true, shouldEnableKeyboardAvoidingView = true, shouldEnablePickerAvoiding = true, headerGapStyles, @@ -263,7 +264,6 @@ function ScreenWrapper( const {insets, paddingTop, paddingBottom, safeAreaPaddingBottomStyle, unmodifiedPaddings} = useStyledSafeAreaInsets(); const paddingTopStyle: StyleProp = {}; - const paddingBottomStyle: StyleProp = {}; const isSafeAreaTopPaddingApplied = includePaddingTop; if (includePaddingTop) { @@ -273,18 +273,16 @@ function ScreenWrapper( paddingTopStyle.paddingTop = unmodifiedPaddings.top; } + const bottomContentStyle: StyleProp = safeAreaPaddingBottomStyle; // We always need the safe area padding bottom if we're showing the offline indicator since it is bottom-docked. - if (includeSafeAreaPaddingBottom) { - paddingBottomStyle.paddingBottom = paddingBottom; - } if (includeSafeAreaPaddingBottom && ignoreInsetsConsumption) { - paddingBottomStyle.paddingBottom = unmodifiedPaddings.bottom; + bottomContentStyle.paddingBottom = unmodifiedPaddings.bottom; } const isAvoidingViewportScroll = useTackInputFocus(isFocused && shouldEnableMaxHeight && shouldAvoidScrollOnVirtualViewport && isMobileWebKit()); const contextValue = useMemo( () => ({didScreenTransitionEnd, isSafeAreaTopPaddingApplied, isSafeAreaBottomPaddingApplied: includeSafeAreaPaddingBottom}), - [didScreenTransitionEnd, includeSafeAreaPaddingBottom, isSafeAreaTopPaddingApplied], + [didScreenTransitionEnd, isSafeAreaTopPaddingApplied], ); return ( @@ -352,7 +350,7 @@ function ScreenWrapper( - {bottomContent} + {bottomContent && {bottomContent}} ); From cdccefa3639360068504b212841dd17fbe7d6700 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Wed, 19 Feb 2025 08:59:31 +0100 Subject: [PATCH 09/80] fix: syntax and eslint error in ScreenWrapper --- src/components/ScreenWrapper.tsx | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/components/ScreenWrapper.tsx b/src/components/ScreenWrapper.tsx index 424da2e16c6b..7c0de6003966 100644 --- a/src/components/ScreenWrapper.tsx +++ b/src/components/ScreenWrapper.tsx @@ -262,7 +262,7 @@ function ScreenWrapper( // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, []); - const {insets, paddingTop, paddingBottom, safeAreaPaddingBottomStyle, unmodifiedPaddings} = useStyledSafeAreaInsets(); + const {insets, paddingTop, safeAreaPaddingBottomStyle, unmodifiedPaddings} = useStyledSafeAreaInsets(); const paddingTopStyle: StyleProp = {}; const isSafeAreaTopPaddingApplied = includePaddingTop; @@ -273,11 +273,11 @@ function ScreenWrapper( paddingTopStyle.paddingTop = unmodifiedPaddings.top; } - const bottomContentStyle: StyleProp = safeAreaPaddingBottomStyle; - // We always need the safe area padding bottom if we're showing the offline indicator since it is bottom-docked. - if (includeSafeAreaPaddingBottom && ignoreInsetsConsumption) { - bottomContentStyle.paddingBottom = unmodifiedPaddings.bottom; - } + const bottomContentStyle: StyleProp = { + ...safeAreaPaddingBottomStyle, + // We always need the safe area padding bottom if we're showing the offline indicator since it is bottom-docked. + ...(includeSafeAreaPaddingBottom && ignoreInsetsConsumption ? {paddingBottom: unmodifiedPaddings.bottom} : {}), + }; const isAvoidingViewportScroll = useTackInputFocus(isFocused && shouldEnableMaxHeight && shouldAvoidScrollOnVirtualViewport && isMobileWebKit()); const contextValue = useMemo( @@ -326,11 +326,7 @@ function ScreenWrapper( <> {/* Since import state is tightly coupled to the offline state, it is safe to display it when showing offline indicator */} @@ -350,7 +346,7 @@ function ScreenWrapper( - {bottomContent && {bottomContent}} + {!!bottomContent && {bottomContent}} ); From 1bbb3b7590ea94a20002f9b6d2d9463f89107209 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Wed, 19 Feb 2025 11:28:11 +0100 Subject: [PATCH 10/80] fix: safe area padding in CategorySelectorModal (SelectionList flag) --- src/components/SelectionList/BaseSelectionList.tsx | 14 +++++++++++--- src/components/SelectionList/types.ts | 9 +++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index 7ec18f288a76..585a0cf0e719 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -114,7 +114,7 @@ function BaseSelectionList( listItemWrapperStyle, shouldIgnoreFocus = false, scrollEventThrottle, - contentContainerStyle, + contentContainerStyle: contentContainerStyleProp, shouldHighlightSelectedItem = false, shouldKeepFocusedItemAtTopOfViewableArea = false, shouldDebounceScrolling = false, @@ -126,6 +126,7 @@ function BaseSelectionList( listItemTitleContainerStyles, isScreenFocused = false, shouldSubscribeToArrowKeyEvents = true, + TEMPORARY_enableBottomSafeAreaPaddingInContent: enableBottomSafeAreaPaddingInContent = false, }: BaseSelectionListProps, ref: ForwardedRef, ) { @@ -822,10 +823,17 @@ function BaseSelectionList( ); const {safeAreaPaddingBottomStyle} = useStyledSafeAreaInsets(); + const paddingBottomStyle = useMemo( + () => (!isKeyboardShown || !!footerContent) && includeSafeAreaPaddingBottom && safeAreaPaddingBottomStyle, + [footerContent, includeSafeAreaPaddingBottom, isKeyboardShown, safeAreaPaddingBottomStyle], + ); + const sectionListContentContainerStyle = useMemo(() => { + return enableBottomSafeAreaPaddingInContent ? [paddingBottomStyle, contentContainerStyleProp] : contentContainerStyleProp; + }, [contentContainerStyleProp, enableBottomSafeAreaPaddingInContent, paddingBottomStyle]); // TODO: test _every_ component that uses SelectionList return ( - + {shouldShowTextInput && !shouldShowTextInputAfterHeader && renderInput()} {/* If we are loading new options we will avoid showing any header message. This is mostly because one of the header messages says there are no options. */} {/* This is misleading because we might be in the process of loading fresh options from the server. */} @@ -881,7 +889,7 @@ function BaseSelectionList( onEndReached={onEndReached} onEndReachedThreshold={onEndReachedThreshold} scrollEventThrottle={scrollEventThrottle} - contentContainerStyle={contentContainerStyle} + contentContainerStyle={sectionListContentContainerStyle} CellRendererComponent={shouldPreventActiveCellVirtualization ? FocusAwareCellRendererComponent : undefined} /> {children} diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 3eb63ae97242..9b1310eb2184 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -668,6 +668,15 @@ type BaseSelectionListProps = Partial & { /** Whether the screen is focused or not. (useIsFocused state does not work in tab screens, e.g. SearchPageBottomTab) */ isScreenFocused?: boolean; + + /** + * Temporary flag to enable bottom safe area handling in selection list content (Default: false) + * Bottom safe area padding at the moment is handled in the root view of the selection list. + * Optimally, we want to add the padding to the scrollable content instead (contentContainerStyle of the ). + * This flag can be removed, once all components/screens have switched to this behavior + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + TEMPORARY_enableBottomSafeAreaPaddingInContent?: boolean; } & TRightHandSideComponent; type SelectionListHandle = { From ffcf463d71d5e2bfe2ee6c91d3930faeb032b835 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Wed, 19 Feb 2025 18:48:25 +0100 Subject: [PATCH 11/80] fix: missing parantheses -> wrong bottom safe area padding --- src/styles/utils/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 6154ed9e2c73..9135cbf19f64 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -353,7 +353,7 @@ function getPlatformSafeAreaPadding(insets?: EdgeInsets, insetsPercentageProp?: return { paddingTop: insets?.top ?? 0, - paddingBottom: insets?.bottom ?? 0 * insetsPercentage, + paddingBottom: (insets?.bottom ?? 0) * insetsPercentage, paddingLeft: (insets?.left ?? 0) * insetsPercentage, paddingRight: (insets?.right ?? 0) * insetsPercentage, }; From f27e1b0a59893099c6ab53dba80d1c7e03810240 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Wed, 19 Feb 2025 18:48:56 +0100 Subject: [PATCH 12/80] feat: simplify `useStyledSafeAreaInsets` hook --- src/hooks/useStyledSafeAreaInsets.ts | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/src/hooks/useStyledSafeAreaInsets.ts b/src/hooks/useStyledSafeAreaInsets.ts index e320b6df466a..1916790f540e 100644 --- a/src/hooks/useStyledSafeAreaInsets.ts +++ b/src/hooks/useStyledSafeAreaInsets.ts @@ -1,5 +1,4 @@ -import {useContext, useMemo} from 'react'; -import {ScreenWrapperStatusContext} from '@components/ScreenWrapper'; +import {useMemo} from 'react'; import useSafeAreaInsets from './useSafeAreaInsets'; import useStyleUtils from './useStyleUtils'; @@ -34,31 +33,14 @@ import useStyleUtils from './useStyleUtils'; function useStyledSafeAreaInsets() { const StyleUtils = useStyleUtils(); const insets = useSafeAreaInsets(); - const {paddingTop: platformPaddingTop, paddingBottom: platformPaddingBottom} = StyleUtils.getPlatformSafeAreaPadding(insets); - - const screenWrapperStatusContext = useContext(ScreenWrapperStatusContext); - const isTopAlreadyApplied = screenWrapperStatusContext?.isSafeAreaTopPaddingApplied ?? false; - const isBottomAlreadyApplied = screenWrapperStatusContext?.isSafeAreaBottomPaddingApplied ?? false; - - const adaptedInsets = { - ...insets, - top: isTopAlreadyApplied ? 0 : insets?.top, - bottom: isBottomAlreadyApplied ? 0 : insets?.bottom, - }; - - const paddingTop = isTopAlreadyApplied ? 0 : platformPaddingTop; - const paddingBottom = isBottomAlreadyApplied ? 0 : platformPaddingBottom; + const {paddingTop, paddingBottom} = StyleUtils.getPlatformSafeAreaPadding(insets); const safeAreaPaddingBottomStyle = useMemo(() => ({paddingBottom}), [paddingBottom]); return { paddingTop, paddingBottom, - unmodifiedPaddings: { - top: platformPaddingTop, - bottom: platformPaddingBottom, - }, - insets: adaptedInsets, + insets, safeAreaPaddingBottomStyle, }; } From 26f9d5c001b8a2f8676f83870d0f10f335b30c1d Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Wed, 19 Feb 2025 19:09:14 +0100 Subject: [PATCH 13/80] feat: add BaseModal flag for disabling bottom safe area spacing --- src/components/Modal/BaseModal.tsx | 4 +++- src/components/Modal/types.ts | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index 5dca70d3b86a..a7a9aa5ca13e 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -75,6 +75,7 @@ function BaseModal( swipeThreshold = 150, swipeDirection, shouldPreventScrollOnFocus = false, + TEMPORARY_disableSafeAreaPaddingBottom = false, }: BaseModalProps, ref: React.ForwardedRef, ) { @@ -213,7 +214,8 @@ function BaseModal( safeAreaPaddingRight, shouldAddBottomSafeAreaMargin, shouldAddTopSafeAreaMargin, - shouldAddBottomSafeAreaPadding: (!avoidKeyboard || !keyboardStateContextValue?.isKeyboardShown) && shouldAddBottomSafeAreaPadding, + // disableSafeAreaPaddingBottom is used as a temporary solution to disable safe area bottom spacing on modals, to allow edge-to-edge content + shouldAddBottomSafeAreaPadding: !TEMPORARY_disableSafeAreaPaddingBottom && (!avoidKeyboard || !keyboardStateContextValue?.isKeyboardShown) && shouldAddBottomSafeAreaPadding, shouldAddTopSafeAreaPadding, modalContainerStyleMarginTop: modalContainerStyle.marginTop, modalContainerStyleMarginBottom: modalContainerStyle.marginBottom, diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts index 0ed3c91c7a3c..ab8a5df44bf0 100644 --- a/src/components/Modal/types.ts +++ b/src/components/Modal/types.ts @@ -110,6 +110,13 @@ type BaseModalProps = Partial & /** Whether to prevent the focus trap from scrolling the element into view. */ shouldPreventScrollOnFocus?: boolean; + + /** + * Temporary solution to disable safe area bottom spacing on modals, to allow edge-to-edge content + * Modals should not always apply bottom safe area padding, instead it should be applied to the scrollable/bottom-docked content directly. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + TEMPORARY_disableSafeAreaPaddingBottom?: boolean; }; export default BaseModalProps; From 0999a97b0269cfcdbdea440dd5163a5fa736d867 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Thu, 20 Feb 2025 13:08:36 +0100 Subject: [PATCH 14/80] fix: unmodifiedPaddings was removed --- src/components/ScreenWrapper.tsx | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/components/ScreenWrapper.tsx b/src/components/ScreenWrapper.tsx index 7c0de6003966..66440c3bf454 100644 --- a/src/components/ScreenWrapper.tsx +++ b/src/components/ScreenWrapper.tsx @@ -262,28 +262,24 @@ function ScreenWrapper( // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, []); - const {insets, paddingTop, safeAreaPaddingBottomStyle, unmodifiedPaddings} = useStyledSafeAreaInsets(); + const {insets, paddingTop, safeAreaPaddingBottomStyle} = useStyledSafeAreaInsets(); const paddingTopStyle: StyleProp = {}; - const isSafeAreaTopPaddingApplied = includePaddingTop; if (includePaddingTop) { paddingTopStyle.paddingTop = paddingTop; } if (includePaddingTop && ignoreInsetsConsumption) { - paddingTopStyle.paddingTop = unmodifiedPaddings.top; + paddingTopStyle.paddingTop = insets.top; } const bottomContentStyle: StyleProp = { ...safeAreaPaddingBottomStyle, // We always need the safe area padding bottom if we're showing the offline indicator since it is bottom-docked. - ...(includeSafeAreaPaddingBottom && ignoreInsetsConsumption ? {paddingBottom: unmodifiedPaddings.bottom} : {}), + ...(includeSafeAreaPaddingBottom && ignoreInsetsConsumption ? {paddingBottom: insets.bottom} : {}), }; const isAvoidingViewportScroll = useTackInputFocus(isFocused && shouldEnableMaxHeight && shouldAvoidScrollOnVirtualViewport && isMobileWebKit()); - const contextValue = useMemo( - () => ({didScreenTransitionEnd, isSafeAreaTopPaddingApplied, isSafeAreaBottomPaddingApplied: includeSafeAreaPaddingBottom}), - [didScreenTransitionEnd, isSafeAreaTopPaddingApplied], - ); + const contextValue = useMemo(() => ({didScreenTransitionEnd}), [didScreenTransitionEnd]); return ( From 042a464cb244f85620712f339eee83241834ea61 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Thu, 20 Feb 2025 13:13:28 +0100 Subject: [PATCH 15/80] rename temporary flag for bottom safe area handling --- src/components/Modal/BaseModal.tsx | 4 ++-- src/components/Modal/types.ts | 2 +- src/components/SelectionList/BaseSelectionList.tsx | 8 ++++---- src/components/SelectionList/types.ts | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index a7a9aa5ca13e..8a6ec3913e8d 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -75,7 +75,7 @@ function BaseModal( swipeThreshold = 150, swipeDirection, shouldPreventScrollOnFocus = false, - TEMPORARY_disableSafeAreaPaddingBottom = false, + enableEdgeToEdgeBottomSafeAreaPadding = false, }: BaseModalProps, ref: React.ForwardedRef, ) { @@ -215,7 +215,7 @@ function BaseModal( shouldAddBottomSafeAreaMargin, shouldAddTopSafeAreaMargin, // disableSafeAreaPaddingBottom is used as a temporary solution to disable safe area bottom spacing on modals, to allow edge-to-edge content - shouldAddBottomSafeAreaPadding: !TEMPORARY_disableSafeAreaPaddingBottom && (!avoidKeyboard || !keyboardStateContextValue?.isKeyboardShown) && shouldAddBottomSafeAreaPadding, + shouldAddBottomSafeAreaPadding: !enableEdgeToEdgeBottomSafeAreaPadding && (!avoidKeyboard || !keyboardStateContextValue?.isKeyboardShown) && shouldAddBottomSafeAreaPadding, shouldAddTopSafeAreaPadding, modalContainerStyleMarginTop: modalContainerStyle.marginTop, modalContainerStyleMarginBottom: modalContainerStyle.marginBottom, diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts index ab8a5df44bf0..26c71e6f95c3 100644 --- a/src/components/Modal/types.ts +++ b/src/components/Modal/types.ts @@ -116,7 +116,7 @@ type BaseModalProps = Partial & * Modals should not always apply bottom safe area padding, instead it should be applied to the scrollable/bottom-docked content directly. */ // eslint-disable-next-line @typescript-eslint/naming-convention - TEMPORARY_disableSafeAreaPaddingBottom?: boolean; + enableEdgeToEdgeBottomSafeAreaPadding?: boolean; }; export default BaseModalProps; diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index 585a0cf0e719..2972404ce822 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -126,7 +126,7 @@ function BaseSelectionList( listItemTitleContainerStyles, isScreenFocused = false, shouldSubscribeToArrowKeyEvents = true, - TEMPORARY_enableBottomSafeAreaPaddingInContent: enableBottomSafeAreaPaddingInContent = false, + enableEdgeToEdgeBottomSafeAreaPadding = false, }: BaseSelectionListProps, ref: ForwardedRef, ) { @@ -828,12 +828,12 @@ function BaseSelectionList( [footerContent, includeSafeAreaPaddingBottom, isKeyboardShown, safeAreaPaddingBottomStyle], ); const sectionListContentContainerStyle = useMemo(() => { - return enableBottomSafeAreaPaddingInContent ? [paddingBottomStyle, contentContainerStyleProp] : contentContainerStyleProp; - }, [contentContainerStyleProp, enableBottomSafeAreaPaddingInContent, paddingBottomStyle]); + return enableEdgeToEdgeBottomSafeAreaPadding ? [paddingBottomStyle, contentContainerStyleProp] : contentContainerStyleProp; + }, [contentContainerStyleProp, enableEdgeToEdgeBottomSafeAreaPadding, paddingBottomStyle]); // TODO: test _every_ component that uses SelectionList return ( - + {shouldShowTextInput && !shouldShowTextInputAfterHeader && renderInput()} {/* If we are loading new options we will avoid showing any header message. This is mostly because one of the header messages says there are no options. */} {/* This is misleading because we might be in the process of loading fresh options from the server. */} diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 9b1310eb2184..3b5eeaaa56e8 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -676,7 +676,7 @@ type BaseSelectionListProps = Partial & { * This flag can be removed, once all components/screens have switched to this behavior */ // eslint-disable-next-line @typescript-eslint/naming-convention - TEMPORARY_enableBottomSafeAreaPaddingInContent?: boolean; + enableEdgeToEdgeBottomSafeAreaPadding?: boolean; } & TRightHandSideComponent; type SelectionListHandle = { From 5aa0b0412ffe2f8c5766838b208fe757af28ef15 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Thu, 20 Feb 2025 14:32:26 +0100 Subject: [PATCH 16/80] feat: implement `NavBarManager.setBackgroundColor` for Android translucent NavBar --- .../chat/navbar/NavBarManagerModule.kt | 9 +++++++++ src/libs/NavBarManager/index.android.ts | 18 +++++++++++++----- src/libs/NavBarManager/index.ts | 2 ++ src/libs/NavBarManager/types.ts | 4 ++++ .../Navigation/AppNavigator/AuthScreens.tsx | 7 ++++--- src/styles/theme/themes/dark.ts | 1 + src/styles/theme/themes/light.ts | 1 + src/styles/theme/types.ts | 1 + src/types/modules/react-native.d.ts | 3 ++- 9 files changed, 37 insertions(+), 9 deletions(-) diff --git a/android/app/src/main/java/com/expensify/chat/navbar/NavBarManagerModule.kt b/android/app/src/main/java/com/expensify/chat/navbar/NavBarManagerModule.kt index 5c566df606eb..86c600062635 100644 --- a/android/app/src/main/java/com/expensify/chat/navbar/NavBarManagerModule.kt +++ b/android/app/src/main/java/com/expensify/chat/navbar/NavBarManagerModule.kt @@ -24,4 +24,13 @@ class NavBarManagerModule( } } } + + @ReactMethod + fun setBackgroundColor(color: Int) { + UiThreadUtil.runOnUiThread { + mReactContext.currentActivity?.window?.let { + it.window.navigationBarColor = color + } + } + } } diff --git a/src/libs/NavBarManager/index.android.ts b/src/libs/NavBarManager/index.android.ts index 81a4626bfb08..f91a3106b0e9 100644 --- a/src/libs/NavBarManager/index.android.ts +++ b/src/libs/NavBarManager/index.android.ts @@ -1,11 +1,19 @@ -import {NativeModules} from 'react-native'; -import type StartupTimer from './types'; -import type {NavBarButtonStyle} from './types'; +import {NativeModules, processColor} from 'react-native'; +import type NavBarManager from './types'; +import type {} from './types'; -const navBarManager: StartupTimer = { - setButtonStyle: (style: NavBarButtonStyle) => { +const navBarManager: NavBarManager = { + setTheme(theme) { + this.setButtonStyle(theme.navigationBarButtonsStyle); + this.setBackgroundColor(theme.navigationBarBackgroundColor); + }, + setButtonStyle: (style) => { NativeModules.RNNavBarManager.setButtonStyle(style); }, + setBackgroundColor: (color) => { + const colorNumber = processColor(color); + NativeModules.RNNavBarManager.setBackgroundColor(colorNumber); + }, }; export default navBarManager; diff --git a/src/libs/NavBarManager/index.ts b/src/libs/NavBarManager/index.ts index 79c9ef85fdcd..8bb1a3ff5fe5 100644 --- a/src/libs/NavBarManager/index.ts +++ b/src/libs/NavBarManager/index.ts @@ -1,7 +1,9 @@ import type NavBarManager from './types'; const navBarManager: NavBarManager = { + setTheme: () => {}, setButtonStyle: () => {}, + setBackgroundColor: () => {}, }; export default navBarManager; diff --git a/src/libs/NavBarManager/types.ts b/src/libs/NavBarManager/types.ts index 443db391da9d..5d228c0b9ff2 100644 --- a/src/libs/NavBarManager/types.ts +++ b/src/libs/NavBarManager/types.ts @@ -1,7 +1,11 @@ +import type {Color, ThemeColors} from '@styles/theme/types'; + type NavBarButtonStyle = 'light' | 'dark'; type NavBarManager = { + setTheme(theme: ThemeColors): void; setButtonStyle: (style: NavBarButtonStyle) => void; + setBackgroundColor: (color: Color) => void; }; export default NavBarManager; diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 4ed6f22f3f3b..bfc1afc535b7 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -38,6 +38,8 @@ import * as SessionUtils from '@libs/SessionUtils'; import ConnectionCompletePage from '@pages/ConnectionCompletePage'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import DesktopSignInRedirectPage from '@pages/signin/DesktopSignInRedirectPage'; +// eslint-disable-next-line no-restricted-imports +import darkTheme from '@styles/theme/themes/dark'; import * as App from '@userActions/App'; import * as Download from '@userActions/Download'; import * as Modal from '@userActions/Modal'; @@ -237,10 +239,9 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie }, [isOnboardingCompleted]); useEffect(() => { - NavBarManager.setButtonStyle(theme.navigationBarButtonsStyle); - + NavBarManager.setTheme(theme); return () => { - NavBarManager.setButtonStyle(CONST.NAVIGATION_BAR_BUTTONS_STYLE.LIGHT); + NavBarManager.setTheme(darkTheme); }; }, [theme]); diff --git a/src/styles/theme/themes/dark.ts b/src/styles/theme/themes/dark.ts index 4d5894812195..cb45fb4c25d7 100644 --- a/src/styles/theme/themes/dark.ts +++ b/src/styles/theme/themes/dark.ts @@ -154,6 +154,7 @@ const darkTheme = { statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, navigationBarButtonsStyle: CONST.NAVIGATION_BAR_BUTTONS_STYLE.LIGHT, + navigationBarBackgroundColor: `${colors.productDark100}CC`, colorScheme: CONST.COLOR_SCHEME.DARK, } satisfies ThemeColors; diff --git a/src/styles/theme/themes/light.ts b/src/styles/theme/themes/light.ts index 1a187e85b0a2..f7be305abf54 100644 --- a/src/styles/theme/themes/light.ts +++ b/src/styles/theme/themes/light.ts @@ -154,6 +154,7 @@ const lightTheme = { statusBarStyle: CONST.STATUS_BAR_STYLE.DARK_CONTENT, navigationBarButtonsStyle: CONST.NAVIGATION_BAR_BUTTONS_STYLE.DARK, + navigationBarBackgroundColor: `${colors.productLight100}CC`, colorScheme: CONST.COLOR_SCHEME.LIGHT, } satisfies ThemeColors; diff --git a/src/styles/theme/types.ts b/src/styles/theme/types.ts index 9dfb26e611d9..51de2a662f4a 100644 --- a/src/styles/theme/types.ts +++ b/src/styles/theme/types.ts @@ -109,6 +109,7 @@ type ThemeColors = { // e.g. the StatusBar displays either "light-content" or "dark-content" based on the theme statusBarStyle: StatusBarStyle; navigationBarButtonsStyle: NavBarButtonStyle; + navigationBarBackgroundColor: Color; colorScheme: ColorScheme; }; diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts index ae63bf77b2a0..7d79961ebee7 100644 --- a/src/types/modules/react-native.d.ts +++ b/src/types/modules/react-native.d.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/consistent-type-definitions */ -import type {TargetedEvent} from 'react-native'; +import type {ProcessedColorValue, TargetedEvent} from 'react-native'; import type {BootSplashModule} from '@libs/BootSplash/types'; import type {EnvironmentCheckerModule} from '@libs/Environment/betaChecker/types'; import type {ShortcutManagerModule} from '@libs/ShortcutManager'; @@ -17,6 +17,7 @@ type RNTextInputResetModule = { type RNNavBarManagerModule = { setButtonStyle: (style: 'light' | 'dark') => void; + setBackgroundColor: (color: ProcessedColorValue | null | undefined) => void; }; declare module 'react-native' { From 06c73c656f22dfa57c0c376152014253a261e730 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Fri, 21 Feb 2025 13:04:35 +0100 Subject: [PATCH 17/80] fix: implement NavigationBar component and use instead of native NavBarManager module --- .../chat/navbar/NavBarManagerModule.kt | 9 --------- src/App.tsx | 2 ++ src/components/Modal/BaseModal.tsx | 2 ++ .../NavigationBar/index.android.tsx | 20 +++++++++++++++++++ src/components/NavigationBar/index.tsx | 5 +++++ src/hooks/useStyledSafeAreaInsets.ts | 2 +- src/libs/NavBarManager/index.android.ts | 10 +--------- src/libs/NavBarManager/index.ts | 2 -- src/libs/NavBarManager/types.ts | 4 ---- .../Navigation/AppNavigator/AuthScreens.tsx | 6 ++---- src/styles/theme/themes/dark.ts | 2 +- src/styles/theme/themes/light.ts | 2 +- src/types/modules/react-native.d.ts | 6 +++--- 13 files changed, 38 insertions(+), 34 deletions(-) create mode 100644 src/components/NavigationBar/index.android.tsx create mode 100644 src/components/NavigationBar/index.tsx diff --git a/android/app/src/main/java/com/expensify/chat/navbar/NavBarManagerModule.kt b/android/app/src/main/java/com/expensify/chat/navbar/NavBarManagerModule.kt index 86c600062635..5c566df606eb 100644 --- a/android/app/src/main/java/com/expensify/chat/navbar/NavBarManagerModule.kt +++ b/android/app/src/main/java/com/expensify/chat/navbar/NavBarManagerModule.kt @@ -24,13 +24,4 @@ class NavBarManagerModule( } } } - - @ReactMethod - fun setBackgroundColor(color: Int) { - UiThreadUtil.runOnUiThread { - mReactContext.currentActivity?.window?.let { - it.window.navigationBarColor = color - } - } - } } diff --git a/src/App.tsx b/src/App.tsx index f3d37fe87c0b..a2224f7f1026 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -17,6 +17,7 @@ import InitialURLContextProvider from './components/InitialURLContextProvider'; import {InputBlurContextProvider} from './components/InputBlurContext'; import KeyboardProvider from './components/KeyboardProvider'; import {LocaleContextProvider} from './components/LocaleContextProvider'; +import NavigationBar from './components/NavigationBar'; import OnyxProvider from './components/OnyxProvider'; import PopoverContextProvider from './components/PopoverProvider'; import {ProductTrainingContextProvider} from './components/ProductTrainingContext'; @@ -107,6 +108,7 @@ function App({url}: AppProps) { + diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index 8a6ec3913e8d..ae1077ff75d9 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -5,6 +5,7 @@ import ReactNativeModal from 'react-native-modal'; import type {ValueOf} from 'type-fest'; import ColorSchemeWrapper from '@components/ColorSchemeWrapper'; import FocusTrapForModal from '@components/FocusTrap/FocusTrapForModal'; +import NavigationBar from '@components/NavigationBar'; import useKeyboardState from '@hooks/useKeyboardState'; import usePrevious from '@hooks/usePrevious'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; @@ -303,6 +304,7 @@ function BaseModal( + diff --git a/src/components/NavigationBar/index.android.tsx b/src/components/NavigationBar/index.android.tsx new file mode 100644 index 000000000000..0778da5d9e63 --- /dev/null +++ b/src/components/NavigationBar/index.android.tsx @@ -0,0 +1,20 @@ +import {useMemo} from 'react'; +import {View} from 'react-native'; +import useStyledSafeAreaInsets from '@hooks/useStyledSafeAreaInsets'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useTheme from '@hooks/useTheme'; +import CONST from '@src/CONST'; + +function NavigationBar() { + const theme = useTheme(); + const StyleUtils = useStyleUtils(); + const {insets} = useStyledSafeAreaInsets(); + + const navigationBarType = useMemo(() => StyleUtils.getNavigationBarType(insets), [StyleUtils, insets]); + + const isSoftKeyNavigation = navigationBarType === CONST.NAVIGATION_BAR_TYPE.SOFT_KEYS; + + return isSoftKeyNavigation ? : null; +} + +export default NavigationBar; diff --git a/src/components/NavigationBar/index.tsx b/src/components/NavigationBar/index.tsx new file mode 100644 index 000000000000..a1a90a81b349 --- /dev/null +++ b/src/components/NavigationBar/index.tsx @@ -0,0 +1,5 @@ +function NavigationBar() { + return null; +} + +export default NavigationBar; diff --git a/src/hooks/useStyledSafeAreaInsets.ts b/src/hooks/useStyledSafeAreaInsets.ts index 1916790f540e..3c797e2c3d23 100644 --- a/src/hooks/useStyledSafeAreaInsets.ts +++ b/src/hooks/useStyledSafeAreaInsets.ts @@ -33,7 +33,7 @@ import useStyleUtils from './useStyleUtils'; function useStyledSafeAreaInsets() { const StyleUtils = useStyleUtils(); const insets = useSafeAreaInsets(); - const {paddingTop, paddingBottom} = StyleUtils.getPlatformSafeAreaPadding(insets); + const {paddingTop, paddingBottom} = useMemo(() => StyleUtils.getPlatformSafeAreaPadding(insets), [StyleUtils, insets]); const safeAreaPaddingBottomStyle = useMemo(() => ({paddingBottom}), [paddingBottom]); diff --git a/src/libs/NavBarManager/index.android.ts b/src/libs/NavBarManager/index.android.ts index f91a3106b0e9..e56149e35e35 100644 --- a/src/libs/NavBarManager/index.android.ts +++ b/src/libs/NavBarManager/index.android.ts @@ -1,19 +1,11 @@ -import {NativeModules, processColor} from 'react-native'; +import {NativeModules} from 'react-native'; import type NavBarManager from './types'; import type {} from './types'; const navBarManager: NavBarManager = { - setTheme(theme) { - this.setButtonStyle(theme.navigationBarButtonsStyle); - this.setBackgroundColor(theme.navigationBarBackgroundColor); - }, setButtonStyle: (style) => { NativeModules.RNNavBarManager.setButtonStyle(style); }, - setBackgroundColor: (color) => { - const colorNumber = processColor(color); - NativeModules.RNNavBarManager.setBackgroundColor(colorNumber); - }, }; export default navBarManager; diff --git a/src/libs/NavBarManager/index.ts b/src/libs/NavBarManager/index.ts index 8bb1a3ff5fe5..79c9ef85fdcd 100644 --- a/src/libs/NavBarManager/index.ts +++ b/src/libs/NavBarManager/index.ts @@ -1,9 +1,7 @@ import type NavBarManager from './types'; const navBarManager: NavBarManager = { - setTheme: () => {}, setButtonStyle: () => {}, - setBackgroundColor: () => {}, }; export default navBarManager; diff --git a/src/libs/NavBarManager/types.ts b/src/libs/NavBarManager/types.ts index 5d228c0b9ff2..443db391da9d 100644 --- a/src/libs/NavBarManager/types.ts +++ b/src/libs/NavBarManager/types.ts @@ -1,11 +1,7 @@ -import type {Color, ThemeColors} from '@styles/theme/types'; - type NavBarButtonStyle = 'light' | 'dark'; type NavBarManager = { - setTheme(theme: ThemeColors): void; setButtonStyle: (style: NavBarButtonStyle) => void; - setBackgroundColor: (color: Color) => void; }; export default NavBarManager; diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index bfc1afc535b7..9bdb844618de 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -38,8 +38,6 @@ import * as SessionUtils from '@libs/SessionUtils'; import ConnectionCompletePage from '@pages/ConnectionCompletePage'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import DesktopSignInRedirectPage from '@pages/signin/DesktopSignInRedirectPage'; -// eslint-disable-next-line no-restricted-imports -import darkTheme from '@styles/theme/themes/dark'; import * as App from '@userActions/App'; import * as Download from '@userActions/Download'; import * as Modal from '@userActions/Modal'; @@ -239,9 +237,9 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie }, [isOnboardingCompleted]); useEffect(() => { - NavBarManager.setTheme(theme); + NavBarManager.setButtonStyle(theme.navigationBarButtonsStyle); return () => { - NavBarManager.setTheme(darkTheme); + NavBarManager.setButtonStyle(CONST.NAVIGATION_BAR_BUTTONS_STYLE.LIGHT); }; }, [theme]); diff --git a/src/styles/theme/themes/dark.ts b/src/styles/theme/themes/dark.ts index cb45fb4c25d7..725966d487a8 100644 --- a/src/styles/theme/themes/dark.ts +++ b/src/styles/theme/themes/dark.ts @@ -154,7 +154,7 @@ const darkTheme = { statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, navigationBarButtonsStyle: CONST.NAVIGATION_BAR_BUTTONS_STYLE.LIGHT, - navigationBarBackgroundColor: `${colors.productDark100}CC`, + navigationBarBackgroundColor: `${colors.productDark100}CD`, // CD is 80% opacity (80% of 0xFF) colorScheme: CONST.COLOR_SCHEME.DARK, } satisfies ThemeColors; diff --git a/src/styles/theme/themes/light.ts b/src/styles/theme/themes/light.ts index f7be305abf54..add3bf183a42 100644 --- a/src/styles/theme/themes/light.ts +++ b/src/styles/theme/themes/light.ts @@ -154,7 +154,7 @@ const lightTheme = { statusBarStyle: CONST.STATUS_BAR_STYLE.DARK_CONTENT, navigationBarButtonsStyle: CONST.NAVIGATION_BAR_BUTTONS_STYLE.DARK, - navigationBarBackgroundColor: `${colors.productLight100}CC`, + navigationBarBackgroundColor: `${colors.productLight100}CD`, // CD is 80% opacity (80% of 0xFF) colorScheme: CONST.COLOR_SCHEME.LIGHT, } satisfies ThemeColors; diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts index 7d79961ebee7..6639ec4f55af 100644 --- a/src/types/modules/react-native.d.ts +++ b/src/types/modules/react-native.d.ts @@ -1,7 +1,8 @@ /* eslint-disable @typescript-eslint/consistent-type-definitions */ -import type {ProcessedColorValue, TargetedEvent} from 'react-native'; +import type {TargetedEvent} from 'react-native'; import type {BootSplashModule} from '@libs/BootSplash/types'; import type {EnvironmentCheckerModule} from '@libs/Environment/betaChecker/types'; +import type {NavBarButtonStyle} from '@libs/NavBarManager/types'; import type {ShortcutManagerModule} from '@libs/ShortcutManager'; import type StartupTimer from '@libs/StartupTimer/types'; @@ -16,8 +17,7 @@ type RNTextInputResetModule = { }; type RNNavBarManagerModule = { - setButtonStyle: (style: 'light' | 'dark') => void; - setBackgroundColor: (color: ProcessedColorValue | null | undefined) => void; + setButtonStyle: (style: NavBarButtonStyle) => void; }; declare module 'react-native' { From 56e4fdcb3c86acbc9e32fd14289ab8dd39f797fd Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Fri, 21 Feb 2025 13:05:20 +0100 Subject: [PATCH 18/80] fix: ScreenWrapper context properties --- src/components/ScreenWrapper.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/ScreenWrapper.tsx b/src/components/ScreenWrapper.tsx index 66440c3bf454..52c9bdcd19c7 100644 --- a/src/components/ScreenWrapper.tsx +++ b/src/components/ScreenWrapper.tsx @@ -114,8 +114,6 @@ type ScreenWrapperProps = { type ScreenWrapperStatusContextType = { didScreenTransitionEnd: boolean; - isSafeAreaTopPaddingApplied: boolean; - isSafeAreaBottomPaddingApplied: boolean; }; const ScreenWrapperStatusContext = createContext(undefined); From 161e66bbee3daf93e56adc2c8c5611f349ee3a65 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Fri, 21 Feb 2025 13:12:18 +0100 Subject: [PATCH 19/80] revert changes in AuthScreens --- src/libs/Navigation/AppNavigator/AuthScreens.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 9bdb844618de..4ed6f22f3f3b 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -238,6 +238,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie useEffect(() => { NavBarManager.setButtonStyle(theme.navigationBarButtonsStyle); + return () => { NavBarManager.setButtonStyle(CONST.NAVIGATION_BAR_BUTTONS_STYLE.LIGHT); }; From 18958b24e30540581862a26fad4f92d93ea5dd3b Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Fri, 21 Feb 2025 15:52:57 +0100 Subject: [PATCH 20/80] feat: add flag to ScreenWrapper --- src/components/ScreenWrapper.tsx | 81 +++++++++++++++++++++------- src/hooks/useStyledSafeAreaInsets.ts | 41 +++++++++++--- 2 files changed, 97 insertions(+), 25 deletions(-) diff --git a/src/components/ScreenWrapper.tsx b/src/components/ScreenWrapper.tsx index 52c9bdcd19c7..8a2a71a859e2 100644 --- a/src/components/ScreenWrapper.tsx +++ b/src/components/ScreenWrapper.tsx @@ -30,8 +30,6 @@ import ModalContext from './Modal/ModalContext'; import OfflineIndicator from './OfflineIndicator'; import withNavigationFallback from './withNavigationFallback'; -const includeSafeAreaPaddingBottom = false; - type ScreenWrapperChildrenProps = { insets: EdgeInsets; safeAreaPaddingBottomStyle?: { @@ -110,10 +108,19 @@ type ScreenWrapperProps = { /** Overrides the focus trap default settings */ focusTrapSettings?: FocusTrapForScreenProps['focusTrapSettings']; + + /** + * Temporary solution to disable safe area bottom spacing on modals, to allow edge-to-edge content + * Modals should not always apply bottom safe area padding, instead it should be applied to the scrollable/bottom-docked content directly. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + enableEdgeToEdgeBottomSafeAreaPadding?: boolean; }; type ScreenWrapperStatusContextType = { didScreenTransitionEnd: boolean; + isSafeAreaTopPaddingApplied: boolean; + isSafeAreaBottomPaddingApplied: boolean; }; const ScreenWrapperStatusContext = createContext(undefined); @@ -124,6 +131,7 @@ function ScreenWrapper( shouldEnableMinHeight = false, includePaddingTop = true, keyboardAvoidingViewBehavior = 'padding', + includeSafeAreaPaddingBottom: includeSafeAreaPaddingBottomProp = true, shouldEnableKeyboardAvoidingView = true, shouldEnablePickerAvoiding = true, headerGapStyles, @@ -140,6 +148,7 @@ function ScreenWrapper( shouldUseCachedViewportHeight = false, focusTrapSettings, bottomContent, + enableEdgeToEdgeBottomSafeAreaPadding = false, }: ScreenWrapperProps, ref: ForwardedRef, ) { @@ -158,6 +167,8 @@ function ScreenWrapper( const ignoreInsetsConsumption = !useContext(ModalContext).default; const {setRootStatusBarEnabled} = useContext(CustomStatusBarAndBackgroundContext); + const includeSafeAreaPaddingBottom = enableEdgeToEdgeBottomSafeAreaPadding ? false : includeSafeAreaPaddingBottomProp; + // 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 const {isSmallScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout(); @@ -260,24 +271,56 @@ function ScreenWrapper( // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, []); - const {insets, paddingTop, safeAreaPaddingBottomStyle} = useStyledSafeAreaInsets(); - const paddingTopStyle: StyleProp = {}; - - if (includePaddingTop) { - paddingTopStyle.paddingTop = paddingTop; - } - if (includePaddingTop && ignoreInsetsConsumption) { - paddingTopStyle.paddingTop = insets.top; - } + const {insets, paddingTop, paddingBottom, safeAreaPaddingBottomStyle, unmodifiedPaddings} = useStyledSafeAreaInsets(enableEdgeToEdgeBottomSafeAreaPadding); + + const isSafeAreaTopPaddingApplied = includePaddingTop; + const paddingTopStyle: StyleProp = useMemo(() => { + if (includePaddingTop && ignoreInsetsConsumption) { + return {paddingTop: unmodifiedPaddings.top}; + } + if (includePaddingTop) { + return {paddingTop}; + } + return {}; + }, [ignoreInsetsConsumption, includePaddingTop, paddingTop, unmodifiedPaddings.top]); + + const showBottomContent = enableEdgeToEdgeBottomSafeAreaPadding ? !!bottomContent : true; + const bottomContentStyle: StyleProp = useMemo(() => { + const shouldUseUnmodifiedPaddings = includeSafeAreaPaddingBottom && ignoreInsetsConsumption; + let bottomStyle: StyleProp = {}; + + if (enableEdgeToEdgeBottomSafeAreaPadding) { + bottomStyle = safeAreaPaddingBottomStyle; + if (shouldUseUnmodifiedPaddings) { + // eslint-disable-next-line react-compiler/react-compiler + bottomStyle.paddingBottom = unmodifiedPaddings.bottom; + } + return bottomStyle; + } + + if (shouldUseUnmodifiedPaddings) { + return { + paddingBottom: unmodifiedPaddings.bottom, + }; + } + + return { + // We always need the safe area padding bottom if we're showing the offline indicator since it is bottom-docked. + paddingBottom: includeSafeAreaPaddingBottom ? paddingBottom : undefined, + }; + }, [enableEdgeToEdgeBottomSafeAreaPadding, ignoreInsetsConsumption, includeSafeAreaPaddingBottom, paddingBottom, safeAreaPaddingBottomStyle, unmodifiedPaddings.bottom]); - const bottomContentStyle: StyleProp = { - ...safeAreaPaddingBottomStyle, - // We always need the safe area padding bottom if we're showing the offline indicator since it is bottom-docked. - ...(includeSafeAreaPaddingBottom && ignoreInsetsConsumption ? {paddingBottom: insets.bottom} : {}), - }; + const offlineIndicatorContainerStyle = useMemo( + () => + includeSafeAreaPaddingBottom ? [styles.offlineIndicatorMobile] : [styles.offlineIndicatorMobile, {paddingBottom: paddingBottom + styles.offlineIndicatorMobile.paddingBottom}], + [includeSafeAreaPaddingBottom, paddingBottom, styles.offlineIndicatorMobile], + ); const isAvoidingViewportScroll = useTackInputFocus(isFocused && shouldEnableMaxHeight && shouldAvoidScrollOnVirtualViewport && isMobileWebKit()); - const contextValue = useMemo(() => ({didScreenTransitionEnd}), [didScreenTransitionEnd]); + const contextValue = useMemo( + () => ({didScreenTransitionEnd, isSafeAreaTopPaddingApplied, isSafeAreaBottomPaddingApplied: includeSafeAreaPaddingBottom}), + [didScreenTransitionEnd, includeSafeAreaPaddingBottom, isSafeAreaTopPaddingApplied], + ); return ( @@ -320,7 +363,7 @@ function ScreenWrapper( <> {/* Since import state is tightly coupled to the offline state, it is safe to display it when showing offline indicator */} @@ -340,7 +383,7 @@ function ScreenWrapper( - {!!bottomContent && {bottomContent}} + {showBottomContent && {bottomContent}} ); diff --git a/src/hooks/useStyledSafeAreaInsets.ts b/src/hooks/useStyledSafeAreaInsets.ts index 3c797e2c3d23..b1d65d46ea5c 100644 --- a/src/hooks/useStyledSafeAreaInsets.ts +++ b/src/hooks/useStyledSafeAreaInsets.ts @@ -1,4 +1,5 @@ -import {useMemo} from 'react'; +import {useContext, useMemo} from 'react'; +import {ScreenWrapperStatusContext} from '@components/ScreenWrapper'; import useSafeAreaInsets from './useSafeAreaInsets'; import useStyleUtils from './useStyleUtils'; @@ -30,17 +31,45 @@ import useStyleUtils from './useStyleUtils'; * // Use these values to style your component accordingly * } */ -function useStyledSafeAreaInsets() { +function useStyledSafeAreaInsets(enableEdgeToEdgeBottomSafeAreaPadding = false) { const StyleUtils = useStyleUtils(); const insets = useSafeAreaInsets(); const {paddingTop, paddingBottom} = useMemo(() => StyleUtils.getPlatformSafeAreaPadding(insets), [StyleUtils, insets]); - const safeAreaPaddingBottomStyle = useMemo(() => ({paddingBottom}), [paddingBottom]); + const screenWrapperStatusContext = useContext(ScreenWrapperStatusContext); + const isSafeAreaTopPaddingApplied = screenWrapperStatusContext?.isSafeAreaTopPaddingApplied ?? false; + const isSafeAreaBottomPaddingApplied = screenWrapperStatusContext?.isSafeAreaBottomPaddingApplied ?? false; + + const adaptedPaddingBottom = isSafeAreaBottomPaddingApplied ? 0 : paddingBottom; + const safeAreaPaddingBottomStyle = useMemo( + () => ({paddingBottom: enableEdgeToEdgeBottomSafeAreaPadding ? paddingBottom : adaptedPaddingBottom}), + [adaptedPaddingBottom, enableEdgeToEdgeBottomSafeAreaPadding, paddingBottom], + ); + + if (enableEdgeToEdgeBottomSafeAreaPadding) { + return { + paddingTop, + paddingBottom, + unmodifiedPaddings: {}, + insets, + safeAreaPaddingBottomStyle, + }; + } + + const adaptedInsets = { + ...insets, + top: isSafeAreaTopPaddingApplied ? 0 : insets?.top, + bottom: isSafeAreaBottomPaddingApplied ? 0 : insets?.bottom, + }; return { - paddingTop, - paddingBottom, - insets, + paddingTop: isSafeAreaTopPaddingApplied ? 0 : paddingTop, + paddingBottom: adaptedPaddingBottom, + unmodifiedPaddings: { + top: paddingTop, + bottom: paddingBottom, + }, + insets: adaptedInsets, safeAreaPaddingBottomStyle, }; } From 2c420faa0be885d12724fed29c667fe8249e81dd Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Fri, 21 Feb 2025 15:58:47 +0100 Subject: [PATCH 21/80] Update AuthScreens.tsx --- src/libs/Navigation/AppNavigator/AuthScreens.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 4ed6f22f3f3b..ec1c06b2bbaf 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -31,7 +31,7 @@ import Presentation from '@libs/Navigation/PlatformStackNavigation/navigationOpt import type {AuthScreensParamList} from '@libs/Navigation/types'; import NetworkConnection from '@libs/NetworkConnection'; import onyxSubscribe from '@libs/onyxSubscribe'; -import Pusher from '@libs/Pusher'; +import * as Pusher from '@libs/Pusher/pusher'; import PusherConnectionManager from '@libs/PusherConnectionManager'; import * as SearchQueryUtils from '@libs/SearchQueryUtils'; import * as SessionUtils from '@libs/SessionUtils'; From 4f8c568d89c259d012130a7828f7c79fbe8ba222 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Fri, 21 Feb 2025 16:16:03 +0100 Subject: [PATCH 22/80] fix: update comments of flags --- src/components/Modal/types.ts | 4 ++-- src/components/ScreenWrapper.tsx | 6 +++--- src/components/SelectionList/types.ts | 3 +-- src/libs/NavBarManager/index.android.ts | 1 - 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts index 26c71e6f95c3..ddc1e2bbd0e3 100644 --- a/src/components/Modal/types.ts +++ b/src/components/Modal/types.ts @@ -112,10 +112,10 @@ type BaseModalProps = Partial & shouldPreventScrollOnFocus?: boolean; /** - * Temporary solution to disable safe area bottom spacing on modals, to allow edge-to-edge content + * Temporary flag to disable safe area bottom spacing in modals and to allow edge-to-edge content. * Modals should not always apply bottom safe area padding, instead it should be applied to the scrollable/bottom-docked content directly. + * This flag can be removed, once all components/screens have switched to edge-to-edge safe area handling. */ - // eslint-disable-next-line @typescript-eslint/naming-convention enableEdgeToEdgeBottomSafeAreaPadding?: boolean; }; diff --git a/src/components/ScreenWrapper.tsx b/src/components/ScreenWrapper.tsx index 8a2a71a859e2..4ff1e6e69d37 100644 --- a/src/components/ScreenWrapper.tsx +++ b/src/components/ScreenWrapper.tsx @@ -110,10 +110,10 @@ type ScreenWrapperProps = { focusTrapSettings?: FocusTrapForScreenProps['focusTrapSettings']; /** - * Temporary solution to disable safe area bottom spacing on modals, to allow edge-to-edge content - * Modals should not always apply bottom safe area padding, instead it should be applied to the scrollable/bottom-docked content directly. + * Temporary flag to disable safe area bottom spacing in the ScreenWrapper and to allow edge-to-edge content + * The ScreenWrapper should not always apply bottom safe area padding, instead it should be applied to the scrollable/bottom-docked content directly. + * This flag can be removed, once all components/screens have switched to edge-to-edge safe area handling. */ - // eslint-disable-next-line @typescript-eslint/naming-convention enableEdgeToEdgeBottomSafeAreaPadding?: boolean; }; diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 3b5eeaaa56e8..6943818e8fee 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -673,9 +673,8 @@ type BaseSelectionListProps = Partial & { * Temporary flag to enable bottom safe area handling in selection list content (Default: false) * Bottom safe area padding at the moment is handled in the root view of the selection list. * Optimally, we want to add the padding to the scrollable content instead (contentContainerStyle of the ). - * This flag can be removed, once all components/screens have switched to this behavior + * This flag can be removed, once all components/screens have switched to edge-to-edge safe area handling. */ - // eslint-disable-next-line @typescript-eslint/naming-convention enableEdgeToEdgeBottomSafeAreaPadding?: boolean; } & TRightHandSideComponent; diff --git a/src/libs/NavBarManager/index.android.ts b/src/libs/NavBarManager/index.android.ts index e56149e35e35..449c8156494b 100644 --- a/src/libs/NavBarManager/index.android.ts +++ b/src/libs/NavBarManager/index.android.ts @@ -1,6 +1,5 @@ import {NativeModules} from 'react-native'; import type NavBarManager from './types'; -import type {} from './types'; const navBarManager: NavBarManager = { setButtonStyle: (style) => { From 3789062a7fa62940083ed3b87463a5ee370abd05 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Fri, 21 Feb 2025 16:16:25 +0100 Subject: [PATCH 23/80] feat: implement `addBottomSafeAreaPaddingToContent` --- src/components/ScrollView.tsx | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/components/ScrollView.tsx b/src/components/ScrollView.tsx index a61c592015ee..b35955bbf6ba 100644 --- a/src/components/ScrollView.tsx +++ b/src/components/ScrollView.tsx @@ -1,10 +1,32 @@ -import React from 'react'; +import React, {useMemo} from 'react'; import type {ForwardedRef} from 'react'; // eslint-disable-next-line no-restricted-imports -import {ScrollView as RNScrollView} from 'react-native'; -import type {ScrollViewProps} from 'react-native'; +import {ScrollView as RNScrollView, StyleSheet} from 'react-native'; +import type {ScrollViewProps as RNScrollViewProps, StyleProp, ViewStyle} from 'react-native'; +import useStyledSafeAreaInsets from '@hooks/useStyledSafeAreaInsets'; + +type ScrollViewProps = RNScrollViewProps & { + /** + * If enabled, the content will have a bottom padding equal to account for the safe bottom area inset. + */ + addBottomSafeAreaPaddingToContent?: boolean; +}; + +function ScrollView( + {children, scrollIndicatorInsets, contentContainerStyle: contentContainerStyleProp, addBottomSafeAreaPaddingToContent = false, ...props}: ScrollViewProps, + ref: ForwardedRef, +) { + const {paddingBottom: safeAreaPaddingBottom} = useStyledSafeAreaInsets(); + + const contentContainerStyle: StyleProp = useMemo(() => { + if (addBottomSafeAreaPaddingToContent) { + const contentContainerStyleFlattened = StyleSheet.flatten(contentContainerStyleProp); + const paddingBottom = contentContainerStyleFlattened.paddingBottom ?? 0; + return [contentContainerStyleProp, {paddingBottom: typeof paddingBottom === 'number' ? safeAreaPaddingBottom + paddingBottom : safeAreaPaddingBottom}]; + } + return contentContainerStyleProp; + }, [addBottomSafeAreaPaddingToContent, contentContainerStyleProp, safeAreaPaddingBottom]); -function ScrollView({children, scrollIndicatorInsets, ...props}: ScrollViewProps, ref: ForwardedRef) { return ( Date: Fri, 21 Feb 2025 16:39:58 +0100 Subject: [PATCH 24/80] feat: extract contentContainerStyle logic with safe area padding and apply to more components --- src/components/Form/FormProvider.tsx | 5 ++++ src/components/Form/FormWrapper.tsx | 8 +++++++ src/components/ScrollView.tsx | 19 ++++----------- src/components/ScrollViewWithContext.tsx | 3 ++- .../SectionList/BaseSectionList.tsx | 24 +++++++++++++++++++ src/components/SectionList/index.android.tsx | 4 ++-- src/components/SectionList/index.tsx | 4 ++-- src/components/SectionList/types.ts | 10 ++++++-- .../SelectionList/BaseSelectionList.tsx | 12 ++++------ src/components/SelectionList/types.ts | 7 ++---- ...ContainerStyleWithBottomSafeAreaPadding.ts | 19 +++++++++++++++ 11 files changed, 82 insertions(+), 33 deletions(-) create mode 100644 src/components/SectionList/BaseSectionList.tsx create mode 100644 src/hooks/useContentContainerStyleWithBottomSafeAreaPadding.ts diff --git a/src/components/Form/FormProvider.tsx b/src/components/Form/FormProvider.tsx index bf3746b61776..fc47e51cf17a 100644 --- a/src/components/Form/FormProvider.tsx +++ b/src/components/Form/FormProvider.tsx @@ -75,6 +75,11 @@ type FormProviderProps = FormProps {scrollViewContent()} @@ -177,6 +184,7 @@ function FormWrapper({ style={[styles.w100, styles.flex1]} contentContainerStyle={styles.flexGrow1} keyboardShouldPersistTaps="handled" + addBottomSafeAreaPaddingToContent={addBottomSafeAreaPaddingToContent} ref={formRef} > {scrollViewContent()} diff --git a/src/components/ScrollView.tsx b/src/components/ScrollView.tsx index b35955bbf6ba..39b1b61fb9f0 100644 --- a/src/components/ScrollView.tsx +++ b/src/components/ScrollView.tsx @@ -1,9 +1,9 @@ -import React, {useMemo} from 'react'; +import React from 'react'; import type {ForwardedRef} from 'react'; // eslint-disable-next-line no-restricted-imports -import {ScrollView as RNScrollView, StyleSheet} from 'react-native'; -import type {ScrollViewProps as RNScrollViewProps, StyleProp, ViewStyle} from 'react-native'; -import useStyledSafeAreaInsets from '@hooks/useStyledSafeAreaInsets'; +import {ScrollView as RNScrollView} from 'react-native'; +import type {ScrollViewProps as RNScrollViewProps} from 'react-native'; +import useContentContainerStyleWithBottomSafeAreaPadding from '@hooks/useContentContainerStyleWithBottomSafeAreaPadding'; type ScrollViewProps = RNScrollViewProps & { /** @@ -16,16 +16,7 @@ function ScrollView( {children, scrollIndicatorInsets, contentContainerStyle: contentContainerStyleProp, addBottomSafeAreaPaddingToContent = false, ...props}: ScrollViewProps, ref: ForwardedRef, ) { - const {paddingBottom: safeAreaPaddingBottom} = useStyledSafeAreaInsets(); - - const contentContainerStyle: StyleProp = useMemo(() => { - if (addBottomSafeAreaPaddingToContent) { - const contentContainerStyleFlattened = StyleSheet.flatten(contentContainerStyleProp); - const paddingBottom = contentContainerStyleFlattened.paddingBottom ?? 0; - return [contentContainerStyleProp, {paddingBottom: typeof paddingBottom === 'number' ? safeAreaPaddingBottom + paddingBottom : safeAreaPaddingBottom}]; - } - return contentContainerStyleProp; - }, [addBottomSafeAreaPaddingToContent, contentContainerStyleProp, safeAreaPaddingBottom]); + const contentContainerStyle = useContentContainerStyleWithBottomSafeAreaPadding(addBottomSafeAreaPaddingToContent, contentContainerStyleProp); return ( ( + {addBottomSafeAreaPaddingToContent = false, contentContainerStyle: contentContainerStyleProp, ...restProps}: SectionListProps, + ref: SectionListRef, +) { + const contentContainerStyle = useContentContainerStyleWithBottomSafeAreaPadding(addBottomSafeAreaPaddingToContent, contentContainerStyleProp); + + return ( + + ); +} + +BaseSectionList.displayName = 'SectionList'; + +export default forwardRef(BaseSectionList); diff --git a/src/components/SectionList/index.android.tsx b/src/components/SectionList/index.android.tsx index 157e546b3ea9..d9877fd5afca 100644 --- a/src/components/SectionList/index.android.tsx +++ b/src/components/SectionList/index.android.tsx @@ -1,10 +1,10 @@ import React, {forwardRef} from 'react'; -import AnimatedSectionList from './AnimatedSectionList'; +import BaseSectionList from './BaseSectionList'; import type {SectionListProps, SectionListRef} from './types'; function SectionListWithRef(props: SectionListProps, ref: SectionListRef) { return ( - (props: SectionListProps, ref: SectionListRef) { return ( - = RNSectionListProps & { + /** + * If enabled, the content will have a bottom padding equal to account for the safe bottom area inset. + */ + addBottomSafeAreaPaddingToContent?: boolean; +}; -type SectionListProps = SectionListPropsRN; type SectionListRef = ForwardedRef>; export type {SectionListProps, SectionListRef}; diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index 2972404ce822..65fabc4eacdb 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -114,7 +114,7 @@ function BaseSelectionList( listItemWrapperStyle, shouldIgnoreFocus = false, scrollEventThrottle, - contentContainerStyle: contentContainerStyleProp, + contentContainerStyle, shouldHighlightSelectedItem = false, shouldKeepFocusedItemAtTopOfViewableArea = false, shouldDebounceScrolling = false, @@ -126,7 +126,7 @@ function BaseSelectionList( listItemTitleContainerStyles, isScreenFocused = false, shouldSubscribeToArrowKeyEvents = true, - enableEdgeToEdgeBottomSafeAreaPadding = false, + addBottomSafeAreaPaddingToContent = false, }: BaseSelectionListProps, ref: ForwardedRef, ) { @@ -827,13 +827,10 @@ function BaseSelectionList( () => (!isKeyboardShown || !!footerContent) && includeSafeAreaPaddingBottom && safeAreaPaddingBottomStyle, [footerContent, includeSafeAreaPaddingBottom, isKeyboardShown, safeAreaPaddingBottomStyle], ); - const sectionListContentContainerStyle = useMemo(() => { - return enableEdgeToEdgeBottomSafeAreaPadding ? [paddingBottomStyle, contentContainerStyleProp] : contentContainerStyleProp; - }, [contentContainerStyleProp, enableEdgeToEdgeBottomSafeAreaPadding, paddingBottomStyle]); // TODO: test _every_ component that uses SelectionList return ( - + {shouldShowTextInput && !shouldShowTextInputAfterHeader && renderInput()} {/* If we are loading new options we will avoid showing any header message. This is mostly because one of the header messages says there are no options. */} {/* This is misleading because we might be in the process of loading fresh options from the server. */} @@ -889,7 +886,8 @@ function BaseSelectionList( onEndReached={onEndReached} onEndReachedThreshold={onEndReachedThreshold} scrollEventThrottle={scrollEventThrottle} - contentContainerStyle={sectionListContentContainerStyle} + addBottomSafeAreaPaddingToContent={addBottomSafeAreaPaddingToContent} + contentContainerStyle={contentContainerStyle} CellRendererComponent={shouldPreventActiveCellVirtualization ? FocusAwareCellRendererComponent : undefined} /> {children} diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 6943818e8fee..6c01d1bbdb4c 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -670,12 +670,9 @@ type BaseSelectionListProps = Partial & { isScreenFocused?: boolean; /** - * Temporary flag to enable bottom safe area handling in selection list content (Default: false) - * Bottom safe area padding at the moment is handled in the root view of the selection list. - * Optimally, we want to add the padding to the scrollable content instead (contentContainerStyle of the ). - * This flag can be removed, once all components/screens have switched to edge-to-edge safe area handling. + * If enabled, the content will have a bottom padding equal to account for the safe bottom area inset. */ - enableEdgeToEdgeBottomSafeAreaPadding?: boolean; + addBottomSafeAreaPaddingToContent?: boolean; } & TRightHandSideComponent; type SelectionListHandle = { diff --git a/src/hooks/useContentContainerStyleWithBottomSafeAreaPadding.ts b/src/hooks/useContentContainerStyleWithBottomSafeAreaPadding.ts new file mode 100644 index 000000000000..e792a210d803 --- /dev/null +++ b/src/hooks/useContentContainerStyleWithBottomSafeAreaPadding.ts @@ -0,0 +1,19 @@ +import {useMemo} from 'react'; +import {StyleSheet} from 'react-native'; +import type {StyleProp, ViewStyle} from 'react-native'; +import useStyledSafeAreaInsets from './useStyledSafeAreaInsets'; + +function useContentContainerStyleWithBottomSafeAreaPadding(addBottomSafeAreaPaddingToContent: boolean, contentContainerStyleProp: StyleProp) { + const {paddingBottom: safeAreaPaddingBottom} = useStyledSafeAreaInsets(); + + return useMemo>(() => { + if (addBottomSafeAreaPaddingToContent) { + const contentContainerStyleFlattened = StyleSheet.flatten(contentContainerStyleProp); + const paddingBottom = contentContainerStyleFlattened.paddingBottom ?? 0; + return [contentContainerStyleProp, {paddingBottom: typeof paddingBottom === 'number' ? safeAreaPaddingBottom + paddingBottom : safeAreaPaddingBottom}]; + } + return contentContainerStyleProp; + }, [addBottomSafeAreaPaddingToContent, contentContainerStyleProp, safeAreaPaddingBottom]); +} + +export default useContentContainerStyleWithBottomSafeAreaPadding; From 4cf3a7c8a037bc70cd554f1ad9fbb04fa437f325 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Fri, 21 Feb 2025 17:10:26 +0100 Subject: [PATCH 25/80] fix: ESLint and TS errors --- src/components/PopoverWithoutOverlay/index.tsx | 8 ++++---- src/libs/Navigation/AppNavigator/AuthScreens.tsx | 2 +- src/pages/ErrorPage/UpdateRequiredView.tsx | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/PopoverWithoutOverlay/index.tsx b/src/components/PopoverWithoutOverlay/index.tsx index 28a3f0a85bc1..5dfaddcab2df 100644 --- a/src/components/PopoverWithoutOverlay/index.tsx +++ b/src/components/PopoverWithoutOverlay/index.tsx @@ -7,8 +7,8 @@ import useSafeAreaInsets from '@hooks/useSafeAreaInsets'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; +import {onModalDidClose, setCloseModal, willAlertModalBecomeVisible} from '@libs/actions/Modal'; import variables from '@styles/variables'; -import * as Modal from '@userActions/Modal'; import viewRef from '@src/types/utils/viewRef'; import type PopoverWithoutOverlayProps from './types'; @@ -54,13 +54,13 @@ function PopoverWithoutOverlay( close: onClose, anchorRef, }); - removeOnClose = Modal.setCloseModal(onClose); + removeOnClose = setCloseModal(onClose); } else { onModalHide(); close(anchorRef); - Modal.onModalDidClose(); + onModalDidClose(); } - Modal.willAlertModalBecomeVisible(isVisible, true); + willAlertModalBecomeVisible(isVisible, true); return () => { if (!removeOnClose) { diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index ec1c06b2bbaf..4ed6f22f3f3b 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -31,7 +31,7 @@ import Presentation from '@libs/Navigation/PlatformStackNavigation/navigationOpt import type {AuthScreensParamList} from '@libs/Navigation/types'; import NetworkConnection from '@libs/NetworkConnection'; import onyxSubscribe from '@libs/onyxSubscribe'; -import * as Pusher from '@libs/Pusher/pusher'; +import Pusher from '@libs/Pusher'; import PusherConnectionManager from '@libs/PusherConnectionManager'; import * as SearchQueryUtils from '@libs/SearchQueryUtils'; import * as SessionUtils from '@libs/SessionUtils'; diff --git a/src/pages/ErrorPage/UpdateRequiredView.tsx b/src/pages/ErrorPage/UpdateRequiredView.tsx index c4f1272654fd..d318b16cd050 100644 --- a/src/pages/ErrorPage/UpdateRequiredView.tsx +++ b/src/pages/ErrorPage/UpdateRequiredView.tsx @@ -12,7 +12,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useSafeAreaInsets from '@hooks/useSafeAreaInsets'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as AppUpdate from '@libs/actions/AppUpdate'; +import {updateApp} from '@libs/actions/AppUpdate/'; function UpdateRequiredView() { const insets = useSafeAreaInsets(); @@ -56,7 +56,7 @@ function UpdateRequiredView() {