From c66b616d139c052eb49d159e4449b044ead3fcac Mon Sep 17 00:00:00 2001 From: mhawryluk Date: Mon, 25 Aug 2025 17:52:58 +0200 Subject: [PATCH 1/2] fix: Modal animation callbacks not called with reduced motion on native apps --- .../Modal/ReanimatedModal/Container/index.tsx | 22 +++++++++++++------ .../ReanimatedModal/Container/index.web.tsx | 14 +++++++++++- .../Modal/ReanimatedModal/index.tsx | 7 +++--- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/components/Modal/ReanimatedModal/Container/index.tsx b/src/components/Modal/ReanimatedModal/Container/index.tsx index 3d944c8f534a..266a6d8f0f21 100644 --- a/src/components/Modal/ReanimatedModal/Container/index.tsx +++ b/src/components/Modal/ReanimatedModal/Container/index.tsx @@ -1,4 +1,4 @@ -import React, {useMemo} from 'react'; +import React, {useEffect, useMemo} from 'react'; import {View} from 'react-native'; import Animated, {Keyframe, runOnJS} from 'react-native-reanimated'; import type ReanimatedModalProps from '@components/Modal/ReanimatedModal/types'; @@ -27,12 +27,8 @@ function Container({ const Entering = useMemo(() => { const AnimationIn = new Keyframe(getModalInAnimation(animationIn)); - return AnimationIn.duration(animationInTiming).withCallback(() => { - 'worklet'; - - runOnJS(onOpenCallBack)(); - }); - }, [animationIn, animationInTiming, onOpenCallBack]); + return AnimationIn.duration(animationInTiming); + }, [animationIn, animationInTiming]); const Exiting = useMemo(() => { const AnimationOut = new Keyframe(getModalOutAnimation(animationOut)); @@ -44,6 +40,18 @@ function Container({ }); }, [animationOutTiming, onCloseCallBack, animationOut]); + // temporary solution to run animation callbacks even with reduced motion setting turned on + // since .reduceMotion method doesn't work in current version of Reanimated (https://github.com/software-mansion/react-native-reanimated/issues/8046) + useEffect(() => { + setTimeout(onOpenCallBack, animationInTiming); + + return () => { + setTimeout(onCloseCallBack, animationOutTiming); + }; + // calling callbacks only when the layout animations are run - on mount and unmount + // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps + }, []); + return ( onCloseCallbackRef.current()) + // ensuring the callback is called even with reduced motion setting turned on .reduceMotion(ReduceMotion.Never), [animationOutTiming, animationOut], ); diff --git a/src/components/Modal/ReanimatedModal/index.tsx b/src/components/Modal/ReanimatedModal/index.tsx index 8646a0b82dda..d13b2d336e42 100644 --- a/src/components/Modal/ReanimatedModal/index.tsx +++ b/src/components/Modal/ReanimatedModal/index.tsx @@ -121,9 +121,10 @@ function ReanimatedModal({ // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [isVisible, isContainerOpen, isTransitioning]); - const backdropStyle: ViewStyle = useMemo(() => { - return {width: windowWidth, height: windowHeight, backgroundColor: backdropColor}; - }, [windowWidth, windowHeight, backdropColor]); + const backdropStyle: ViewStyle = useMemo( + () => ({width: windowWidth, height: windowHeight, backgroundColor: backdropColor, opacity: backdropOpacity}), + [windowWidth, windowHeight, backdropColor, backdropOpacity], + ); const onOpenCallBack = useCallback(() => { setIsTransitioning(false); From be194903aaad1f854bf5733434b6157887de0852 Mon Sep 17 00:00:00 2001 From: Marcin Hawryluk <70582973+mhawryluk@users.noreply.github.com> Date: Tue, 26 Aug 2025 10:00:16 +0200 Subject: [PATCH 2/2] Update src/components/Modal/ReanimatedModal/Container/index.tsx Co-authored-by: Vit Horacek <36083550+mountiny@users.noreply.github.com> --- src/components/Modal/ReanimatedModal/Container/index.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/Modal/ReanimatedModal/Container/index.tsx b/src/components/Modal/ReanimatedModal/Container/index.tsx index 266a6d8f0f21..2e22d6fa8146 100644 --- a/src/components/Modal/ReanimatedModal/Container/index.tsx +++ b/src/components/Modal/ReanimatedModal/Container/index.tsx @@ -40,8 +40,9 @@ function Container({ }); }, [animationOutTiming, onCloseCallBack, animationOut]); - // temporary solution to run animation callbacks even with reduced motion setting turned on - // since .reduceMotion method doesn't work in current version of Reanimated (https://github.com/software-mansion/react-native-reanimated/issues/8046) + // Temporary solution to run animation callbacks even with reduced motion setting turned on + // since .reduceMotion method doesn't work in the current version of Reanimated (https://github.com/software-mansion/react-native-reanimated/issues/8046) + // We will remove this once fixed upstream https://github.com/Expensify/App/issues/69190 useEffect(() => { setTimeout(onOpenCallBack, animationInTiming);