diff --git a/src/CONST/index.ts b/src/CONST/index.ts index d788ddda9b06..35f5d9e2a35e 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -8830,6 +8830,9 @@ const CONST = { SEND_BUTTON: 'AttachmentModal-SendButton', IMAGE_ZOOM: 'AttachmentModal-ImageZoom', }, + MODAL: { + DISMISS_DIALOG: 'Modal-DismissDialog', + }, ATTACHMENT_PREVIEW: { VIDEO_THUMBNAIL: 'AttachmentPreview-VideoThumbnail', IMAGE_THUMBNAIL: 'AttachmentPreview-ImageThumbnail', diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index 967e5ab7f791..16cd10802baa 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -1,13 +1,15 @@ import React, {useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'; -import type {LayoutChangeEvent} from 'react-native'; +import type {GestureResponderEvent, LayoutChangeEvent} from 'react-native'; // Animated required for side panel navigation // eslint-disable-next-line no-restricted-imports import {Animated, View} from 'react-native'; import ColorSchemeWrapper from '@components/ColorSchemeWrapper'; import NavigationBar from '@components/NavigationBar'; +import {PressableWithoutFeedback} from '@components/Pressable'; import ScreenWrapperOfflineIndicatorContext from '@components/ScreenWrapper/ScreenWrapperOfflineIndicatorContext'; import ScrollView from '@components/ScrollView'; import useKeyboardState from '@hooks/useKeyboardState'; +import useLocalize from '@hooks/useLocalize'; import usePrevious from '@hooks/usePrevious'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useSafeAreaInsets from '@hooks/useSafeAreaInsets'; @@ -78,6 +80,7 @@ function BaseModal({ const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); + const {translate} = useLocalize(); const {windowWidth, windowHeight} = useWindowDimensions(); // We need to use isSmallScreenWidth instead of shouldUseNarrowLayout to apply correct modal width const canUseTouchScreen = canUseTouchScreenCheck(); @@ -95,6 +98,7 @@ function BaseModal({ const shouldCallHideModalOnUnmount = useRef(false); const hideModalCallbackRef = useRef<(callHideCallback: boolean) => void>(undefined); + const bottomDockedDismissButtonRef = useRef(null); const wasVisible = usePrevious(isVisible); @@ -177,8 +181,8 @@ function BaseModal({ onModalShow(); }, [onModalShow, shouldSetModalVisibility, type]); - const handleBackdropPress = (e?: KeyboardEvent) => { - if (e?.key === CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey) { + const handleBackdropPress = (e?: GestureResponderEvent | KeyboardEvent) => { + if (e && 'key' in e && e.key === CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey) { return; } @@ -311,6 +315,8 @@ function BaseModal({ const isBottomDockedModalInLandscapeMode = type === CONST.MODAL.MODAL_TYPE.BOTTOM_DOCKED && isInLandscapeMode; const shouldWrapChildrenInScrollView = shouldWrapModalChildrenInScrollViewIfBottomDockedInLandscapeMode && isBottomDockedModalInLandscapeMode; + const shouldShowBottomDockedDismissButton = isSmallScreenWidth && type === CONST.MODAL.MODAL_TYPE.BOTTOM_DOCKED && !!(onBackdropPress ?? onClose); + const modalInitialFocus = shouldShowBottomDockedDismissButton ? () => bottomDockedDismissButtonRef.current : initialFocus; return ( @@ -348,7 +354,7 @@ function BaseModal({ onSwipeComplete={onClose} swipeDirection={swipeDirection} shouldPreventScrollOnFocus={shouldPreventScrollOnFocus} - initialFocus={initialFocus} + initialFocus={modalInitialFocus} swipeThreshold={swipeThreshold} isVisible={isVisible} backdropColor={theme.overlay} @@ -389,6 +395,19 @@ function BaseModal({ ref={ref} fsClass={forwardedFSClass} > + {shouldShowBottomDockedDismissButton && ( + + + + )} {shouldWrapChildrenInScrollView ? {children} : children} {!keyboardStateContextValue?.isKeyboardActive && } diff --git a/src/styles/index.ts b/src/styles/index.ts index 9a33de715ddb..0457091a61ee 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -3649,6 +3649,16 @@ const staticStyles = (theme: ThemeColors) => backgroundColor: theme.overlay, }, + bottomDockedModalDismissButton: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + height: variables.iconSizeXSmall, + backgroundColor: theme.transparent, + zIndex: 1, + }, + invisibleOverlay: { backgroundColor: theme.transparent, zIndex: 1000,