diff --git a/src/libs/VisualViewport/index.js b/src/libs/VisualViewport/index.js index cc6be038209c..efb9dbebd5e3 100644 --- a/src/libs/VisualViewport/index.js +++ b/src/libs/VisualViewport/index.js @@ -13,4 +13,22 @@ function addViewportResizeListener(onViewportResize) { return () => window.visualViewport.removeEventListener('resize', onViewportResize); } -export default addViewportResizeListener; +/** + * Add a visual viewport scroll listener if available. Return a function to remove the listener. + * + * @param {Function} onViewportScroll + * @returns {Function} + */ +function addViewportScrollListener(onViewportScroll) { + if (!window.visualViewport) { + return () => {}; + } + + window.visualViewport.addEventListener('scroll', onViewportScroll); + return () => window.visualViewport.removeEventListener('scroll', onViewportScroll); +} + +export default { + addViewportResizeListener, + addViewportScrollListener, +}; diff --git a/src/libs/VisualViewport/index.native.js b/src/libs/VisualViewport/index.native.js index 823e3c1feef6..6f661196a3db 100644 --- a/src/libs/VisualViewport/index.native.js +++ b/src/libs/VisualViewport/index.native.js @@ -1,4 +1,3 @@ - /** * Visual Viewport is not available on native, so return an empty function. * @@ -8,4 +7,16 @@ function addViewportResizeListener() { return () => {}; } -export default addViewportResizeListener; +/** + * Visual Viewport is not available on native, so return an empty function. + * + * @returns {Function} + */ +function addViewportScrollListener() { + return () => {}; +} + +export default { + addViewportResizeListener, + addViewportScrollListener, +}; diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 8b5d746e1f47..0e41b5cc237f 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -20,7 +20,7 @@ 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 VisualViewport from '../../libs/VisualViewport'; import {withNetwork} from '../../components/OnyxProvider'; import compose from '../../libs/compose'; import networkPropTypes from '../../components/networkPropTypes'; @@ -111,6 +111,7 @@ class ReportScreen extends React.Component { this.chatWithAccountManager = this.chatWithAccountManager.bind(this); this.dismissBanner = this.dismissBanner.bind(this); this.removeViewportResizeListener = () => {}; + this.removeViewportScrollListener = () => {}; this.state = { skeletonViewContainerHeight: reportActionsListViewHeight, @@ -122,7 +123,14 @@ class ReportScreen extends React.Component { componentDidMount() { this.fetchReportIfNeeded(); toggleReportActionComposeView(true); - this.removeViewportResizeListener = addViewportResizeListener(this.updateViewportOffsetTop); + + // In order to keep the header always at the top + // we add two events listeners to detect the change in the visual viewport and apply marginTop = offsetTop + // Usually the resize event would be enough however on Chrome the offsetTop is changed after resize i.e. on scroll + // More info: https://github.com/Expensify/App/issues/13491 + this.removeViewportResizeListener = VisualViewport.addViewportResizeListener(this.updateViewportOffsetTop); + this.removeViewportScrollListener = VisualViewport.addViewportScrollListener(this.updateViewportOffsetTop); + Navigation.setIsReportScreenIsReady(); } @@ -137,6 +145,7 @@ class ReportScreen extends React.Component { componentWillUnmount() { this.removeViewportResizeListener(); + this.removeViewportScrollListener(); } /**