diff --git a/src/libs/Navigation/FreezeWrapper.tsx b/src/libs/Navigation/FreezeWrapper/index.native.tsx similarity index 96% rename from src/libs/Navigation/FreezeWrapper.tsx rename to src/libs/Navigation/FreezeWrapper/index.native.tsx index fb5f769b19c..670544a0926 100644 --- a/src/libs/Navigation/FreezeWrapper.tsx +++ b/src/libs/Navigation/FreezeWrapper/index.native.tsx @@ -2,7 +2,7 @@ import {useIsFocused, useNavigation, useRoute} from '@react-navigation/native'; import React, {useEffect, useRef, useState} from 'react'; import {Freeze} from 'react-freeze'; import type ChildrenProps from '@src/types/utils/ChildrenProps'; -import shouldSetScreenBlurred from './shouldSetScreenBlurred'; +import shouldSetScreenBlurred from '../shouldSetScreenBlurred'; type FreezeWrapperProps = ChildrenProps & { /** Prop to disable freeze */ diff --git a/src/libs/Navigation/FreezeWrapper/index.tsx b/src/libs/Navigation/FreezeWrapper/index.tsx new file mode 100644 index 00000000000..4c4d54e610b --- /dev/null +++ b/src/libs/Navigation/FreezeWrapper/index.tsx @@ -0,0 +1,46 @@ +import {useIsFocused, useNavigation, useRoute} from '@react-navigation/native'; +import React, {useEffect, useLayoutEffect, useRef, useState} from 'react'; +import {Freeze} from 'react-freeze'; +import type ChildrenProps from '@src/types/utils/ChildrenProps'; +import shouldSetScreenBlurred from '../shouldSetScreenBlurred'; + +type FreezeWrapperProps = ChildrenProps & { + /** Prop to disable freeze */ + keepVisible?: boolean; +}; + +function FreezeWrapper({keepVisible = false, children}: FreezeWrapperProps) { + const [isScreenBlurred, setIsScreenBlurred] = useState(false); + const [freezed, setFreezed] = useState(false); + // we need to know the screen index to determine if the screen can be frozen + const screenIndexRef = useRef(null); + const isFocused = useIsFocused(); + const navigation = useNavigation(); + const currentRoute = useRoute(); + + useEffect(() => { + const index = navigation.getState()?.routes.findIndex((route) => route.key === currentRoute.key) ?? 0; + screenIndexRef.current = index; + // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + const unsubscribe = navigation.addListener('state', () => { + const navigationIndex = (navigation.getState()?.index ?? 0) - (screenIndexRef.current ?? 0); + setIsScreenBlurred(shouldSetScreenBlurred(navigationIndex)); + }); + return () => unsubscribe(); + }, [isFocused, isScreenBlurred, navigation]); + + // Decouple the Suspense render task so it won't be interuptted by React's concurrent mode + // and stuck in an infinite loop + useLayoutEffect(() => { + setFreezed(!isFocused && isScreenBlurred && !keepVisible); + }, [isFocused, isScreenBlurred, keepVisible]); + + return {children}; +} + +FreezeWrapper.displayName = 'FreezeWrapper'; + +export default FreezeWrapper;