1- import React , { ReactNode , useEffect , useRef } from 'react' ;
1+ import React , { ReactNode , useEffect , useMemo , useRef } from 'react' ;
22import { Platform , View } from 'react-native' ;
33
44import Animated , { useAnimatedStyle , useSharedValue } from 'react-native-reanimated' ;
@@ -11,6 +11,7 @@ import {
1111 createClosingPortalLayoutRegistrationId ,
1212 setClosingPortalLayout ,
1313 useShouldTeleportToClosingPortal ,
14+ useOverlayController ,
1415} from '../../state-store' ;
1516
1617type PortalWhileClosingViewProps = {
@@ -31,6 +32,49 @@ type PortalWhileClosingViewProps = {
3132 portalName : string ;
3233} ;
3334
35+ const useSyncingApi = ( portalHostName : string , registrationId : string ) => {
36+ const containerRef = useRef < View | null > ( null ) ;
37+ const placeholderLayout = useSharedValue ( { h : 0 , w : 0 } ) ;
38+ const insets = useSafeAreaInsets ( ) ;
39+ const { id } = useOverlayController ( ) ;
40+
41+ const syncPortalLayout = useStableCallback ( ( ) => {
42+ if ( ! id ) {
43+ return ;
44+ }
45+
46+ containerRef . current ?. measureInWindow ( ( x , y , width , height ) => {
47+ const absolute = {
48+ x,
49+ y : y + ( Platform . OS === 'android' ? insets . top : 0 ) ,
50+ } ;
51+
52+ if ( ! width || ! height ) {
53+ return ;
54+ }
55+
56+ placeholderLayout . value = { h : height , w : width } ;
57+
58+ setClosingPortalLayout ( portalHostName , registrationId , {
59+ ...absolute ,
60+ h : height ,
61+ w : width ,
62+ } ) ;
63+ } ) ;
64+ } ) ;
65+
66+ useEffect ( ( ) => {
67+ if ( id ) {
68+ syncPortalLayout ( ) ;
69+ }
70+ } , [ insets . bottom , id , syncPortalLayout ] ) ;
71+
72+ return useMemo (
73+ ( ) => ( { syncPortalLayout, containerRef, placeholderLayout } ) ,
74+ [ placeholderLayout , syncPortalLayout ] ,
75+ ) ;
76+ } ;
77+
3478/**
3579 * Keeps wrapped UI above the message overlay during close animation by teleporting it to a closing portal host.
3680 *
@@ -67,54 +111,26 @@ export const PortalWhileClosingView = ({
67111 portalHostName,
68112 portalName,
69113} : PortalWhileClosingViewProps ) => {
70- const containerRef = useRef < View | null > ( null ) ;
71114 const registrationIdRef = useRef < string | null > ( null ) ;
72- const placeholderLayout = useSharedValue ( { h : 0 , w : 0 } ) ;
73- const insets = useSafeAreaInsets ( ) ;
74115
75116 if ( ! registrationIdRef . current ) {
76117 registrationIdRef . current = createClosingPortalLayoutRegistrationId ( ) ;
77118 }
78119
79120 const registrationId = registrationIdRef . current ;
80- const shouldTeleport = useShouldTeleportToClosingPortal ( portalHostName , registrationId ) ;
81-
82- const syncPortalLayout = useStableCallback ( ( ) => {
83- containerRef . current ?. measureInWindow ( ( x , y , width , height ) => {
84- const absolute = {
85- x,
86- y : y + ( Platform . OS === 'android' ? insets . top : 0 ) ,
87- } ;
88-
89- if ( ! width || ! height ) {
90- return ;
91- }
92121
93- placeholderLayout . value = { h : height , w : width } ;
94-
95- setClosingPortalLayout ( portalHostName , registrationId , {
96- ...absolute ,
97- h : height ,
98- w : width ,
99- } ) ;
100- } ) ;
101- } ) ;
122+ const { syncPortalLayout, containerRef, placeholderLayout } = useSyncingApi (
123+ portalHostName ,
124+ registrationId ,
125+ ) ;
126+ const shouldTeleport = useShouldTeleportToClosingPortal ( portalHostName , registrationId ) ;
102127
103128 useEffect ( ( ) => {
104129 return ( ) => {
105130 clearClosingPortalLayout ( portalHostName , registrationId ) ;
106131 } ;
107132 } , [ portalHostName , registrationId ] ) ;
108133
109- useEffect ( ( ) => {
110- // Measure once after mount and layout settle.
111- requestAnimationFrame ( ( ) => {
112- requestAnimationFrame ( ( ) => {
113- syncPortalLayout ( ) ;
114- } ) ;
115- } ) ;
116- } , [ insets . top , portalHostName , syncPortalLayout ] ) ;
117-
118134 const placeholderStyle = useAnimatedStyle ( ( ) => ( {
119135 height : placeholderLayout . value . h ,
120136 width : placeholderLayout . value . w > 0 ? placeholderLayout . value . w : '100%' ,
0 commit comments