From 48b5419b3c0266f852c30c306e75ab5a316c6408 Mon Sep 17 00:00:00 2001 From: Cong Pham Date: Tue, 26 Dec 2023 09:38:00 +0700 Subject: [PATCH 1/3] 33546 visual viewport deplay --- src/components/ScreenWrapper.tsx | 2 +- src/hooks/useWindowDimensions/index.ts | 83 +++++++++++++++++++++++--- 2 files changed, 75 insertions(+), 10 deletions(-) diff --git a/src/components/ScreenWrapper.tsx b/src/components/ScreenWrapper.tsx index 0653e2ff8577..97bff2a6b3bb 100644 --- a/src/components/ScreenWrapper.tsx +++ b/src/components/ScreenWrapper.tsx @@ -118,7 +118,7 @@ function ScreenWrapper( */ const navigationFallback = useNavigation>(); const navigation = navigationProp ?? navigationFallback; - const {windowHeight, isSmallScreenWidth} = useWindowDimensions(); + const { windowHeight, isSmallScreenWidth } = useWindowDimensions(shouldEnableMaxHeight); const {initialHeight} = useInitialDimensions(); const styles = useThemeStyles(); const keyboardState = useKeyboardState(); diff --git a/src/hooks/useWindowDimensions/index.ts b/src/hooks/useWindowDimensions/index.ts index b0a29e9f901b..d0cc34701630 100644 --- a/src/hooks/useWindowDimensions/index.ts +++ b/src/hooks/useWindowDimensions/index.ts @@ -1,13 +1,21 @@ +import {useEffect, useMemo, useRef, useState} from 'react'; // eslint-disable-next-line no-restricted-imports import {Dimensions, useWindowDimensions} from 'react-native'; +import * as Browser from '@libs/Browser'; import variables from '@styles/variables'; import type WindowDimensions from './types'; +const initalViewportHeight = window.visualViewport?.height ?? window.innerHeight; +const tagNamesOpenKeyboard = ['INPUT', 'TEXTAREA']; + /** * A convenience wrapper around React Native's useWindowDimensions hook that also provides booleans for our breakpoints. */ -export default function (): WindowDimensions { +export default function (isCachedViewportHeight = false): WindowDimensions { + const shouldAwareVitualViewportHeight = isCachedViewportHeight && Browser.isMobileSafari(); + const cachedViewportHeightWithKeyboardRef = useRef(initalViewportHeight); const {width: windowWidth, height: windowHeight} = useWindowDimensions(); + // When the soft keyboard opens on mWeb, the window height changes. Use static screen height instead to get real screenHeight. const screenHeight = Dimensions.get('screen').height; const isExtraSmallScreenHeight = screenHeight <= variables.extraSmallMobileResponsiveHeightBreakpoint; @@ -15,12 +23,69 @@ export default function (): WindowDimensions { const isMediumScreenWidth = windowWidth > variables.mobileResponsiveWidthBreakpoint && windowWidth <= variables.tabletResponsiveWidthBreakpoint; const isLargeScreenWidth = windowWidth > variables.tabletResponsiveWidthBreakpoint; - return { - windowWidth, - windowHeight, - isExtraSmallScreenHeight, - isSmallScreenWidth, - isMediumScreenWidth, - isLargeScreenWidth, - }; + const [cachedViewportHeight, setCachedViewportHeight] = useState(windowHeight); + + const handleFocusIn = useRef((event: FocusEvent) => { + const targetElement = event.target as HTMLElement; + if (tagNamesOpenKeyboard.includes(targetElement.tagName)) { + setCachedViewportHeight(cachedViewportHeightWithKeyboardRef.current); + } + }); + + // eslint-disable-next-line rulesdir/prefer-early-return + useEffect(() => { + if (shouldAwareVitualViewportHeight) { + window.addEventListener('focusin', handleFocusIn.current); + return () => { + // eslint-disable-next-line react-hooks/exhaustive-deps + window.removeEventListener('focusin', handleFocusIn.current); + }; + } + }, [shouldAwareVitualViewportHeight]); + + const handleFocusOut = useRef((event: FocusEvent) => { + const targetElement = event.target as HTMLElement; + if (tagNamesOpenKeyboard.includes(targetElement.tagName)) { + setCachedViewportHeight(initalViewportHeight); + } + }); + + // eslint-disable-next-line rulesdir/prefer-early-return + useEffect(() => { + if (shouldAwareVitualViewportHeight) { + window.addEventListener('focusout', handleFocusOut.current); + return () => { + // eslint-disable-next-line react-hooks/exhaustive-deps + window.removeEventListener('focusout', handleFocusOut.current); + }; + } + }, [shouldAwareVitualViewportHeight]); + + // eslint-disable-next-line rulesdir/prefer-early-return + useEffect(() => { + if (shouldAwareVitualViewportHeight && windowHeight < cachedViewportHeightWithKeyboardRef.current) { + setCachedViewportHeight(windowHeight); + } + }, [windowHeight, shouldAwareVitualViewportHeight]); + + // eslint-disable-next-line rulesdir/prefer-early-return + useEffect(() => { + if (shouldAwareVitualViewportHeight && window.matchMedia('(orientation: portrait)').matches) { + if (windowHeight < initalViewportHeight) { + cachedViewportHeightWithKeyboardRef.current = windowHeight; + } + } + }, [shouldAwareVitualViewportHeight, windowHeight]); + + return useMemo( + () => ({ + windowWidth, + windowHeight: shouldAwareVitualViewportHeight ? cachedViewportHeight : windowHeight, + isExtraSmallScreenHeight, + isSmallScreenWidth, + isMediumScreenWidth, + isLargeScreenWidth, + }), + [windowWidth, shouldAwareVitualViewportHeight, cachedViewportHeight, windowHeight, isExtraSmallScreenHeight, isSmallScreenWidth, isMediumScreenWidth, isLargeScreenWidth], + ); } From 5ae984d2c65f9a9ff2f510adab244f43fb3f72f0 Mon Sep 17 00:00:00 2001 From: Cong Pham Date: Wed, 10 Jan 2024 22:30:34 +0700 Subject: [PATCH 2/3] 33546 cleanup prefer-early-return --- src/components/ScreenWrapper.tsx | 2 +- src/hooks/useWindowDimensions/index.ts | 73 ++++++++++++-------------- 2 files changed, 35 insertions(+), 40 deletions(-) diff --git a/src/components/ScreenWrapper.tsx b/src/components/ScreenWrapper.tsx index 97bff2a6b3bb..5b1ad95b6554 100644 --- a/src/components/ScreenWrapper.tsx +++ b/src/components/ScreenWrapper.tsx @@ -118,7 +118,7 @@ function ScreenWrapper( */ const navigationFallback = useNavigation>(); const navigation = navigationProp ?? navigationFallback; - const { windowHeight, isSmallScreenWidth } = useWindowDimensions(shouldEnableMaxHeight); + const {windowHeight, isSmallScreenWidth} = useWindowDimensions(shouldEnableMaxHeight); const {initialHeight} = useInitialDimensions(); const styles = useThemeStyles(); const keyboardState = useKeyboardState(); diff --git a/src/hooks/useWindowDimensions/index.ts b/src/hooks/useWindowDimensions/index.ts index d0cc34701630..0b805202ede8 100644 --- a/src/hooks/useWindowDimensions/index.ts +++ b/src/hooks/useWindowDimensions/index.ts @@ -11,8 +11,8 @@ const tagNamesOpenKeyboard = ['INPUT', 'TEXTAREA']; /** * A convenience wrapper around React Native's useWindowDimensions hook that also provides booleans for our breakpoints. */ -export default function (isCachedViewportHeight = false): WindowDimensions { - const shouldAwareVitualViewportHeight = isCachedViewportHeight && Browser.isMobileSafari(); +export default function (useCachedViewportHeight = false): WindowDimensions { + const isCachedViewportHeight = useCachedViewportHeight && Browser.isMobileSafari(); const cachedViewportHeightWithKeyboardRef = useRef(initalViewportHeight); const {width: windowWidth, height: windowHeight} = useWindowDimensions(); @@ -32,16 +32,16 @@ export default function (isCachedViewportHeight = false): WindowDimensions { } }); - // eslint-disable-next-line rulesdir/prefer-early-return useEffect(() => { - if (shouldAwareVitualViewportHeight) { - window.addEventListener('focusin', handleFocusIn.current); - return () => { - // eslint-disable-next-line react-hooks/exhaustive-deps - window.removeEventListener('focusin', handleFocusIn.current); - }; + if (!isCachedViewportHeight) { + return; } - }, [shouldAwareVitualViewportHeight]); + window.addEventListener('focusin', handleFocusIn.current); + return () => { + // eslint-disable-next-line react-hooks/exhaustive-deps + window.removeEventListener('focusin', handleFocusIn.current); + }; + }, [isCachedViewportHeight]); const handleFocusOut = useRef((event: FocusEvent) => { const targetElement = event.target as HTMLElement; @@ -50,42 +50,37 @@ export default function (isCachedViewportHeight = false): WindowDimensions { } }); - // eslint-disable-next-line rulesdir/prefer-early-return useEffect(() => { - if (shouldAwareVitualViewportHeight) { - window.addEventListener('focusout', handleFocusOut.current); - return () => { - // eslint-disable-next-line react-hooks/exhaustive-deps - window.removeEventListener('focusout', handleFocusOut.current); - }; + if (!isCachedViewportHeight) { + return; } - }, [shouldAwareVitualViewportHeight]); + window.addEventListener('focusout', handleFocusOut.current); + return () => { + // eslint-disable-next-line react-hooks/exhaustive-deps + window.removeEventListener('focusout', handleFocusOut.current); + }; + }, [isCachedViewportHeight]); - // eslint-disable-next-line rulesdir/prefer-early-return useEffect(() => { - if (shouldAwareVitualViewportHeight && windowHeight < cachedViewportHeightWithKeyboardRef.current) { - setCachedViewportHeight(windowHeight); + if (!isCachedViewportHeight && windowHeight >= cachedViewportHeightWithKeyboardRef.current) { + return; } - }, [windowHeight, shouldAwareVitualViewportHeight]); + setCachedViewportHeight(windowHeight); + }, [windowHeight, isCachedViewportHeight]); - // eslint-disable-next-line rulesdir/prefer-early-return useEffect(() => { - if (shouldAwareVitualViewportHeight && window.matchMedia('(orientation: portrait)').matches) { - if (windowHeight < initalViewportHeight) { - cachedViewportHeightWithKeyboardRef.current = windowHeight; - } + if (!isCachedViewportHeight || !window.matchMedia('(orientation: portrait)').matches || windowHeight >= initalViewportHeight) { + return; } - }, [shouldAwareVitualViewportHeight, windowHeight]); + cachedViewportHeightWithKeyboardRef.current = windowHeight; + }, [isCachedViewportHeight, windowHeight]); - return useMemo( - () => ({ - windowWidth, - windowHeight: shouldAwareVitualViewportHeight ? cachedViewportHeight : windowHeight, - isExtraSmallScreenHeight, - isSmallScreenWidth, - isMediumScreenWidth, - isLargeScreenWidth, - }), - [windowWidth, shouldAwareVitualViewportHeight, cachedViewportHeight, windowHeight, isExtraSmallScreenHeight, isSmallScreenWidth, isMediumScreenWidth, isLargeScreenWidth], - ); + return { + windowWidth, + windowHeight: isCachedViewportHeight ? cachedViewportHeight : windowHeight, + isExtraSmallScreenHeight, + isSmallScreenWidth, + isMediumScreenWidth, + isLargeScreenWidth, + }; } From 1064c3248a590dd44c11e6d3de27f73b8769d950 Mon Sep 17 00:00:00 2001 From: Cong Pham Date: Wed, 10 Jan 2024 23:23:20 +0700 Subject: [PATCH 3/3] fix eslint --- src/hooks/useWindowDimensions/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useWindowDimensions/index.ts b/src/hooks/useWindowDimensions/index.ts index 0b805202ede8..4ba2c4ad9b41 100644 --- a/src/hooks/useWindowDimensions/index.ts +++ b/src/hooks/useWindowDimensions/index.ts @@ -1,4 +1,4 @@ -import {useEffect, useMemo, useRef, useState} from 'react'; +import {useEffect, useRef, useState} from 'react'; // eslint-disable-next-line no-restricted-imports import {Dimensions, useWindowDimensions} from 'react-native'; import * as Browser from '@libs/Browser';