From 2fc343c98bd96c91a2ee804a4cc6d34448aa518f Mon Sep 17 00:00:00 2001 From: Oliver Wilks Date: Wed, 1 Feb 2023 08:33:51 +0000 Subject: [PATCH 01/13] Created HOC for viewportOffsetTop --- src/components/withViewportOffsetTop.js | 69 +++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/components/withViewportOffsetTop.js diff --git a/src/components/withViewportOffsetTop.js b/src/components/withViewportOffsetTop.js new file mode 100644 index 000000000000..784f4867444d --- /dev/null +++ b/src/components/withViewportOffsetTop.js @@ -0,0 +1,69 @@ +import React, {Component} from 'react'; +import PropTypes from 'prop-types'; +import lodashGet from 'lodash/get'; +import getComponentDisplayName from '../libs/getComponentDisplayName'; +import addViewportResizeListener from '../libs/VisualViewport'; + +const viewportOffsetTopPropTypes = { + viewportOffsetTop: PropTypes.number.isRequired, +}; + +export default function (WrappedComponent) { + class WithViewportOffsetTop extends Component { + constructor(props) { + super(props); + + this.updateDimensions = this.updateDimensions.bind(this); + + this.state = { + viewportOffsetTop: 0, + }; + } + + componentDidMount() { + this.removeViewportResizeListener = addViewportResizeListener(this.updateDimensions); + } + + componentWillUnmount() { + this.removeViewportResizeListener(); + } + + /** + * @param {SyntheticEvent} e + */ + updateDimensions(e) { + const viewportOffsetTop = lodashGet(e, 'target.offsetTop', 0); + this.setState({viewportOffsetTop}); + } + + render() { + return ( + + ); + } + } + + WithViewportOffsetTop.displayName = `WithViewportOffsetTop(${getComponentDisplayName(WrappedComponent)})`; + WithViewportOffsetTop.propTypes = { + forwardedRef: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.shape({current: PropTypes.instanceOf(React.Component)}), + ]), + }; + WithViewportOffsetTop.defaultProps = { + forwardedRef: undefined, + }; + return React.forwardRef((props, ref) => ( + // eslint-disable-next-line react/jsx-props-no-spreading + + )); +} + +export { + viewportOffsetTopPropTypes, +}; From f4a1d49a60a5d47382f70b5a5e92b9a3607667df Mon Sep 17 00:00:00 2001 From: Oliver Wilks Date: Wed, 1 Feb 2023 08:35:55 +0000 Subject: [PATCH 02/13] Added disableCoverScreen prop for PasswordPopover --- src/components/Modal/BaseModal.js | 2 +- src/components/PasswordPopover/BasePasswordPopover.js | 1 + src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/Modal/BaseModal.js b/src/components/Modal/BaseModal.js index 323df19c7720..8530adc9ceed 100644 --- a/src/components/Modal/BaseModal.js +++ b/src/components/Modal/BaseModal.js @@ -105,7 +105,7 @@ class BaseModal extends PureComponent { backdropOpacity={hideBackdrop ? 0 : variables.overlayOpacity} backdropTransitionOutTiming={0} hasBackdrop={this.props.fullscreen} - coverScreen={this.props.fullscreen} + coverScreen={this.props.disableCoverScreen ? false : this.props.fullscreen} style={modalStyle} deviceHeight={this.props.windowHeight} deviceWidth={this.props.windowWidth} diff --git a/src/components/PasswordPopover/BasePasswordPopover.js b/src/components/PasswordPopover/BasePasswordPopover.js index d4d9fb5a9a82..56eb87f40cd4 100644 --- a/src/components/PasswordPopover/BasePasswordPopover.js +++ b/src/components/PasswordPopover/BasePasswordPopover.js @@ -51,6 +51,7 @@ class BasePasswordPopover extends Component { render() { return ( Date: Wed, 1 Feb 2023 08:37:36 +0000 Subject: [PATCH 03/13] Implemented withViewportOffsetTop HOC --- src/pages/home/ReportScreen.js | 22 ++++--------------- .../Payments/PaymentsPage/BasePaymentsPage.js | 5 ++++- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index d544d78a65e0..ca6b60695c70 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -20,7 +20,6 @@ import CONST from '../../CONST'; import ReportActionsSkeletonView from '../../components/ReportActionsSkeletonView'; import reportActionPropTypes from './report/reportActionPropTypes'; import toggleReportActionComposeView from '../../libs/toggleReportActionComposeView'; -import addViewportResizeListener from '../../libs/VisualViewport'; import {withNetwork} from '../../components/OnyxProvider'; import compose from '../../libs/compose'; import networkPropTypes from '../../components/networkPropTypes'; @@ -33,6 +32,7 @@ import withLocalize from '../../components/withLocalize'; import reportPropTypes from '../reportPropTypes'; import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView'; import ReportHeaderSkeletonView from '../../components/ReportHeaderSkeletonView'; +import withViewportOffsetTop, {viewportOffsetTopPropTypes} from '../../components/withViewportOffsetTop'; const propTypes = { /** Navigation route context info provided by react navigation */ @@ -73,6 +73,7 @@ const propTypes = { ...windowDimensionsPropTypes, ...withDrawerPropTypes, + ...viewportOffsetTopPropTypes, }; const defaultProps = { @@ -107,14 +108,11 @@ class ReportScreen extends React.Component { super(props); this.onSubmitComment = this.onSubmitComment.bind(this); - this.updateViewportOffsetTop = this.updateViewportOffsetTop.bind(this); this.chatWithAccountManager = this.chatWithAccountManager.bind(this); this.dismissBanner = this.dismissBanner.bind(this); - this.removeViewportResizeListener = () => {}; this.state = { skeletonViewContainerHeight: reportActionsListViewHeight, - viewportOffsetTop: 0, isBannerVisible: true, }; } @@ -122,7 +120,6 @@ class ReportScreen extends React.Component { componentDidMount() { this.fetchReportIfNeeded(); toggleReportActionComposeView(true); - this.removeViewportResizeListener = addViewportResizeListener(this.updateViewportOffsetTop); } componentDidUpdate(prevProps) { @@ -134,10 +131,6 @@ class ReportScreen extends React.Component { toggleReportActionComposeView(true); } - componentWillUnmount() { - this.removeViewportResizeListener(); - } - /** * @param {String} text */ @@ -171,14 +164,6 @@ class ReportScreen extends React.Component { Report.openReport(reportIDFromPath); } - /** - * @param {SyntheticEvent} e - */ - updateViewportOffsetTop(e) { - const viewportOffsetTop = lodashGet(e, 'target.offsetTop', 0); - this.setState({viewportOffsetTop}); - } - dismissBanner() { this.setState({isBannerVisible: false}); } @@ -214,7 +199,7 @@ class ReportScreen extends React.Component { const reportID = getReportID(this.props.route); const addWorkspaceRoomOrChatPendingAction = lodashGet(this.props.report, 'pendingFields.addWorkspaceRoom') || lodashGet(this.props.report, 'pendingFields.createChat'); const addWorkspaceRoomOrChatErrors = lodashGet(this.props.report, 'errorFields.addWorkspaceRoom') || lodashGet(this.props.report, 'errorFields.createChat'); - const screenWrapperStyle = [styles.appContent, styles.flex1, {marginTop: this.state.viewportOffsetTop}]; + const screenWrapperStyle = [styles.appContent, styles.flex1, {marginTop: this.props.viewportOffsetTop}]; // There are no reportActions at all to display and we are still in the process of loading the next set of actions. const isLoadingInitialReportActions = _.isEmpty(this.props.reportActions) && this.props.report.isLoadingReportActions; @@ -332,6 +317,7 @@ ReportScreen.propTypes = propTypes; ReportScreen.defaultProps = defaultProps; export default compose( + withViewportOffsetTop, withLocalize, withWindowDimensions, withDrawerState, diff --git a/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js b/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js index 8453da3adeea..b386eb823807 100644 --- a/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js +++ b/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js @@ -34,6 +34,7 @@ import OfflineWithFeedback from '../../../../components/OfflineWithFeedback'; import ConfirmContent from '../../../../components/ConfirmContent'; import Button from '../../../../components/Button'; import themeColors from '../../../../styles/themes/default'; +import withViewportOffsetTop from '../../../../components/withViewportOffsetTop'; class BasePaymentsPage extends React.Component { constructor(props) { @@ -341,8 +342,9 @@ class BasePaymentsPage extends React.Component { // Determines whether or not the modal popup is mounted from the bottom of the screen instead of the side mount on Web or Desktop screens const isPopoverBottomMount = this.state.anchorPositionTop === 0 || this.props.isSmallScreenWidth; + const screenWrapperStyle = [{marginTop: this.props.viewportOffsetTop}]; return ( - + Date: Wed, 1 Feb 2023 08:38:01 +0000 Subject: [PATCH 04/13] Set Android KeyboardSpacer to null --- .../KeyboardSpacer/index.android.js | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/src/components/KeyboardSpacer/index.android.js b/src/components/KeyboardSpacer/index.android.js index 8c549e056fe7..b3a8c6376d71 100644 --- a/src/components/KeyboardSpacer/index.android.js +++ b/src/components/KeyboardSpacer/index.android.js @@ -1,21 +1,9 @@ /** - * On Android the keyboard covers the input fields on the bottom of the view. This component moves the - * view up with the keyboard allowing the user to see what they are typing. + * On Android we do not need to implement a keyboard spacer, so we return a null component. + * + * @returns {null} + * @constructor */ -import React from 'react'; -import {StatusBar} from 'react-native'; -import BaseKeyboardSpacer from './BaseKeyboardSpacer'; -import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions'; +const KeyboardSpacer = () => null; -const KeyboardSpacer = () => ( - -); - -KeyboardSpacer.propTypes = windowDimensionsPropTypes; -KeyboardSpacer.displayName = 'KeyboardSpacer'; - -export default withWindowDimensions(KeyboardSpacer); +export default KeyboardSpacer; From a3cc3288eba974af2ea15387ce4dfa4cc2d05a3d Mon Sep 17 00:00:00 2001 From: Oliver Wilks Date: Wed, 1 Feb 2023 16:57:39 +0000 Subject: [PATCH 05/13] Deleted KeyboardSpacer index.android --- src/components/KeyboardSpacer/index.android.js | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 src/components/KeyboardSpacer/index.android.js diff --git a/src/components/KeyboardSpacer/index.android.js b/src/components/KeyboardSpacer/index.android.js deleted file mode 100644 index b3a8c6376d71..000000000000 --- a/src/components/KeyboardSpacer/index.android.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * On Android we do not need to implement a keyboard spacer, so we return a null component. - * - * @returns {null} - * @constructor - */ -const KeyboardSpacer = () => null; - -export default KeyboardSpacer; From 663c71b6f7d5ec7ad189e0f057b955fc2f4441c6 Mon Sep 17 00:00:00 2001 From: Oliver Wilks Date: Wed, 1 Feb 2023 17:05:14 +0000 Subject: [PATCH 06/13] Added comment for disableCoverScreen prop --- src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js b/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js index b386eb823807..4d37261c7aaf 100644 --- a/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js +++ b/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js @@ -480,6 +480,9 @@ class BasePaymentsPage extends React.Component { )} Date: Wed, 1 Feb 2023 17:21:01 +0000 Subject: [PATCH 07/13] Updated comment in KeyboardSpacer index --- src/components/KeyboardSpacer/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/KeyboardSpacer/index.js b/src/components/KeyboardSpacer/index.js index bee4d13e51e9..2b436ca78300 100644 --- a/src/components/KeyboardSpacer/index.js +++ b/src/components/KeyboardSpacer/index.js @@ -1,5 +1,5 @@ /** - * On non native platforms we do not need to implement a keyboard spacer, so we return a null component. + * On non iOS platforms we do not need to implement a keyboard spacer, so we return a null component. * * @returns {null} * @constructor From 99bd6a7aa55ca6f91ac84a72632c60315e36e6c4 Mon Sep 17 00:00:00 2001 From: Oliver Wilks Date: Wed, 1 Feb 2023 18:06:32 +0000 Subject: [PATCH 08/13] Added comment for viewPortOffsetTop prop --- src/components/withViewportOffsetTop.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/withViewportOffsetTop.js b/src/components/withViewportOffsetTop.js index 784f4867444d..2fcad4f48651 100644 --- a/src/components/withViewportOffsetTop.js +++ b/src/components/withViewportOffsetTop.js @@ -5,6 +5,9 @@ import getComponentDisplayName from '../libs/getComponentDisplayName'; import addViewportResizeListener from '../libs/VisualViewport'; const viewportOffsetTopPropTypes = { + // viewportOffsetTop returns the offset of the top edge of the visual viewport from the + // top edge of the layout viewport in CSS pixels, when the visual viewport is resized. + viewportOffsetTop: PropTypes.number.isRequired, }; From b09f1ebcd08f2075a5fcd994343cea6b7841261e Mon Sep 17 00:00:00 2001 From: Oliver Wilks Date: Fri, 3 Feb 2023 08:34:02 +0000 Subject: [PATCH 09/13] Revert disableCoverScreen and screenWrapper style --- src/components/Modal/BaseModal.js | 2 +- src/components/PasswordPopover/BasePasswordPopover.js | 1 - .../settings/Payments/PaymentsPage/BasePaymentsPage.js | 9 +-------- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/components/Modal/BaseModal.js b/src/components/Modal/BaseModal.js index 8530adc9ceed..323df19c7720 100644 --- a/src/components/Modal/BaseModal.js +++ b/src/components/Modal/BaseModal.js @@ -105,7 +105,7 @@ class BaseModal extends PureComponent { backdropOpacity={hideBackdrop ? 0 : variables.overlayOpacity} backdropTransitionOutTiming={0} hasBackdrop={this.props.fullscreen} - coverScreen={this.props.disableCoverScreen ? false : this.props.fullscreen} + coverScreen={this.props.fullscreen} style={modalStyle} deviceHeight={this.props.windowHeight} deviceWidth={this.props.windowWidth} diff --git a/src/components/PasswordPopover/BasePasswordPopover.js b/src/components/PasswordPopover/BasePasswordPopover.js index 56eb87f40cd4..d4d9fb5a9a82 100644 --- a/src/components/PasswordPopover/BasePasswordPopover.js +++ b/src/components/PasswordPopover/BasePasswordPopover.js @@ -51,7 +51,6 @@ class BasePasswordPopover extends Component { render() { return ( + Date: Fri, 3 Feb 2023 08:34:27 +0000 Subject: [PATCH 10/13] Revert keyboardSpacer changes --- .../KeyboardSpacer/index.android.js | 21 +++++++++++++++++++ src/components/KeyboardSpacer/index.js | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 src/components/KeyboardSpacer/index.android.js diff --git a/src/components/KeyboardSpacer/index.android.js b/src/components/KeyboardSpacer/index.android.js new file mode 100644 index 000000000000..8c549e056fe7 --- /dev/null +++ b/src/components/KeyboardSpacer/index.android.js @@ -0,0 +1,21 @@ +/** + * On Android the keyboard covers the input fields on the bottom of the view. This component moves the + * view up with the keyboard allowing the user to see what they are typing. + */ +import React from 'react'; +import {StatusBar} from 'react-native'; +import BaseKeyboardSpacer from './BaseKeyboardSpacer'; +import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions'; + +const KeyboardSpacer = () => ( + +); + +KeyboardSpacer.propTypes = windowDimensionsPropTypes; +KeyboardSpacer.displayName = 'KeyboardSpacer'; + +export default withWindowDimensions(KeyboardSpacer); diff --git a/src/components/KeyboardSpacer/index.js b/src/components/KeyboardSpacer/index.js index 2b436ca78300..bee4d13e51e9 100644 --- a/src/components/KeyboardSpacer/index.js +++ b/src/components/KeyboardSpacer/index.js @@ -1,5 +1,5 @@ /** - * On non iOS platforms we do not need to implement a keyboard spacer, so we return a null component. + * On non native platforms we do not need to implement a keyboard spacer, so we return a null component. * * @returns {null} * @constructor From 1359e055bb92756921c1a0d29a0b0f1d81d3ab9c Mon Sep 17 00:00:00 2001 From: Oliver Wilks Date: Fri, 3 Feb 2023 08:35:49 +0000 Subject: [PATCH 11/13] Added extraModalStyles prop --- src/components/Modal/BaseModal.js | 1 + src/components/PasswordPopover/BasePasswordPopover.js | 1 + src/styles/getModalStyles/getBaseModalStyles.js | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/Modal/BaseModal.js b/src/components/Modal/BaseModal.js index 323df19c7720..d3e5b27c8570 100644 --- a/src/components/Modal/BaseModal.js +++ b/src/components/Modal/BaseModal.js @@ -77,6 +77,7 @@ class BaseModal extends PureComponent { }, this.props.popoverAnchorPosition, this.props.containerStyle, + this.props.extraModalStyles, ); return ( { +export default (type, windowDimensions, popoverAnchorPosition = {}, containerStyle = {}, extraModalStyles = {}) => { const {isSmallScreenWidth, windowWidth} = windowDimensions; let modalStyle = { margin: 0, + ...extraModalStyles, }; let modalContainerStyle; From acca5353d48a26f7d5e31a64d65eb96353d32be2 Mon Sep 17 00:00:00 2001 From: Oliver Wilks Date: Fri, 3 Feb 2023 08:36:20 +0000 Subject: [PATCH 12/13] Added withViewportOffsetTop --- src/components/PasswordPopover/BasePasswordPopover.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/PasswordPopover/BasePasswordPopover.js b/src/components/PasswordPopover/BasePasswordPopover.js index 96e16fa579b0..76aeaa0f8190 100644 --- a/src/components/PasswordPopover/BasePasswordPopover.js +++ b/src/components/PasswordPopover/BasePasswordPopover.js @@ -11,6 +11,7 @@ import TextInput from '../TextInput'; import KeyboardSpacer from '../KeyboardSpacer'; import {propTypes as passwordPopoverPropTypes, defaultProps as passwordPopoverDefaultProps} from './passwordPopoverPropTypes'; import Button from '../Button'; +import withViewportOffsetTop from '../withViewportOffsetTop'; const propTypes = { /** Whether we should wait before focusing the TextInput, useful when using transitions on Android */ @@ -99,6 +100,7 @@ class BasePasswordPopover extends Component { BasePasswordPopover.propTypes = propTypes; BasePasswordPopover.defaultProps = defaultProps; export default compose( + withViewportOffsetTop, withWindowDimensions, withLocalize, )(BasePasswordPopover); From 34401d38f0cbee8d62e14eebe97a1f1b5b0a5672 Mon Sep 17 00:00:00 2001 From: Oliver Wilks Date: Thu, 9 Feb 2023 09:23:47 +0000 Subject: [PATCH 13/13] Change modal style prop names --- src/components/KeyboardShortcutsModal.js | 2 +- src/components/Modal/BaseModal.js | 4 ++-- src/components/Modal/modalPropTypes.js | 4 ++-- src/components/PasswordPopover/BasePasswordPopover.js | 2 +- src/styles/getModalStyles/getBaseModalStyles.js | 6 +++--- src/styles/getModalStyles/index.android.js | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/components/KeyboardShortcutsModal.js b/src/components/KeyboardShortcutsModal.js index c45ced82f11b..fc4f2e5cf17e 100644 --- a/src/components/KeyboardShortcutsModal.js +++ b/src/components/KeyboardShortcutsModal.js @@ -79,7 +79,7 @@ class KeyboardShortcutsModal extends React.Component { diff --git a/src/components/Modal/BaseModal.js b/src/components/Modal/BaseModal.js index 9c41516f58ce..dc29bc489618 100644 --- a/src/components/Modal/BaseModal.js +++ b/src/components/Modal/BaseModal.js @@ -76,8 +76,8 @@ class BaseModal extends PureComponent { isSmallScreenWidth: this.props.isSmallScreenWidth, }, this.props.popoverAnchorPosition, - this.props.containerStyle, - this.props.extraModalStyles, + this.props.innerContainerStyle, + this.props.outerStyle, ); return ( { +export default (type, windowDimensions, popoverAnchorPosition = {}, innerContainerStyle = {}, outerStyle = {}) => { const {isSmallScreenWidth, windowWidth} = windowDimensions; let modalStyle = { margin: 0, - ...extraModalStyles, + ...outerStyle, }; let modalContainerStyle; @@ -214,7 +214,7 @@ export default (type, windowDimensions, popoverAnchorPosition = {}, containerSty animationOut = 'slideOutDown'; } - modalContainerStyle = {...modalContainerStyle, ...containerStyle}; + modalContainerStyle = {...modalContainerStyle, ...innerContainerStyle}; return { modalStyle, diff --git a/src/styles/getModalStyles/index.android.js b/src/styles/getModalStyles/index.android.js index 9f2b0dae0720..69606478cca8 100644 --- a/src/styles/getModalStyles/index.android.js +++ b/src/styles/getModalStyles/index.android.js @@ -1,8 +1,8 @@ import getBaseModalStyles from './getBaseModalStyles'; // Only apply top padding on iOS since it's the only platform using SafeAreaView -export default (type, windowDimensions, popoverAnchorPosition = {}, containerStyle = {}) => ({ - ...getBaseModalStyles(type, windowDimensions, popoverAnchorPosition, containerStyle), +export default (type, windowDimensions, popoverAnchorPosition = {}, innerContainerStyle = {}) => ({ + ...getBaseModalStyles(type, windowDimensions, popoverAnchorPosition, innerContainerStyle), shouldAddTopSafeAreaMargin: false, shouldAddTopSafeAreaPadding: false, });