From aa59e9053d6b0c9a6368621de64a171f9e4aeca6 Mon Sep 17 00:00:00 2001 From: Linh Vo Date: Wed, 25 Dec 2024 14:05:58 +0700 Subject: [PATCH 1/3] fix: android native tooltip appears broken --- .../BaseGenericTooltip/index.native.tsx | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/components/Tooltip/BaseGenericTooltip/index.native.tsx b/src/components/Tooltip/BaseGenericTooltip/index.native.tsx index b48cb55ccea1..326d8dbf042b 100644 --- a/src/components/Tooltip/BaseGenericTooltip/index.native.tsx +++ b/src/components/Tooltip/BaseGenericTooltip/index.native.tsx @@ -1,9 +1,9 @@ import {Portal} from '@gorhom/portal'; import React, {useMemo, useRef, useState} from 'react'; -import {InteractionManager, View} from 'react-native'; +import {View} from 'react-native'; // eslint-disable-next-line no-restricted-imports import type {View as RNView} from 'react-native'; -import Animated, {useAnimatedStyle} from 'react-native-reanimated'; +import Animated, {useAnimatedStyle, useSharedValue} from 'react-native-reanimated'; import TransparentOverlay from '@components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/TransparentOverlay/TransparentOverlay'; import Text from '@components/Text'; import useStyleUtils from '@hooks/useStyleUtils'; @@ -40,10 +40,12 @@ function BaseGenericTooltip({ // The width of tooltip's inner content. Has to be undefined in the beginning // as a width of 0 will cause the content to be rendered of a width of 0, // which prevents us from measuring it correctly. - const [contentMeasuredWidth, setContentMeasuredWidth] = useState(); + const [contentMeasuredWidthState, setContentMeasuredWidth] = useState(); + const contentMeasuredWidth = useSharedValue(0); // The height of tooltip's wrapper. - const [wrapperMeasuredHeight, setWrapperMeasuredHeight] = useState(); + const [wrapperMeasuredHeightState, setWrapperMeasuredHeight] = useState(); + const wrapperMeasuredHeight = useSharedValue(0); const rootWrapper = useRef(null); const StyleUtils = useStyleUtils(); @@ -58,8 +60,8 @@ function BaseGenericTooltip({ tooltipTargetWidth: targetWidth, tooltipTargetHeight: targetHeight, maxWidth, - tooltipContentWidth: contentMeasuredWidth, - tooltipWrapperHeight: wrapperMeasuredHeight, + tooltipContentWidth: contentMeasuredWidthState, + tooltipWrapperHeight: wrapperMeasuredHeightState, manualShiftHorizontal: shiftHorizontal, manualShiftVertical: shiftVertical, shouldForceRenderingBelow, @@ -75,8 +77,8 @@ function BaseGenericTooltip({ targetWidth, targetHeight, maxWidth, - contentMeasuredWidth, - wrapperMeasuredHeight, + contentMeasuredWidthState, + wrapperMeasuredHeightState, shiftHorizontal, shiftVertical, shouldForceRenderingBelow, @@ -86,7 +88,7 @@ function BaseGenericTooltip({ ); const animationStyle = useAnimatedStyle(() => { - return StyleUtils.getTooltipAnimatedStyles({tooltipContentWidth: contentMeasuredWidth, tooltipWrapperHeight: wrapperMeasuredHeight, currentSize: animation}); + return StyleUtils.getTooltipAnimatedStyles({tooltipContentWidth: contentMeasuredWidth.get(), tooltipWrapperHeight: wrapperMeasuredHeight.get(), currentSize: animation}); }); let content; @@ -110,20 +112,16 @@ function BaseGenericTooltip({ ref={rootWrapper} style={[rootWrapperStyle, animationStyle]} onLayout={(e) => { - const {height} = e.nativeEvent.layout; - if (height === wrapperMeasuredHeight) { + const {height, width} = e.nativeEvent.layout; + if (height === wrapperMeasuredHeight.get()) { return; } + setContentMeasuredWidth(width); setWrapperMeasuredHeight(height); - // When tooltip is used inside an animated view (e.g. popover), we need to wait for the animation to finish before measuring content. - const target = e.target; - setTimeout(() => { - InteractionManager.runAfterInteractions(() => { - target.measure((x, y, width) => { - setContentMeasuredWidth(width); - }); - }); - }, CONST.ANIMATED_TRANSITION); + // To avoid unnecessary re-renders of the content container when passing state values to useAnimatedStyle, + // we use SharedValue for managing content and wrapper measurements. + contentMeasuredWidth.set(width); + wrapperMeasuredHeight.set(height); }} > {content} From 95e067ce3064eadec15d2b02609919adeaf69c34 Mon Sep 17 00:00:00 2001 From: Linh Vo Date: Wed, 25 Dec 2024 14:30:05 +0700 Subject: [PATCH 2/3] fix: rename animated value --- .../BaseGenericTooltip/index.native.tsx | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/components/Tooltip/BaseGenericTooltip/index.native.tsx b/src/components/Tooltip/BaseGenericTooltip/index.native.tsx index 326d8dbf042b..e798fb6fca9b 100644 --- a/src/components/Tooltip/BaseGenericTooltip/index.native.tsx +++ b/src/components/Tooltip/BaseGenericTooltip/index.native.tsx @@ -1,14 +1,14 @@ -import {Portal} from '@gorhom/portal'; -import React, {useMemo, useRef, useState} from 'react'; -import {View} from 'react-native'; +import { Portal } from '@gorhom/portal'; +import React, { useMemo, useRef, useState } from 'react'; +import { View } from 'react-native'; // eslint-disable-next-line no-restricted-imports -import type {View as RNView} from 'react-native'; -import Animated, {useAnimatedStyle, useSharedValue} from 'react-native-reanimated'; +import type { View as RNView } from 'react-native'; +import Animated, { useAnimatedStyle, useSharedValue } from 'react-native-reanimated'; import TransparentOverlay from '@components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/TransparentOverlay/TransparentOverlay'; import Text from '@components/Text'; import useStyleUtils from '@hooks/useStyleUtils'; import CONST from '@src/CONST'; -import type {BaseGenericTooltipProps} from './types'; +import type { BaseGenericTooltipProps } from './types'; // Props will change frequently. // On every tooltip hover, we update the position in state which will result in re-rendering. @@ -35,21 +35,21 @@ function BaseGenericTooltip({ }, wrapperStyle = {}, shouldUseOverlay = false, - onHideTooltip = () => {}, + onHideTooltip = () => { }, }: BaseGenericTooltipProps) { // The width of tooltip's inner content. Has to be undefined in the beginning // as a width of 0 will cause the content to be rendered of a width of 0, // which prevents us from measuring it correctly. const [contentMeasuredWidthState, setContentMeasuredWidth] = useState(); - const contentMeasuredWidth = useSharedValue(0); + const contentMeasuredWidthAnimated = useSharedValue(0); // The height of tooltip's wrapper. const [wrapperMeasuredHeightState, setWrapperMeasuredHeight] = useState(); - const wrapperMeasuredHeight = useSharedValue(0); + const wrapperMeasuredHeightAnimated = useSharedValue(0); const rootWrapper = useRef(null); const StyleUtils = useStyleUtils(); - const {rootWrapperStyle, textStyle, pointerWrapperStyle, pointerStyle} = useMemo( + const { rootWrapperStyle, textStyle, pointerWrapperStyle, pointerStyle } = useMemo( () => StyleUtils.getTooltipStyles({ // eslint-disable-next-line react-compiler/react-compiler @@ -88,7 +88,7 @@ function BaseGenericTooltip({ ); const animationStyle = useAnimatedStyle(() => { - return StyleUtils.getTooltipAnimatedStyles({tooltipContentWidth: contentMeasuredWidth.get(), tooltipWrapperHeight: wrapperMeasuredHeight.get(), currentSize: animation}); + return StyleUtils.getTooltipAnimatedStyles({ tooltipContentWidth: contentMeasuredWidthAnimated.get(), tooltipWrapperHeight: wrapperMeasuredHeightAnimated.get(), currentSize: animation }); }); let content; @@ -112,16 +112,17 @@ function BaseGenericTooltip({ ref={rootWrapper} style={[rootWrapperStyle, animationStyle]} onLayout={(e) => { - const {height, width} = e.nativeEvent.layout; - if (height === wrapperMeasuredHeight.get()) { + const { height, width } = e.nativeEvent.layout; + if (height === wrapperMeasuredHeightAnimated.get()) { return; } - setContentMeasuredWidth(width); - setWrapperMeasuredHeight(height); // To avoid unnecessary re-renders of the content container when passing state values to useAnimatedStyle, // we use SharedValue for managing content and wrapper measurements. - contentMeasuredWidth.set(width); - wrapperMeasuredHeight.set(height); + contentMeasuredWidthAnimated.set(width); + wrapperMeasuredHeightAnimated.set(height); + + setContentMeasuredWidth(width); + setWrapperMeasuredHeight(height); }} > {content} From 10812e3ae79c99888a4cff12ad4c8e7b6f5630d5 Mon Sep 17 00:00:00 2001 From: Linh Vo Date: Wed, 25 Dec 2024 14:34:38 +0700 Subject: [PATCH 3/3] chore: prettier --- .../BaseGenericTooltip/index.native.tsx | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/components/Tooltip/BaseGenericTooltip/index.native.tsx b/src/components/Tooltip/BaseGenericTooltip/index.native.tsx index e798fb6fca9b..f695ebaf0ca3 100644 --- a/src/components/Tooltip/BaseGenericTooltip/index.native.tsx +++ b/src/components/Tooltip/BaseGenericTooltip/index.native.tsx @@ -1,14 +1,14 @@ -import { Portal } from '@gorhom/portal'; -import React, { useMemo, useRef, useState } from 'react'; -import { View } from 'react-native'; +import {Portal} from '@gorhom/portal'; +import React, {useMemo, useRef, useState} from 'react'; +import {View} from 'react-native'; // eslint-disable-next-line no-restricted-imports -import type { View as RNView } from 'react-native'; -import Animated, { useAnimatedStyle, useSharedValue } from 'react-native-reanimated'; +import type {View as RNView} from 'react-native'; +import Animated, {useAnimatedStyle, useSharedValue} from 'react-native-reanimated'; import TransparentOverlay from '@components/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/TransparentOverlay/TransparentOverlay'; import Text from '@components/Text'; import useStyleUtils from '@hooks/useStyleUtils'; import CONST from '@src/CONST'; -import type { BaseGenericTooltipProps } from './types'; +import type {BaseGenericTooltipProps} from './types'; // Props will change frequently. // On every tooltip hover, we update the position in state which will result in re-rendering. @@ -35,7 +35,7 @@ function BaseGenericTooltip({ }, wrapperStyle = {}, shouldUseOverlay = false, - onHideTooltip = () => { }, + onHideTooltip = () => {}, }: BaseGenericTooltipProps) { // The width of tooltip's inner content. Has to be undefined in the beginning // as a width of 0 will cause the content to be rendered of a width of 0, @@ -49,7 +49,7 @@ function BaseGenericTooltip({ const rootWrapper = useRef(null); const StyleUtils = useStyleUtils(); - const { rootWrapperStyle, textStyle, pointerWrapperStyle, pointerStyle } = useMemo( + const {rootWrapperStyle, textStyle, pointerWrapperStyle, pointerStyle} = useMemo( () => StyleUtils.getTooltipStyles({ // eslint-disable-next-line react-compiler/react-compiler @@ -88,7 +88,11 @@ function BaseGenericTooltip({ ); const animationStyle = useAnimatedStyle(() => { - return StyleUtils.getTooltipAnimatedStyles({ tooltipContentWidth: contentMeasuredWidthAnimated.get(), tooltipWrapperHeight: wrapperMeasuredHeightAnimated.get(), currentSize: animation }); + return StyleUtils.getTooltipAnimatedStyles({ + tooltipContentWidth: contentMeasuredWidthAnimated.get(), + tooltipWrapperHeight: wrapperMeasuredHeightAnimated.get(), + currentSize: animation, + }); }); let content; @@ -112,7 +116,7 @@ function BaseGenericTooltip({ ref={rootWrapper} style={[rootWrapperStyle, animationStyle]} onLayout={(e) => { - const { height, width } = e.nativeEvent.layout; + const {height, width} = e.nativeEvent.layout; if (height === wrapperMeasuredHeightAnimated.get()) { return; }