From eebfbf1a08cf5d2f3bf18964d5986a3404c8303a Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Fri, 20 Oct 2023 13:41:21 +0200 Subject: [PATCH 01/25] migrate withWindowDimensions directory to TypeScript --- .../{index.native.js => index.native.tsx} | 55 +++++++++---------- .../{index.js => index.tsx} | 54 +++++++++--------- src/components/withWindowDimensions/types.ts | 30 ++++++++++ 3 files changed, 82 insertions(+), 57 deletions(-) rename src/components/withWindowDimensions/{index.native.js => index.native.tsx} (65%) rename src/components/withWindowDimensions/{index.js => index.tsx} (68%) create mode 100644 src/components/withWindowDimensions/types.ts diff --git a/src/components/withWindowDimensions/index.native.js b/src/components/withWindowDimensions/index.native.tsx similarity index 65% rename from src/components/withWindowDimensions/index.native.js rename to src/components/withWindowDimensions/index.native.tsx index 363196b3fd4d..889397f2bab6 100644 --- a/src/components/withWindowDimensions/index.native.js +++ b/src/components/withWindowDimensions/index.native.tsx @@ -1,12 +1,14 @@ -import React, {forwardRef, createContext, useState, useEffect, useMemo} from 'react'; +import React, {createContext, useState, useEffect, useMemo, ComponentType, RefAttributes, ForwardedRef} from 'react'; import PropTypes from 'prop-types'; -import {Dimensions} from 'react-native'; +import {Dimensions, ScaledSize} from 'react-native'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; import getComponentDisplayName from '../../libs/getComponentDisplayName'; import variables from '../../styles/variables'; import getWindowHeightAdjustment from '../../libs/getWindowHeightAdjustment'; +import {WindowDimensionsContextData, WindowDimensionsProps} from './types'; +import ChildrenProps from '../../types/utils/ChildrenProps'; -const WindowDimensionsContext = createContext(null); +const WindowDimensionsContext = createContext(null); const windowDimensionsPropTypes = { // Width of the window windowWidth: PropTypes.number.isRequired, @@ -27,12 +29,7 @@ const windowDimensionsPropTypes = { isLargeScreenWidth: PropTypes.bool.isRequired, }; -const windowDimensionsProviderPropTypes = { - /* Actual content wrapped by this component */ - children: PropTypes.node.isRequired, -}; - -function WindowDimensionsProvider(props) { +function WindowDimensionsProvider(props: ChildrenProps) { const [windowDimension, setWindowDimension] = useState(() => { const initialDimensions = Dimensions.get('window'); return { @@ -42,9 +39,8 @@ function WindowDimensionsProvider(props) { }); useEffect(() => { - const onDimensionChange = (newDimensions) => { + const onDimensionChange = (newDimensions: {window: ScaledSize}) => { const {window} = newDimensions; - setWindowDimension({ windowHeight: window.height, windowWidth: window.width, @@ -76,30 +72,31 @@ function WindowDimensionsProvider(props) { return {props.children}; } -WindowDimensionsProvider.propTypes = windowDimensionsProviderPropTypes; WindowDimensionsProvider.displayName = 'WindowDimensionsProvider'; /** - * @param {React.Component} WrappedComponent - * @returns {React.Component} + * @param WrappedComponent + * @returns */ -export default function withWindowDimensions(WrappedComponent) { - const WithWindowDimensions = forwardRef((props, ref) => ( - - {(windowDimensionsProps) => ( - - )} - - )); +export default function withWindowDimensions(WrappedComponent: ComponentType>) { + function WithWindowDimensions(props: Omit, ref: ForwardedRef) { + return ( + + {(windowDimensionsProps) => ( + + )} + + ); + } WithWindowDimensions.displayName = `withWindowDimensions(${getComponentDisplayName(WrappedComponent)})`; - return WithWindowDimensions; + return React.forwardRef(WithWindowDimensions); } export {WindowDimensionsProvider, windowDimensionsPropTypes}; diff --git a/src/components/withWindowDimensions/index.js b/src/components/withWindowDimensions/index.tsx similarity index 68% rename from src/components/withWindowDimensions/index.js rename to src/components/withWindowDimensions/index.tsx index 16e5985e0985..6a5dfadc6a49 100644 --- a/src/components/withWindowDimensions/index.js +++ b/src/components/withWindowDimensions/index.tsx @@ -1,13 +1,15 @@ -import React, {forwardRef, createContext, useState, useEffect, useMemo} from 'react'; +import React, {createContext, useState, useEffect, useMemo, RefAttributes, ComponentType, ForwardedRef} from 'react'; import PropTypes from 'prop-types'; import lodashDebounce from 'lodash/debounce'; -import {Dimensions} from 'react-native'; +import {Dimensions, ScaledSize} from 'react-native'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; import getComponentDisplayName from '../../libs/getComponentDisplayName'; import variables from '../../styles/variables'; import getWindowHeightAdjustment from '../../libs/getWindowHeightAdjustment'; +import ChildrenProps from '../../types/utils/ChildrenProps'; +import {WindowDimensionsContextData, WindowDimensionsProps} from './types'; -const WindowDimensionsContext = createContext(null); +const WindowDimensionsContext = createContext(null); const windowDimensionsPropTypes = { // Width of the window windowWidth: PropTypes.number.isRequired, @@ -28,12 +30,7 @@ const windowDimensionsPropTypes = { isLargeScreenWidth: PropTypes.bool.isRequired, }; -const windowDimensionsProviderPropTypes = { - /* Actual content wrapped by this component */ - children: PropTypes.node.isRequired, -}; - -function WindowDimensionsProvider(props) { +function WindowDimensionsProvider(props: ChildrenProps) { const [windowDimension, setWindowDimension] = useState(() => { const initialDimensions = Dimensions.get('window'); return { @@ -43,7 +40,7 @@ function WindowDimensionsProvider(props) { }); useEffect(() => { - const onDimensionChange = (newDimensions) => { + const onDimensionChange = (newDimensions: {window: ScaledSize}) => { const {window} = newDimensions; setWindowDimension({ windowHeight: window.height, @@ -81,30 +78,31 @@ function WindowDimensionsProvider(props) { return {props.children}; } -WindowDimensionsProvider.propTypes = windowDimensionsProviderPropTypes; WindowDimensionsProvider.displayName = 'WindowDimensionsProvider'; /** - * @param {React.Component} WrappedComponent - * @returns {React.Component} + * @param WrappedComponent + * @returns */ -export default function withWindowDimensions(WrappedComponent) { - const WithWindowDimensions = forwardRef((props, ref) => ( - - {(windowDimensionsProps) => ( - - )} - - )); +export default function withWindowDimensions(WrappedComponent: ComponentType>) { + function WithWindowDimensions(props: Omit, ref: ForwardedRef) { + return ( + + {(windowDimensionsProps) => ( + + )} + + ); + } WithWindowDimensions.displayName = `withWindowDimensions(${getComponentDisplayName(WrappedComponent)})`; - return WithWindowDimensions; + return React.forwardRef(WithWindowDimensions); } export {WindowDimensionsProvider, windowDimensionsPropTypes}; diff --git a/src/components/withWindowDimensions/types.ts b/src/components/withWindowDimensions/types.ts new file mode 100644 index 000000000000..994c10fa4a52 --- /dev/null +++ b/src/components/withWindowDimensions/types.ts @@ -0,0 +1,30 @@ +type WindowDimensionsContextData = { + windowHeight: number; + windowWidth: number; + isExtraSmallScreenWidth: boolean; + isSmallScreenWidth: boolean; + isMediumScreenWidth: boolean; + isLargeScreenWidth: boolean; +}; + +type WindowDimensionsProps = WindowDimensionsContextData & { + // Width of the window + windowWidth: number; + + // Height of the window + windowHeight: number; + + // Is the window width extra narrow, like on a Fold mobile device? + isExtraSmallScreenWidth: boolean; + + // Is the window width narrow, like on a mobile device? + isSmallScreenWidth: boolean; + + // Is the window width medium sized, like on a tablet device? + isMediumScreenWidth: boolean; + + // Is the window width wide, like on a browser or desktop? + isLargeScreenWidth: boolean; +}; + +export type {WindowDimensionsContextData, WindowDimensionsProps}; From 8213caad111c8c59a26ccf3f92d967dc9debe2ab Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Fri, 20 Oct 2023 13:41:42 +0200 Subject: [PATCH 02/25] create types file for Modal --- src/components/Modal/types.ts | 69 +++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/components/Modal/types.ts diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts new file mode 100644 index 000000000000..b17f794374e6 --- /dev/null +++ b/src/components/Modal/types.ts @@ -0,0 +1,69 @@ +import {ValueOf} from 'type-fest'; +import {ModalProps} from 'react-native-modal'; +import ChildrenProps from '../../types/utils/ChildrenProps'; +import {WindowDimensionsProps} from '../withWindowDimensions/types'; +import CONST from '../../CONST'; + +type BaseModalProps = WindowDimensionsProps & + ChildrenProps & { + /** Decides whether the modal should cover fullscreen. FullScreen modal has backdrop */ + fullscreen?: boolean; + + /** Should we close modal on outside click */ + shouldCloseOnOutsideClick?: boolean; + + /** Should we announce the Modal visibility changes? */ + shouldSetModalVisibility?: boolean; + + /** Callback method fired when the user requests to close the modal */ + onClose: () => void; + + /** State that determines whether to display the modal or not */ + isVisible: boolean; + + /** Callback method fired when the user requests to submit the modal content. */ + onSubmit?: () => void; + + /** Callback method fired when the modal is hidden */ + onModalHide?: () => void; + + /** Callback method fired when the modal is shown */ + onModalShow?: () => void; + + /** Style of modal to display */ + // type: PropTypes.oneOf(_.values(CONST.MODAL.MODAL_TYPE)), + type?: ValueOf; + + /** A react-native-animatable animation definition for the modal display animation. */ + animationIn?: Pick; + + /** A react-native-animatable animation definition for the modal hide animation. */ + animationOut?: Pick; + + /** The anchor position of a popover modal. Has no effect on other modal types. */ + popoverAnchorPosition?: { + top: number; + right: number; + bottom: number; + left: number; + }; + + /** Modal container styles */ + innerContainerStyle?: Pick; + + /** Whether the modal should go under the system statusbar */ + statusBarTranslucent?: boolean; + + /** Whether the modal should avoid the keyboard */ + avoidKeyboard?: boolean; + + /** + * Whether the modal should hide its content while animating. On iOS, set to true + * if `useNativeDriver` is also true, to avoid flashes in the UI. + * + * See: https://github.com/react-native-modal/react-native-modal/pull/116 + * */ + hideModalContentWhileAnimating?: boolean; + }; + +export default BaseModalProps; From cf613ad591c4448573fe68ab142ff4b86bfc37ea Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 23 Oct 2023 13:33:37 +0200 Subject: [PATCH 03/25] start migrating BaseModal to TypeScript --- .../Modal/{BaseModal.js => BaseModal.tsx} | 79 ++++++++----------- src/components/Modal/types.ts | 13 +++ 2 files changed, 44 insertions(+), 48 deletions(-) rename src/components/Modal/{BaseModal.js => BaseModal.tsx} (86%) diff --git a/src/components/Modal/BaseModal.js b/src/components/Modal/BaseModal.tsx similarity index 86% rename from src/components/Modal/BaseModal.js rename to src/components/Modal/BaseModal.tsx index 051c4ba3f80a..ec60b8520180 100644 --- a/src/components/Modal/BaseModal.js +++ b/src/components/Modal/BaseModal.tsx @@ -15,43 +15,34 @@ import CONST from '../../CONST'; import ComposerFocusManager from '../../libs/ComposerFocusManager'; import useNativeDriver from '../../libs/useNativeDriver'; import usePrevious from '../../hooks/usePrevious'; - -const propTypes = { - ...modalPropTypes, - - /** The ref to the modal container */ - forwardedRef: PropTypes.func, -}; - -const defaultProps = { - ...modalDefaultProps, - forwardedRef: () => {}, -}; - -function BaseModal({ - isVisible, - onClose, - shouldSetModalVisibility, - onModalHide, - type, - popoverAnchorPosition, - innerContainerStyle, - outerStyle, - onModalShow, - propagateSwipe, - fullscreen, - animationIn, - animationOut, - useNativeDriver: useNativeDriverProp, - hideModalContentWhileAnimating, - animationInTiming, - animationOutTiming, - statusBarTranslucent, - onLayout, - avoidKeyboard, - forwardedRef, - children, -}) { +import BaseModalProps from './types'; + +function BaseModal( + { + isVisible, + onClose, + shouldSetModalVisibility, + onModalHide, + type, + popoverAnchorPosition, + innerContainerStyle, + outerStyle, + onModalShow, + propagateSwipe, + fullscreen, + animationIn, + animationOut, + useNativeDriver: useNativeDriverProp, + hideModalContentWhileAnimating, + animationInTiming, + animationOutTiming, + statusBarTranslucent, + onLayout, + avoidKeyboard, + children, + }: BaseModalProps, + ref: React.ForwardedRef, +) { const {windowWidth, windowHeight, isSmallScreenWidth} = useWindowDimensions(); const safeAreaInsets = useSafeAreaInsets(); @@ -69,7 +60,7 @@ function BaseModal({ Modal.setModalVisibility(false); } if (callHideCallback) { - onModalHide(); + onModalHide?.(); } Modal.onModalDidClose(); if (!fullscreen) { @@ -207,7 +198,7 @@ function BaseModal({ > {children} @@ -215,14 +206,6 @@ function BaseModal({ ); } -BaseModal.propTypes = propTypes; -BaseModal.defaultProps = defaultProps; BaseModal.displayName = 'BaseModal'; -export default forwardRef((props, ref) => ( - -)); +export default forwardRef(BaseModal); diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts index b17f794374e6..585df54b4b55 100644 --- a/src/components/Modal/types.ts +++ b/src/components/Modal/types.ts @@ -1,4 +1,5 @@ import {ValueOf} from 'type-fest'; +import {StyleProp, ViewStyle} from 'react-native'; import {ModalProps} from 'react-native-modal'; import ChildrenProps from '../../types/utils/ChildrenProps'; import {WindowDimensionsProps} from '../withWindowDimensions/types'; @@ -30,6 +31,8 @@ type BaseModalProps = WindowDimensionsProps & /** Callback method fired when the modal is shown */ onModalShow?: () => void; + propagateSwipe?: Pick; + /** Style of modal to display */ // type: PropTypes.oneOf(_.values(CONST.MODAL.MODAL_TYPE)), type?: ValueOf; @@ -40,6 +43,12 @@ type BaseModalProps = WindowDimensionsProps & /** A react-native-animatable animation definition for the modal hide animation. */ animationOut?: Pick; + useNativeDriver?: Pick; + + animationInTiming?: Pick; + + animationOutTiming?: Pick; + /** The anchor position of a popover modal. Has no effect on other modal types. */ popoverAnchorPosition?: { top: number; @@ -51,9 +60,13 @@ type BaseModalProps = WindowDimensionsProps & /** Modal container styles */ innerContainerStyle?: Pick; + outerStyle?: StyleProp; + /** Whether the modal should go under the system statusbar */ statusBarTranslucent?: boolean; + onLayout: Pick; + /** Whether the modal should avoid the keyboard */ avoidKeyboard?: boolean; From 408ef25d1139445bc451b3f0f809173f95ec2379 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Tue, 24 Oct 2023 13:53:15 +0200 Subject: [PATCH 04/25] add missing prop types --- src/components/Modal/types.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts index 585df54b4b55..03c6cee89fed 100644 --- a/src/components/Modal/types.ts +++ b/src/components/Modal/types.ts @@ -1,5 +1,5 @@ import {ValueOf} from 'type-fest'; -import {StyleProp, ViewStyle} from 'react-native'; +import {ViewStyle} from 'react-native'; import {ModalProps} from 'react-native-modal'; import ChildrenProps from '../../types/utils/ChildrenProps'; import {WindowDimensionsProps} from '../withWindowDimensions/types'; @@ -51,16 +51,16 @@ type BaseModalProps = WindowDimensionsProps & /** The anchor position of a popover modal. Has no effect on other modal types. */ popoverAnchorPosition?: { - top: number; - right: number; - bottom: number; - left: number; + top?: number; + right?: number; + bottom?: number; + left?: number; }; /** Modal container styles */ innerContainerStyle?: Pick; - outerStyle?: StyleProp; + outerStyle?: ViewStyle; /** Whether the modal should go under the system statusbar */ statusBarTranslucent?: boolean; From 1c1d092c1737c65138ef423d9e9e0dd3ec885513 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Tue, 24 Oct 2023 16:17:20 +0200 Subject: [PATCH 05/25] update types, update helper function types --- src/components/Modal/types.ts | 25 ++++--------------------- src/libs/actions/Modal.ts | 4 ++-- src/styles/getModalStyles.ts | 4 +--- 3 files changed, 7 insertions(+), 26 deletions(-) diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts index 03c6cee89fed..b1dfdaff640d 100644 --- a/src/components/Modal/types.ts +++ b/src/components/Modal/types.ts @@ -1,12 +1,11 @@ import {ValueOf} from 'type-fest'; import {ViewStyle} from 'react-native'; import {ModalProps} from 'react-native-modal'; -import ChildrenProps from '../../types/utils/ChildrenProps'; import {WindowDimensionsProps} from '../withWindowDimensions/types'; import CONST from '../../CONST'; type BaseModalProps = WindowDimensionsProps & - ChildrenProps & { + ModalProps & { /** Decides whether the modal should cover fullscreen. FullScreen modal has backdrop */ fullscreen?: boolean; @@ -31,24 +30,10 @@ type BaseModalProps = WindowDimensionsProps & /** Callback method fired when the modal is shown */ onModalShow?: () => void; - propagateSwipe?: Pick; - /** Style of modal to display */ // type: PropTypes.oneOf(_.values(CONST.MODAL.MODAL_TYPE)), type?: ValueOf; - /** A react-native-animatable animation definition for the modal display animation. */ - animationIn?: Pick; - - /** A react-native-animatable animation definition for the modal hide animation. */ - animationOut?: Pick; - - useNativeDriver?: Pick; - - animationInTiming?: Pick; - - animationOutTiming?: Pick; - /** The anchor position of a popover modal. Has no effect on other modal types. */ popoverAnchorPosition?: { top?: number; @@ -57,19 +42,17 @@ type BaseModalProps = WindowDimensionsProps & left?: number; }; - /** Modal container styles */ - innerContainerStyle?: Pick; - outerStyle?: ViewStyle; /** Whether the modal should go under the system statusbar */ statusBarTranslucent?: boolean; - onLayout: Pick; - /** Whether the modal should avoid the keyboard */ avoidKeyboard?: boolean; + /** Modal container styles */ + innerContainerStyle?: ViewStyle; + /** * Whether the modal should hide its content while animating. On iOS, set to true * if `useNativeDriver` is also true, to avoid flashes in the UI. diff --git a/src/libs/actions/Modal.ts b/src/libs/actions/Modal.ts index ff09731f59a7..7a443b94354f 100644 --- a/src/libs/actions/Modal.ts +++ b/src/libs/actions/Modal.ts @@ -1,13 +1,13 @@ import Onyx from 'react-native-onyx'; import ONYXKEYS from '../../ONYXKEYS'; -let closeModal: (isNavigating: boolean) => void; +let closeModal: ((isNavigating: boolean) => void) | null; let onModalClose: null | (() => void); /** * Allows other parts of the app to call modal close function */ -function setCloseModal(onClose: () => void) { +function setCloseModal(onClose: (() => void) | null) { closeModal = onClose; } diff --git a/src/styles/getModalStyles.ts b/src/styles/getModalStyles.ts index d52d29568c2d..e318e7b989b8 100644 --- a/src/styles/getModalStyles.ts +++ b/src/styles/getModalStyles.ts @@ -21,8 +21,6 @@ type WindowDimensions = { windowWidth: number; windowHeight: number; isSmallScreenWidth: boolean; - isMediumScreenWidth: boolean; - isLargeScreenWidth: boolean; }; type GetModalStyles = { @@ -39,7 +37,7 @@ type GetModalStyles = { }; export default function getModalStyles( - type: ModalType, + type: ModalType | undefined, windowDimensions: WindowDimensions, popoverAnchorPosition: ViewStyle = {}, innerContainerStyle: ViewStyle = {}, From 118415e0d938ad828401f6a5052beba72851b018 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Tue, 24 Oct 2023 16:17:42 +0200 Subject: [PATCH 06/25] reorganise types in StyleUtils --- src/styles/StyleUtils.ts | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index 62da2bf3be4b..68a2e61529bc 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -56,10 +56,10 @@ type ModalPaddingStylesParams = { safeAreaPaddingBottom: number; safeAreaPaddingLeft: number; safeAreaPaddingRight: number; - modalContainerStyleMarginTop: number; - modalContainerStyleMarginBottom: number; - modalContainerStylePaddingTop: number; - modalContainerStylePaddingBottom: number; + modalContainerStyleMarginTop: DimensionValue | undefined; + modalContainerStyleMarginBottom: DimensionValue | undefined; + modalContainerStylePaddingTop: DimensionValue | undefined; + modalContainerStylePaddingBottom: DimensionValue | undefined; insets: EdgeInsets; }; @@ -287,12 +287,19 @@ function getEReceiptColorStyles(colorCode: EReceiptColorName): EreceiptColorStyl return eReceiptColorStyles[colorCode]; } +type SafeAreaPadding = { + paddingTop: number; + paddingBottom: number; + paddingLeft: number; + paddingRight: number; +}; + /** * Takes safe area insets and returns padding to use for a View */ -function getSafeAreaPadding(insets?: EdgeInsets, insetsPercentage: number = variables.safeInsertPercentage): ViewStyle { +function getSafeAreaPadding(insets?: EdgeInsets, insetsPercentage: number = variables.safeInsertPercentage): SafeAreaPadding { return { - paddingTop: insets?.top, + paddingTop: insets?.top ?? 0, paddingBottom: (insets?.bottom ?? 0) * insetsPercentage, paddingLeft: (insets?.left ?? 0) * insetsPercentage, paddingRight: (insets?.right ?? 0) * insetsPercentage, @@ -568,6 +575,14 @@ function getWidthAndHeightStyle(width: number, height?: number): ViewStyle { }; } +function getCombinedSpacing(modalContainerValue: DimensionValue | undefined, safeAreaValue: number, shouldAddSafeAreaValue: boolean): number | DimensionValue | undefined { + if (typeof modalContainerValue === 'number') { + return (modalContainerValue ?? 0) + (shouldAddSafeAreaValue ? safeAreaValue : 0); + } + + return modalContainerValue; +} + function getModalPaddingStyles({ shouldAddBottomSafeAreaMargin, shouldAddTopSafeAreaMargin, @@ -585,12 +600,12 @@ function getModalPaddingStyles({ }: ModalPaddingStylesParams): ViewStyle { // use fallback value for safeAreaPaddingBottom to keep padding bottom consistent with padding top. // More info: issue #17376 - const safeAreaPaddingBottomWithFallback = insets.bottom === 0 ? modalContainerStylePaddingTop ?? 0 : safeAreaPaddingBottom; + const safeAreaPaddingBottomWithFallback = insets.bottom === 0 && typeof modalContainerStylePaddingTop === 'number' ? modalContainerStylePaddingTop ?? 0 : safeAreaPaddingBottom; return { - marginTop: (modalContainerStyleMarginTop ?? 0) + (shouldAddTopSafeAreaMargin ? safeAreaPaddingTop : 0), - marginBottom: (modalContainerStyleMarginBottom ?? 0) + (shouldAddBottomSafeAreaMargin ? safeAreaPaddingBottomWithFallback : 0), - paddingTop: shouldAddTopSafeAreaPadding ? (modalContainerStylePaddingTop ?? 0) + safeAreaPaddingTop : modalContainerStylePaddingTop ?? 0, - paddingBottom: shouldAddBottomSafeAreaPadding ? (modalContainerStylePaddingBottom ?? 0) + safeAreaPaddingBottomWithFallback : modalContainerStylePaddingBottom ?? 0, + marginTop: getCombinedSpacing(modalContainerStyleMarginTop, safeAreaPaddingTop, shouldAddTopSafeAreaMargin), + marginBottom: getCombinedSpacing(modalContainerStyleMarginBottom, safeAreaPaddingBottomWithFallback, shouldAddBottomSafeAreaMargin), + paddingTop: getCombinedSpacing(modalContainerStylePaddingTop, safeAreaPaddingTop, shouldAddTopSafeAreaPadding), + paddingBottom: getCombinedSpacing(modalContainerStylePaddingBottom, safeAreaPaddingBottomWithFallback, shouldAddBottomSafeAreaPadding), paddingLeft: safeAreaPaddingLeft ?? 0, paddingRight: safeAreaPaddingRight ?? 0, }; From 4c18fc0ba771b5c5f2995b97c24c9d388ae17b34 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Tue, 24 Oct 2023 16:18:01 +0200 Subject: [PATCH 07/25] fix typings in BaseModal --- src/components/Modal/BaseModal.tsx | 32 ++++++++++++------------------ 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index ec60b8520180..86fbbf896ac1 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -1,17 +1,14 @@ import React, {forwardRef, useCallback, useEffect, useMemo, useRef} from 'react'; import {View} from 'react-native'; -import PropTypes from 'prop-types'; import ReactNativeModal from 'react-native-modal'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; import styles from '../../styles/styles'; import * as Modal from '../../libs/actions/Modal'; import * as StyleUtils from '../../styles/StyleUtils'; import themeColors from '../../styles/themes/default'; -import {propTypes as modalPropTypes, defaultProps as modalDefaultProps} from './modalPropTypes'; import getModalStyles from '../../styles/getModalStyles'; import useWindowDimensions from '../../hooks/useWindowDimensions'; import variables from '../../styles/variables'; -import CONST from '../../CONST'; import ComposerFocusManager from '../../libs/ComposerFocusManager'; import useNativeDriver from '../../libs/useNativeDriver'; import usePrevious from '../../hooks/usePrevious'; @@ -21,24 +18,24 @@ function BaseModal( { isVisible, onClose, - shouldSetModalVisibility, - onModalHide, + shouldSetModalVisibility = true, + onModalHide = () => {}, type, - popoverAnchorPosition, - innerContainerStyle, + popoverAnchorPosition = {}, + innerContainerStyle = {}, outerStyle, - onModalShow, + onModalShow = () => {}, propagateSwipe, - fullscreen, + fullscreen = true, animationIn, animationOut, useNativeDriver: useNativeDriverProp, - hideModalContentWhileAnimating, + hideModalContentWhileAnimating = false, animationInTiming, animationOutTiming, - statusBarTranslucent, + statusBarTranslucent = true, onLayout, - avoidKeyboard, + avoidKeyboard = false, children, }: BaseModalProps, ref: React.ForwardedRef, @@ -101,13 +98,10 @@ function BaseModal( if (shouldSetModalVisibility) { Modal.setModalVisibility(true); } - onModalShow(); + onModalShow?.(); }; - const handleBackdropPress = (e) => { - if (e && e.key === CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey) { - return; - } + const handleBackdropPress = () => { onClose(); }; @@ -186,8 +180,8 @@ function BaseModal( style={modalStyle} deviceHeight={windowHeight} deviceWidth={windowWidth} - animationIn={animationIn || modalStyleAnimationIn} - animationOut={animationOut || modalStyleAnimationOut} + animationIn={animationIn ?? modalStyleAnimationIn} + animationOut={animationOut ?? modalStyleAnimationOut} useNativeDriver={useNativeDriverProp && useNativeDriver} hideModalContentWhileAnimating={hideModalContentWhileAnimating} animationInTiming={animationInTiming} From c016d8436219e39f8fd6da99d0544271c2fa393c Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Wed, 25 Oct 2023 12:27:07 +0200 Subject: [PATCH 08/25] migrate index.web.js to TypeScript --- .../Modal/{index.web.js => index.web.tsx} | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) rename src/components/Modal/{index.web.js => index.web.tsx} (63%) diff --git a/src/components/Modal/index.web.js b/src/components/Modal/index.web.tsx similarity index 63% rename from src/components/Modal/index.web.js rename to src/components/Modal/index.web.tsx index 065b3a9f210f..d5ed7e6369ba 100644 --- a/src/components/Modal/index.web.js +++ b/src/components/Modal/index.web.tsx @@ -1,14 +1,14 @@ import React, {useState} from 'react'; import withWindowDimensions from '../withWindowDimensions'; import BaseModal from './BaseModal'; -import {propTypes, defaultProps} from './modalPropTypes'; import * as StyleUtils from '../../styles/StyleUtils'; import themeColors from '../../styles/themes/default'; import StatusBar from '../../libs/StatusBar'; import CONST from '../../CONST'; +import BaseModalProps from './types'; -function Modal(props) { - const [previousStatusBarColor, setPreviousStatusBarColor] = useState(); +function Modal(props: BaseModalProps) { + const [previousStatusBarColor, setPreviousStatusBarColor] = useState(); const setStatusBarColor = (color = themeColors.appBG) => { if (!props.fullscreen) { @@ -25,12 +25,15 @@ function Modal(props) { const showModal = () => { const statusBarColor = StatusBar.getBackgroundColor(); - const isFullScreenModal = - props.type === CONST.MODAL.MODAL_TYPE.CENTERED || props.type === CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE || props.type === CONST.MODAL.MODAL_TYPE.RIGHT_DOCKED; - setPreviousStatusBarColor(statusBarColor); - // If it is a full screen modal then match it with appBG, otherwise we use the backdrop color - setStatusBarColor(isFullScreenModal ? themeColors.appBG : StyleUtils.getThemeBackgroundColor(statusBarColor)); - props.onModalShow(); + + if (statusBarColor) { + const isFullScreenModal = + props.type === CONST.MODAL.MODAL_TYPE.CENTERED || props.type === CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE || props.type === CONST.MODAL.MODAL_TYPE.RIGHT_DOCKED; + setPreviousStatusBarColor(statusBarColor); + // If it is a full screen modal then match it with appBG, otherwise we use the backdrop color + setStatusBarColor(isFullScreenModal ? themeColors.appBG : StyleUtils.getThemeBackgroundColor(statusBarColor)); + props.onModalShow(); + } }; return ( @@ -46,7 +49,5 @@ function Modal(props) { ); } -Modal.propTypes = propTypes; -Modal.defaultProps = defaultProps; Modal.displayName = 'Modal'; export default withWindowDimensions(Modal); From 5f3968d44bccd743a1b4739484771de7a52a0c5b Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Wed, 25 Oct 2023 12:35:39 +0200 Subject: [PATCH 09/25] migrate index.ios.js to TypeScript --- src/components/Modal/{index.ios.js => index.ios.tsx} | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) rename src/components/Modal/{index.ios.js => index.ios.tsx} (72%) diff --git a/src/components/Modal/index.ios.js b/src/components/Modal/index.ios.tsx similarity index 72% rename from src/components/Modal/index.ios.js rename to src/components/Modal/index.ios.tsx index d8206d12532d..8b82a67d8601 100644 --- a/src/components/Modal/index.ios.js +++ b/src/components/Modal/index.ios.tsx @@ -1,9 +1,9 @@ import React from 'react'; import withWindowDimensions from '../withWindowDimensions'; import BaseModal from './BaseModal'; -import {propTypes, defaultProps} from './modalPropTypes'; +import BaseModalProps from './types'; -function Modal(props) { +function Modal(props: BaseModalProps) { return ( Date: Wed, 25 Oct 2023 13:15:23 +0200 Subject: [PATCH 10/25] migrate index.android.js to TypeScript --- .../Modal/{index.android.js => index.android.tsx} | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) rename src/components/Modal/{index.android.js => index.android.tsx} (86%) diff --git a/src/components/Modal/index.android.js b/src/components/Modal/index.android.tsx similarity index 86% rename from src/components/Modal/index.android.js rename to src/components/Modal/index.android.tsx index b5f11a02650a..cf61452b7780 100644 --- a/src/components/Modal/index.android.js +++ b/src/components/Modal/index.android.tsx @@ -2,8 +2,8 @@ import React from 'react'; import {AppState} from 'react-native'; import withWindowDimensions from '../withWindowDimensions'; import BaseModal from './BaseModal'; -import {propTypes, defaultProps} from './modalPropTypes'; import ComposerFocusManager from '../../libs/ComposerFocusManager'; +import BaseModalProps from './types'; AppState.addEventListener('focus', () => { ComposerFocusManager.setReadyToFocus(); @@ -15,19 +15,17 @@ AppState.addEventListener('blur', () => { // Only want to use useNativeDriver on Android. It has strange flashes issue on IOS // https://github.com/react-native-modal/react-native-modal#the-modal-flashes-in-a-weird-way-when-animating -function Modal(props) { +function Modal(props: BaseModalProps) { return ( {props.children} ); } -Modal.propTypes = propTypes; -Modal.defaultProps = defaultProps; Modal.displayName = 'Modal'; export default withWindowDimensions(Modal); From a4378183f74b13825f67968607b3314f62c6bac5 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Wed, 25 Oct 2023 14:47:54 +0200 Subject: [PATCH 11/25] fix spacing calculation --- src/styles/StyleUtils.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index 68a2e61529bc..8d42a3810469 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -575,8 +575,16 @@ function getWidthAndHeightStyle(width: number, height?: number): ViewStyle { }; } +/** + * Combine margin/padding with safe area inset + * + * @param modalContainerValue - margin or padding value + * @param safeAreaValue - safe area inset + * @param shouldAddSafeAreaValue - indicator whether safe area inset should be applied + */ function getCombinedSpacing(modalContainerValue: DimensionValue | undefined, safeAreaValue: number, shouldAddSafeAreaValue: boolean): number | DimensionValue | undefined { - if (typeof modalContainerValue === 'number') { + // modalContainerValue can only be added to safe area inset if it's a number, otherwise it's returned as is + if (typeof modalContainerValue === 'number' || !modalContainerValue) { return (modalContainerValue ?? 0) + (shouldAddSafeAreaValue ? safeAreaValue : 0); } From 73859687f0883648cfa0ac1b68f6f6508f02bddb Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 26 Oct 2023 10:20:43 +0200 Subject: [PATCH 12/25] apply finishing touches --- src/components/Modal/BaseModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index 9074d9d9e53e..d845beb2e3a1 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -49,7 +49,7 @@ function BaseModal( /** * Hides modal - * @param {Boolean} [callHideCallback=true] Should we call the onModalHide callback + * @param callHideCallback - Should we call the onModalHide callback */ const hideModal = useCallback( (callHideCallback = true) => { From c26a716da26ed01036bd370c4a2a55b75791f9e4 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 26 Oct 2023 10:38:26 +0200 Subject: [PATCH 13/25] remove unnecessary conditional function calls --- src/components/Modal/BaseModal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index d845beb2e3a1..4c6532d16fb8 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -57,7 +57,7 @@ function BaseModal( Modal.setModalVisibility(false); } if (callHideCallback) { - onModalHide?.(); + onModalHide(); } Modal.onModalDidClose(); if (!fullscreen) { @@ -98,7 +98,7 @@ function BaseModal( if (shouldSetModalVisibility) { Modal.setModalVisibility(true); } - onModalShow?.(); + onModalShow(); }; const handleBackdropPress = () => { From a1adc3766af1546c69c0bb6ad1f387eb97abb5dc Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 26 Oct 2023 10:40:44 +0200 Subject: [PATCH 14/25] create common NewDimensions type --- src/components/withWindowDimensions/index.native.tsx | 6 +++--- src/components/withWindowDimensions/index.tsx | 6 +++--- src/components/withWindowDimensions/types.ts | 6 +++++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/components/withWindowDimensions/index.native.tsx b/src/components/withWindowDimensions/index.native.tsx index 889397f2bab6..f4ce0707c83e 100644 --- a/src/components/withWindowDimensions/index.native.tsx +++ b/src/components/withWindowDimensions/index.native.tsx @@ -1,11 +1,11 @@ import React, {createContext, useState, useEffect, useMemo, ComponentType, RefAttributes, ForwardedRef} from 'react'; import PropTypes from 'prop-types'; -import {Dimensions, ScaledSize} from 'react-native'; +import {Dimensions} from 'react-native'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; import getComponentDisplayName from '../../libs/getComponentDisplayName'; import variables from '../../styles/variables'; import getWindowHeightAdjustment from '../../libs/getWindowHeightAdjustment'; -import {WindowDimensionsContextData, WindowDimensionsProps} from './types'; +import {NewDimensions, WindowDimensionsContextData, WindowDimensionsProps} from './types'; import ChildrenProps from '../../types/utils/ChildrenProps'; const WindowDimensionsContext = createContext(null); @@ -39,7 +39,7 @@ function WindowDimensionsProvider(props: ChildrenProps) { }); useEffect(() => { - const onDimensionChange = (newDimensions: {window: ScaledSize}) => { + const onDimensionChange = (newDimensions: NewDimensions) => { const {window} = newDimensions; setWindowDimension({ windowHeight: window.height, diff --git a/src/components/withWindowDimensions/index.tsx b/src/components/withWindowDimensions/index.tsx index 6a5dfadc6a49..ab5c4d006751 100644 --- a/src/components/withWindowDimensions/index.tsx +++ b/src/components/withWindowDimensions/index.tsx @@ -1,13 +1,13 @@ import React, {createContext, useState, useEffect, useMemo, RefAttributes, ComponentType, ForwardedRef} from 'react'; import PropTypes from 'prop-types'; import lodashDebounce from 'lodash/debounce'; -import {Dimensions, ScaledSize} from 'react-native'; +import {Dimensions} from 'react-native'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; import getComponentDisplayName from '../../libs/getComponentDisplayName'; import variables from '../../styles/variables'; import getWindowHeightAdjustment from '../../libs/getWindowHeightAdjustment'; import ChildrenProps from '../../types/utils/ChildrenProps'; -import {WindowDimensionsContextData, WindowDimensionsProps} from './types'; +import {NewDimensions, WindowDimensionsContextData, WindowDimensionsProps} from './types'; const WindowDimensionsContext = createContext(null); const windowDimensionsPropTypes = { @@ -40,7 +40,7 @@ function WindowDimensionsProvider(props: ChildrenProps) { }); useEffect(() => { - const onDimensionChange = (newDimensions: {window: ScaledSize}) => { + const onDimensionChange = (newDimensions: NewDimensions) => { const {window} = newDimensions; setWindowDimension({ windowHeight: window.height, diff --git a/src/components/withWindowDimensions/types.ts b/src/components/withWindowDimensions/types.ts index 994c10fa4a52..514c86616b87 100644 --- a/src/components/withWindowDimensions/types.ts +++ b/src/components/withWindowDimensions/types.ts @@ -1,3 +1,5 @@ +import {ScaledSize} from 'react-native'; + type WindowDimensionsContextData = { windowHeight: number; windowWidth: number; @@ -27,4 +29,6 @@ type WindowDimensionsProps = WindowDimensionsContextData & { isLargeScreenWidth: boolean; }; -export type {WindowDimensionsContextData, WindowDimensionsProps}; +type NewDimensions = {window: ScaledSize}; + +export type {WindowDimensionsContextData, WindowDimensionsProps, NewDimensions}; From bc5c041c7f016a46ff02a207bd297697ca4d822d Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 26 Oct 2023 11:20:03 +0200 Subject: [PATCH 15/25] change index.web.tsx to index.tsx to fix linting --- src/components/Modal/{index.web.tsx => index.tsx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/components/Modal/{index.web.tsx => index.tsx} (100%) diff --git a/src/components/Modal/index.web.tsx b/src/components/Modal/index.tsx similarity index 100% rename from src/components/Modal/index.web.tsx rename to src/components/Modal/index.tsx From 673d1207fb1653c823fdc76edfd5d9f86cad7e1d Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 26 Oct 2023 14:14:21 +0200 Subject: [PATCH 16/25] add event to backdrop press handler --- src/components/Modal/BaseModal.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index 4c6532d16fb8..7a4747718fb9 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -13,6 +13,7 @@ import ComposerFocusManager from '../../libs/ComposerFocusManager'; import useNativeDriver from '../../libs/useNativeDriver'; import usePrevious from '../../hooks/usePrevious'; import BaseModalProps from './types'; +import CONST from '../../CONST'; function BaseModal( { @@ -101,7 +102,11 @@ function BaseModal( onModalShow(); }; - const handleBackdropPress = () => { + const handleBackdropPress = (e?: KeyboardEvent) => { + if (e?.key === CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey) { + return; + } + onClose(); }; From 2d7d45bf6e5928b8490596888255b8e8f97597e8 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 26 Oct 2023 14:33:17 +0200 Subject: [PATCH 17/25] apply changes based on feedback from code review --- src/components/Modal/index.tsx | 8 +++++--- src/components/Modal/types.ts | 5 +++-- src/components/withWindowDimensions/index.native.tsx | 4 ---- src/components/withWindowDimensions/index.tsx | 4 ---- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx index d5ed7e6369ba..82f2aac9bbc2 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -26,14 +26,16 @@ function Modal(props: BaseModalProps) { const showModal = () => { const statusBarColor = StatusBar.getBackgroundColor(); + const isFullScreenModal = + props.type === CONST.MODAL.MODAL_TYPE.CENTERED || props.type === CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE || props.type === CONST.MODAL.MODAL_TYPE.RIGHT_DOCKED; + if (statusBarColor) { - const isFullScreenModal = - props.type === CONST.MODAL.MODAL_TYPE.CENTERED || props.type === CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE || props.type === CONST.MODAL.MODAL_TYPE.RIGHT_DOCKED; setPreviousStatusBarColor(statusBarColor); // If it is a full screen modal then match it with appBG, otherwise we use the backdrop color setStatusBarColor(isFullScreenModal ? themeColors.appBG : StyleUtils.getThemeBackgroundColor(statusBarColor)); - props.onModalShow(); } + + props.onModalShow(); }; return ( diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts index b1dfdaff640d..36ce5fa28781 100644 --- a/src/components/Modal/types.ts +++ b/src/components/Modal/types.ts @@ -4,6 +4,8 @@ import {ModalProps} from 'react-native-modal'; import {WindowDimensionsProps} from '../withWindowDimensions/types'; import CONST from '../../CONST'; +type ModalVariant = ValueOf; + type BaseModalProps = WindowDimensionsProps & ModalProps & { /** Decides whether the modal should cover fullscreen. FullScreen modal has backdrop */ @@ -31,8 +33,7 @@ type BaseModalProps = WindowDimensionsProps & onModalShow?: () => void; /** Style of modal to display */ - // type: PropTypes.oneOf(_.values(CONST.MODAL.MODAL_TYPE)), - type?: ValueOf; + type?: ModalVariant; /** The anchor position of a popover modal. Has no effect on other modal types. */ popoverAnchorPosition?: { diff --git a/src/components/withWindowDimensions/index.native.tsx b/src/components/withWindowDimensions/index.native.tsx index f4ce0707c83e..611efa60ee79 100644 --- a/src/components/withWindowDimensions/index.native.tsx +++ b/src/components/withWindowDimensions/index.native.tsx @@ -74,10 +74,6 @@ function WindowDimensionsProvider(props: ChildrenProps) { WindowDimensionsProvider.displayName = 'WindowDimensionsProvider'; -/** - * @param WrappedComponent - * @returns - */ export default function withWindowDimensions(WrappedComponent: ComponentType>) { function WithWindowDimensions(props: Omit, ref: ForwardedRef) { return ( diff --git a/src/components/withWindowDimensions/index.tsx b/src/components/withWindowDimensions/index.tsx index ab5c4d006751..d952f036fba0 100644 --- a/src/components/withWindowDimensions/index.tsx +++ b/src/components/withWindowDimensions/index.tsx @@ -80,10 +80,6 @@ function WindowDimensionsProvider(props: ChildrenProps) { WindowDimensionsProvider.displayName = 'WindowDimensionsProvider'; -/** - * @param WrappedComponent - * @returns - */ export default function withWindowDimensions(WrappedComponent: ComponentType>) { function WithWindowDimensions(props: Omit, ref: ForwardedRef) { return ( From a75557fcbeebdded218b6eb8752b58aa97c68ae9 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 26 Oct 2023 16:02:48 +0200 Subject: [PATCH 18/25] extract PopoverAnchorPosition to TypeScript --- src/components/Modal/types.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts index 36ce5fa28781..68bffbb60080 100644 --- a/src/components/Modal/types.ts +++ b/src/components/Modal/types.ts @@ -4,7 +4,12 @@ import {ModalProps} from 'react-native-modal'; import {WindowDimensionsProps} from '../withWindowDimensions/types'; import CONST from '../../CONST'; -type ModalVariant = ValueOf; +type PopoverAnchorPosition = { + top?: number; + right?: number; + bottom?: number; + left?: number; +}; type BaseModalProps = WindowDimensionsProps & ModalProps & { @@ -33,15 +38,10 @@ type BaseModalProps = WindowDimensionsProps & onModalShow?: () => void; /** Style of modal to display */ - type?: ModalVariant; + type?: ValueOf; /** The anchor position of a popover modal. Has no effect on other modal types. */ - popoverAnchorPosition?: { - top?: number; - right?: number; - bottom?: number; - left?: number; - }; + popoverAnchorPosition?: PopoverAnchorPosition; outerStyle?: ViewStyle; From da8b083bd138ff6468447e173b2b559cfadddcc4 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 30 Oct 2023 16:19:29 +0100 Subject: [PATCH 19/25] add return types to HOCs, fix linting --- src/components/Modal/BaseModal.tsx | 14 +++++++------- src/components/Modal/types.ts | 4 ++-- .../withWindowDimensions/index.native.tsx | 10 ++++++---- src/components/withWindowDimensions/index.tsx | 10 ++++++---- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index 71a89ddbfdf4..512ab65eb7af 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -3,18 +3,18 @@ import React, {forwardRef, useCallback, useEffect, useMemo, useRef} from 'react' import {View} from 'react-native'; import ReactNativeModal from 'react-native-modal'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; -import styles from '../../styles/styles'; +import CONST from '../../CONST'; +import usePrevious from '../../hooks/usePrevious'; +import useWindowDimensions from '../../hooks/useWindowDimensions'; import * as Modal from '../../libs/actions/Modal'; +import ComposerFocusManager from '../../libs/ComposerFocusManager'; +import useNativeDriver from '../../libs/useNativeDriver'; +import getModalStyles from '../../styles/getModalStyles'; +import styles from '../../styles/styles'; import * as StyleUtils from '../../styles/StyleUtils'; import themeColors from '../../styles/themes/default'; -import getModalStyles from '../../styles/getModalStyles'; -import useWindowDimensions from '../../hooks/useWindowDimensions'; import variables from '../../styles/variables'; -import ComposerFocusManager from '../../libs/ComposerFocusManager'; -import useNativeDriver from '../../libs/useNativeDriver'; -import usePrevious from '../../hooks/usePrevious'; import BaseModalProps from './types'; -import CONST from '../../CONST'; function BaseModal( { diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts index 68bffbb60080..dcb9edf910de 100644 --- a/src/components/Modal/types.ts +++ b/src/components/Modal/types.ts @@ -1,8 +1,8 @@ -import {ValueOf} from 'type-fest'; import {ViewStyle} from 'react-native'; import {ModalProps} from 'react-native-modal'; -import {WindowDimensionsProps} from '../withWindowDimensions/types'; +import {ValueOf} from 'type-fest'; import CONST from '../../CONST'; +import {WindowDimensionsProps} from '../withWindowDimensions/types'; type PopoverAnchorPosition = { top?: number; diff --git a/src/components/withWindowDimensions/index.native.tsx b/src/components/withWindowDimensions/index.native.tsx index 611efa60ee79..8e4611e6c5b0 100644 --- a/src/components/withWindowDimensions/index.native.tsx +++ b/src/components/withWindowDimensions/index.native.tsx @@ -1,12 +1,12 @@ -import React, {createContext, useState, useEffect, useMemo, ComponentType, RefAttributes, ForwardedRef} from 'react'; import PropTypes from 'prop-types'; +import React, {ComponentType, createContext, ForwardedRef, RefAttributes, useEffect, useMemo, useState} from 'react'; import {Dimensions} from 'react-native'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; import getComponentDisplayName from '../../libs/getComponentDisplayName'; -import variables from '../../styles/variables'; import getWindowHeightAdjustment from '../../libs/getWindowHeightAdjustment'; -import {NewDimensions, WindowDimensionsContextData, WindowDimensionsProps} from './types'; +import variables from '../../styles/variables'; import ChildrenProps from '../../types/utils/ChildrenProps'; +import {NewDimensions, WindowDimensionsContextData, WindowDimensionsProps} from './types'; const WindowDimensionsContext = createContext(null); const windowDimensionsPropTypes = { @@ -74,7 +74,9 @@ function WindowDimensionsProvider(props: ChildrenProps) { WindowDimensionsProvider.displayName = 'WindowDimensionsProvider'; -export default function withWindowDimensions(WrappedComponent: ComponentType>) { +export default function withWindowDimensions( + WrappedComponent: ComponentType>, +): (props: Omit & React.RefAttributes) => React.ReactElement | null { function WithWindowDimensions(props: Omit, ref: ForwardedRef) { return ( diff --git a/src/components/withWindowDimensions/index.tsx b/src/components/withWindowDimensions/index.tsx index c1b12d6f6156..508b9e2608fc 100644 --- a/src/components/withWindowDimensions/index.tsx +++ b/src/components/withWindowDimensions/index.tsx @@ -1,13 +1,13 @@ -import React, {createContext, useState, useEffect, useMemo, RefAttributes, ComponentType, ForwardedRef} from 'react'; -import PropTypes from 'prop-types'; import lodashDebounce from 'lodash/debounce'; +import PropTypes from 'prop-types'; +import React, {ComponentType, createContext, ForwardedRef, RefAttributes, useEffect, useMemo, useState} from 'react'; import {Dimensions} from 'react-native'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; +import getWindowHeightAdjustment from '@libs/getWindowHeightAdjustment'; import getComponentDisplayName from '../../libs/getComponentDisplayName'; import variables from '../../styles/variables'; import ChildrenProps from '../../types/utils/ChildrenProps'; import {NewDimensions, WindowDimensionsContextData, WindowDimensionsProps} from './types'; -import getWindowHeightAdjustment from '@libs/getWindowHeightAdjustment'; const WindowDimensionsContext = createContext(null); const windowDimensionsPropTypes = { @@ -80,7 +80,9 @@ function WindowDimensionsProvider(props: ChildrenProps) { WindowDimensionsProvider.displayName = 'WindowDimensionsProvider'; -export default function withWindowDimensions(WrappedComponent: ComponentType>) { +export default function withWindowDimensions( + WrappedComponent: ComponentType>, +): (props: Omit & React.RefAttributes) => React.ReactElement | null { function WithWindowDimensions(props: Omit, ref: ForwardedRef) { return ( From a6287edbcb47c65f6aff9cfe680f659475266dd5 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 30 Oct 2023 16:49:36 +0100 Subject: [PATCH 20/25] fix imports --- src/components/Modal/BaseModal.tsx | 23 +++++++++---------- src/components/Modal/types.ts | 4 ++-- .../withWindowDimensions/index.native.tsx | 8 +++---- src/components/withWindowDimensions/index.tsx | 6 ++--- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index 512ab65eb7af..89f2a5ba2a53 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -1,19 +1,18 @@ -import PropTypes from 'prop-types'; import React, {forwardRef, useCallback, useEffect, useMemo, useRef} from 'react'; import {View} from 'react-native'; import ReactNativeModal from 'react-native-modal'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; -import CONST from '../../CONST'; -import usePrevious from '../../hooks/usePrevious'; -import useWindowDimensions from '../../hooks/useWindowDimensions'; -import * as Modal from '../../libs/actions/Modal'; -import ComposerFocusManager from '../../libs/ComposerFocusManager'; -import useNativeDriver from '../../libs/useNativeDriver'; -import getModalStyles from '../../styles/getModalStyles'; -import styles from '../../styles/styles'; -import * as StyleUtils from '../../styles/StyleUtils'; -import themeColors from '../../styles/themes/default'; -import variables from '../../styles/variables'; +import usePrevious from '@hooks/usePrevious'; +import useWindowDimensions from '@hooks/useWindowDimensions'; +import ComposerFocusManager from '@libs/ComposerFocusManager'; +import useNativeDriver from '@libs/useNativeDriver'; +import getModalStyles from '@styles/getModalStyles'; +import styles from '@styles/styles'; +import * as StyleUtils from '@styles/StyleUtils'; +import themeColors from '@styles/themes/default'; +import variables from '@styles/variables'; +import * as Modal from '@userActions/Modal'; +import CONST from '@src/CONST'; import BaseModalProps from './types'; function BaseModal( diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts index dcb9edf910de..3fa60e6ac765 100644 --- a/src/components/Modal/types.ts +++ b/src/components/Modal/types.ts @@ -1,8 +1,8 @@ import {ViewStyle} from 'react-native'; import {ModalProps} from 'react-native-modal'; import {ValueOf} from 'type-fest'; -import CONST from '../../CONST'; -import {WindowDimensionsProps} from '../withWindowDimensions/types'; +import {WindowDimensionsProps} from '@components/withWindowDimensions/types'; +import CONST from '@src/CONST'; type PopoverAnchorPosition = { top?: number; diff --git a/src/components/withWindowDimensions/index.native.tsx b/src/components/withWindowDimensions/index.native.tsx index 8e4611e6c5b0..0c9f61a45c0b 100644 --- a/src/components/withWindowDimensions/index.native.tsx +++ b/src/components/withWindowDimensions/index.native.tsx @@ -2,10 +2,10 @@ import PropTypes from 'prop-types'; import React, {ComponentType, createContext, ForwardedRef, RefAttributes, useEffect, useMemo, useState} from 'react'; import {Dimensions} from 'react-native'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; -import getComponentDisplayName from '../../libs/getComponentDisplayName'; -import getWindowHeightAdjustment from '../../libs/getWindowHeightAdjustment'; -import variables from '../../styles/variables'; -import ChildrenProps from '../../types/utils/ChildrenProps'; +import getComponentDisplayName from '@libs/getComponentDisplayName'; +import getWindowHeightAdjustment from '@libs/getWindowHeightAdjustment'; +import variables from '@styles/variables'; +import ChildrenProps from '@src/types/utils/ChildrenProps'; import {NewDimensions, WindowDimensionsContextData, WindowDimensionsProps} from './types'; const WindowDimensionsContext = createContext(null); diff --git a/src/components/withWindowDimensions/index.tsx b/src/components/withWindowDimensions/index.tsx index 508b9e2608fc..1479450deec4 100644 --- a/src/components/withWindowDimensions/index.tsx +++ b/src/components/withWindowDimensions/index.tsx @@ -3,10 +3,10 @@ import PropTypes from 'prop-types'; import React, {ComponentType, createContext, ForwardedRef, RefAttributes, useEffect, useMemo, useState} from 'react'; import {Dimensions} from 'react-native'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; +import getComponentDisplayName from '@libs/getComponentDisplayName'; import getWindowHeightAdjustment from '@libs/getWindowHeightAdjustment'; -import getComponentDisplayName from '../../libs/getComponentDisplayName'; -import variables from '../../styles/variables'; -import ChildrenProps from '../../types/utils/ChildrenProps'; +import variables from '@styles/variables'; +import ChildrenProps from '@src/types/utils/ChildrenProps'; import {NewDimensions, WindowDimensionsContextData, WindowDimensionsProps} from './types'; const WindowDimensionsContext = createContext(null); From f4afdb42aae6cdd44a552454562c7be0e610836e Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Wed, 8 Nov 2023 15:53:16 +0100 Subject: [PATCH 21/25] remove redundant useNativeDriver prop --- src/components/Modal/index.android.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Modal/index.android.tsx b/src/components/Modal/index.android.tsx index 6c7f3173fd0a..3aaf2c342634 100644 --- a/src/components/Modal/index.android.tsx +++ b/src/components/Modal/index.android.tsx @@ -20,7 +20,6 @@ function Modal(props: BaseModalProps) { {props.children} From 8082b7c49407a2048faedf6cc432faa407adccab Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 9 Nov 2023 08:54:52 +0100 Subject: [PATCH 22/25] bring back default useNativeDriver prop for android --- src/components/Modal/index.android.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/Modal/index.android.tsx b/src/components/Modal/index.android.tsx index 3aaf2c342634..2343cb4c70a9 100644 --- a/src/components/Modal/index.android.tsx +++ b/src/components/Modal/index.android.tsx @@ -15,13 +15,14 @@ AppState.addEventListener('blur', () => { // Only want to use useNativeDriver on Android. It has strange flashes issue on IOS // https://github.com/react-native-modal/react-native-modal#the-modal-flashes-in-a-weird-way-when-animating -function Modal(props: BaseModalProps) { +function Modal({useNativeDriver = true, ...rest}: BaseModalProps) { return ( - {props.children} + {rest.children} ); } From a5e8a581860dd9e3a9d54f6cb95bc7371342f28d Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 9 Nov 2023 09:31:50 +0100 Subject: [PATCH 23/25] add optional chaining to onModalShow --- src/components/Modal/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx index 47da7256abf3..3d8a30ba3e2a 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -35,7 +35,7 @@ function Modal(props: BaseModalProps) { setStatusBarColor(isFullScreenModal ? themeColors.appBG : StyleUtils.getThemeBackgroundColor(statusBarColor)); } - props.onModalShow(); + props.onModalShow?.(); }; return ( From 20e80d13a434ac8bc2876394a92cf3c54f557e3a Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 13 Nov 2023 09:28:17 +0100 Subject: [PATCH 24/25] destructure props in Modal --- src/components/Modal/index.ios.tsx | 6 +++--- src/components/Modal/index.tsx | 17 +++++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/components/Modal/index.ios.tsx b/src/components/Modal/index.ios.tsx index 6952392a6326..f780775ec216 100644 --- a/src/components/Modal/index.ios.tsx +++ b/src/components/Modal/index.ios.tsx @@ -3,13 +3,13 @@ import withWindowDimensions from '@components/withWindowDimensions'; import BaseModal from './BaseModal'; import BaseModalProps from './types'; -function Modal(props: BaseModalProps) { +function Modal({children, ...rest}: BaseModalProps) { return ( - {props.children} + {children} ); } diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx index 3d8a30ba3e2a..a3d21d67b9af 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -7,11 +7,11 @@ import CONST from '@src/CONST'; import BaseModal from './BaseModal'; import BaseModalProps from './types'; -function Modal(props: BaseModalProps) { +function Modal({fullscreen, onModalHide, type, onModalShow, children, ...rest}: BaseModalProps) { const [previousStatusBarColor, setPreviousStatusBarColor] = useState(); const setStatusBarColor = (color = themeColors.appBG) => { - if (!props.fullscreen) { + if (!fullscreen) { return; } @@ -20,14 +20,13 @@ function Modal(props: BaseModalProps) { const hideModal = () => { setStatusBarColor(previousStatusBarColor); - props.onModalHide(); + onModalHide(); }; const showModal = () => { const statusBarColor = StatusBar.getBackgroundColor(); - const isFullScreenModal = - props.type === CONST.MODAL.MODAL_TYPE.CENTERED || props.type === CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE || props.type === CONST.MODAL.MODAL_TYPE.RIGHT_DOCKED; + const isFullScreenModal = type === CONST.MODAL.MODAL_TYPE.CENTERED || type === CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE || type === CONST.MODAL.MODAL_TYPE.RIGHT_DOCKED; if (statusBarColor) { setPreviousStatusBarColor(statusBarColor); @@ -35,18 +34,20 @@ function Modal(props: BaseModalProps) { setStatusBarColor(isFullScreenModal ? themeColors.appBG : StyleUtils.getThemeBackgroundColor(statusBarColor)); } - props.onModalShow?.(); + onModalShow?.(); }; return ( - {props.children} + {children} ); } From 434e472ded14000ceb318e25176940523b11ed82 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Tue, 14 Nov 2023 09:28:02 +0100 Subject: [PATCH 25/25] bring back default props --- src/components/Modal/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx index a3d21d67b9af..b4cfc1f06211 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -7,7 +7,7 @@ import CONST from '@src/CONST'; import BaseModal from './BaseModal'; import BaseModalProps from './types'; -function Modal({fullscreen, onModalHide, type, onModalShow, children, ...rest}: BaseModalProps) { +function Modal({fullscreen = true, onModalHide = () => {}, type, onModalShow = () => {}, children, ...rest}: BaseModalProps) { const [previousStatusBarColor, setPreviousStatusBarColor] = useState(); const setStatusBarColor = (color = themeColors.appBG) => {