From d72affc684b69cb0583881bdbf86afe8b2cecab8 Mon Sep 17 00:00:00 2001 From: GCyganek Date: Wed, 22 Jul 2026 16:18:48 +0200 Subject: [PATCH 1/2] Fix Contact list modal broken in landscape mode --- .../EmojiPickerMenu/useEmojiPickerMenu.ts | 8 ++++--- src/hooks/useReportSubmitToPopover.tsx | 21 ++++++++++++------- src/pages/ReportSubmitToContent.tsx | 15 +++++++------ .../calculateModalHeightInLandscapeMode.ts | 14 +++++++++++++ 4 files changed, 39 insertions(+), 19 deletions(-) create mode 100644 src/utils/calculateModalHeightInLandscapeMode.ts diff --git a/src/components/EmojiPicker/EmojiPickerMenu/useEmojiPickerMenu.ts b/src/components/EmojiPicker/EmojiPickerMenu/useEmojiPickerMenu.ts index fcaa1727506e..cb3c2901ca9a 100644 --- a/src/components/EmojiPicker/EmojiPickerMenu/useEmojiPickerMenu.ts +++ b/src/components/EmojiPicker/EmojiPickerMenu/useEmojiPickerMenu.ts @@ -4,6 +4,7 @@ import useKeyboardState from '@hooks/useKeyboardState'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePreferredEmojiSkinTone from '@hooks/usePreferredEmojiSkinTone'; +import useSafeAreaInsets from '@hooks/useSafeAreaInsets'; import useStyleUtils from '@hooks/useStyleUtils'; import useWindowDimensions from '@hooks/useWindowDimensions'; @@ -11,8 +12,8 @@ import type {EmojiPickerList, EmojiPickerListItem} from '@libs/EmojiUtils'; import {getHeaderEmojis, getSpacersIndexes, mergeEmojisWithFrequentlyUsedEmojis, processFrequentlyUsedEmojis, suggestEmojis} from '@libs/EmojiUtils'; import isInLandscapeModeUtil from '@libs/isInLandscapeMode'; -import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import calculateModalHeightInLandscapeMode from '@src/utils/calculateModalHeightInLandscapeMode'; import type {FlashListRef} from '@shopify/flash-list'; @@ -33,8 +34,9 @@ const useEmojiPickerMenu = () => { const [preferredSkinTone] = usePreferredEmojiSkinTone(); const {windowHeight, windowWidth} = useWindowDimensions(); const StyleUtils = useStyleUtils(); - const {keyboardHeight} = useKeyboardState(); + const {keyboardActiveHeight} = useKeyboardState(); const isInLandscapeMode = isInLandscapeModeUtil(windowWidth, windowHeight); + const {top: topSafeAreaInset} = useSafeAreaInsets(); /** * The EmojiPicker sets the `innerContainerStyle` with `maxHeight: '95%'` in `styles.popoverInnerContainer` @@ -44,7 +46,7 @@ const useEmojiPickerMenu = () => { */ const listStyle = StyleUtils.getEmojiPickerListHeight( isListFiltered, - windowHeight * (isInLandscapeMode ? CONST.MODAL_MAX_HEIGHT_TO_WINDOW_HEIGHT_RATIO_LANDSCAPE_MODE : 0.95) - keyboardHeight, + isInLandscapeMode ? calculateModalHeightInLandscapeMode(windowHeight, topSafeAreaInset, keyboardActiveHeight) : (windowHeight - topSafeAreaInset) * 0.95 - keyboardActiveHeight, ); useEffect(() => { diff --git a/src/hooks/useReportSubmitToPopover.tsx b/src/hooks/useReportSubmitToPopover.tsx index 02d73ae3490c..0196f3406729 100644 --- a/src/hooks/useReportSubmitToPopover.tsx +++ b/src/hooks/useReportSubmitToPopover.tsx @@ -9,6 +9,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {personalDetailsLoginSelector} from '@src/selectors/PersonalDetails'; import type AnchorAlignment from '@src/types/utils/AnchorAlignment'; +import calculateModalHeightInLandscapeMode from '@src/utils/calculateModalHeightInLandscapeMode'; import type {RefObject} from 'react'; @@ -17,10 +18,12 @@ import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {View} from 'react-native'; import useIsInLandscapeMode from './useIsInLandscapeMode'; +import useKeyboardState from './useKeyboardState'; import useOnyx from './useOnyx'; import usePopoverPosition from './usePopoverPosition'; import usePrevious from './usePrevious'; import useResponsiveLayout from './useResponsiveLayout'; +import useSafeAreaInsets from './useSafeAreaInsets'; import useStyleUtils from './useStyleUtils'; import useThemeStyles from './useThemeStyles'; import useViewportOffsetTop from './useViewportOffsetTop'; @@ -60,15 +63,18 @@ function useReportSubmitToPopover({reportID, onSubmitSuccess, anchorAlignment = // Bottom-docked Modal path only; aligns with Popover path that omits modal shell padding chrome // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth const {isSmallScreenWidth} = useResponsiveLayout(); - const isBottomDockedInLandscape = isSmallScreenWidth && isInLandscapeMode; + const {keyboardActiveHeight} = useKeyboardState(); + const {top: topSafeAreaInset} = useSafeAreaInsets(); const submitToPopoverContentHeight = useMemo(() => { - if (!isBottomDockedInLandscape) { + if (!isInLandscapeMode) { return popoverDimensions.height; } - return Math.min(popoverDimensions.height, windowHeight * CONST.MODAL_MAX_HEIGHT_TO_WINDOW_HEIGHT_RATIO_LANDSCAPE_MODE); - }, [isBottomDockedInLandscape, windowHeight]); + const contentHeightLandscapeMode = calculateModalHeightInLandscapeMode(windowHeight, topSafeAreaInset, keyboardActiveHeight); + + return Math.min(popoverDimensions.height, contentHeightLandscapeMode); + }, [isInLandscapeMode, windowHeight, keyboardActiveHeight, topSafeAreaInset]); const anchorRef = useRef(null); const oneShotOnSubmitSuccessRef = useRef<(() => void) | undefined>(undefined); const onSubmitWithManagerEmailRef = useRef(undefined); @@ -227,9 +233,9 @@ function useReportSubmitToPopover({reportID, onSubmitSuccess, anchorAlignment = const innerContainerStyle = useMemo( () => ({ ...popoverContainerStyle, - ...(isBottomDockedInLandscape ? styles.getPopoverMaxHeight(windowHeight, true) : {minHeight: popoverDimensions.minHeight}), + ...(isInLandscapeMode ? styles.getPopoverMaxHeight(windowHeight, true) : {minHeight: popoverDimensions.minHeight}), }), - [popoverContainerStyle, isBottomDockedInLandscape, windowHeight, styles], + [popoverContainerStyle, isInLandscapeMode, windowHeight, styles], ); const reportSubmitToPopover = useMemo(() => { @@ -259,7 +265,7 @@ function useReportSubmitToPopover({reportID, onSubmitSuccess, anchorAlignment = > ({ - showButton: true, + showButton: !keyboardActiveHeight || !isInLandscapeMode, text: translate('common.confirm'), onConfirm: handleSubmit, confirmButtonSize: 'medium' as const, }), - [handleSubmit, translate], + [handleSubmit, translate, keyboardActiveHeight, isInLandscapeMode], ); const containerStyle = useMemo(() => { const baseStyle = [styles.w100, styles.flex1, styles.pt3, styles.pb3]; - if (isBottomDockedInLandscape) { + if (isInLandscapeMode) { return baseStyle; } return [...baseStyle, StyleUtils.getMinimumHeight(CONST.POPOVER_REPORT_SUBMIT_TO_CONTENT_HEIGHT)]; - }, [StyleUtils, isBottomDockedInLandscape, styles.flex1, styles.pb3, styles.pt3, styles.w100]); + }, [StyleUtils, isInLandscapeMode, styles.flex1, styles.pb3, styles.pt3, styles.w100]); if (shouldShowNotFoundView) { return ( diff --git a/src/utils/calculateModalHeightInLandscapeMode.ts b/src/utils/calculateModalHeightInLandscapeMode.ts new file mode 100644 index 000000000000..b7fc6e1c9f4b --- /dev/null +++ b/src/utils/calculateModalHeightInLandscapeMode.ts @@ -0,0 +1,14 @@ +import CONST from '@src/CONST'; + +// Calculates the height of a modal in landscape mode based on the window height and keyboard state. +function calculateModalHeightInLandscapeMode(windowHeight: number, topSafeAreaInset: number, keyboardActiveHeight: number) { + const availableWindowHeight = windowHeight - topSafeAreaInset; + + if (keyboardActiveHeight > 0) { + return availableWindowHeight - keyboardActiveHeight; + } + + return windowHeight * CONST.MODAL_MAX_HEIGHT_TO_WINDOW_HEIGHT_RATIO_LANDSCAPE_MODE; +} + +export default calculateModalHeightInLandscapeMode; From 0ba9d336a7400c6cb80733b8bf3b52ff2e55a2fc Mon Sep 17 00:00:00 2001 From: GCyganek Date: Mon, 27 Jul 2026 12:03:42 +0200 Subject: [PATCH 2/2] Fix popover height --- src/components/withKeyboardState.tsx | 2 ++ src/hooks/useReportSubmitToPopover.tsx | 19 ++++++++++--------- src/pages/ReportSubmitToContent.tsx | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/components/withKeyboardState.tsx b/src/components/withKeyboardState.tsx index c08da225353d..40235e171e83 100755 --- a/src/components/withKeyboardState.tsx +++ b/src/components/withKeyboardState.tsx @@ -50,6 +50,8 @@ function KeyboardStateProvider({children}: ChildrenProps): ReactElement | null { const keyboardDidHideListener = KeyboardEvents.addListener('keyboardDidHide', () => { setKeyboardHeight(0); setIsKeyboardActive(false); + // Sometimes 'keyboardWillHide' is not called (popover closed when keyboard is open), in this case we don't want stale keyboardActiveHeight value + setKeyboardActiveHeight(0); }); const keyboardWillShowListener = KeyboardEvents.addListener('keyboardWillShow', (e) => { setIsKeyboardActive(true); diff --git a/src/hooks/useReportSubmitToPopover.tsx b/src/hooks/useReportSubmitToPopover.tsx index 0196f3406729..37f4117d2e94 100644 --- a/src/hooks/useReportSubmitToPopover.tsx +++ b/src/hooks/useReportSubmitToPopover.tsx @@ -265,7 +265,7 @@ function useReportSubmitToPopover({reportID, onSubmitSuccess, anchorAlignment = > ); }, [ - StyleUtils, - styles.flexColumn, - styles.pt4, - styles.w100, + shouldRenderSubmitToPopover, innerContainerStyle, outerStyle, - submitToPopoverContentHeight, - shouldRenderSubmitToPopover, isVisible, closeReportSubmitToPopover, handleReportSubmitToPopoverModalHide, anchorPosition, anchorAlignment, - anchorRef, + StyleUtils, + submitToPopoverContentHeight, + styles.flexColumn, + styles.flex1, + styles.w100, + styles.pt4, + isInLandscapeMode, + submitToContentKey, report, policy, isLoadingReportData, handleCombinedSubmitSuccess, isSearchSubmitFlow, handleSearchSubmitWithManagerEmail, - submitToContentKey, ]); return { diff --git a/src/pages/ReportSubmitToContent.tsx b/src/pages/ReportSubmitToContent.tsx index fbf94e73c194..a83fb4fa89b0 100644 --- a/src/pages/ReportSubmitToContent.tsx +++ b/src/pages/ReportSubmitToContent.tsx @@ -444,7 +444,7 @@ function ReportSubmitToContent({ isRowMultilineSupported style={{containerStyle: styles.flex1}} disableMaintainingScrollPosition - addBottomSafeAreaPadding + addBottomSafeAreaPadding={!isInLandscapeMode} > {hasError && (