stream-chat-react-native-core 9.0.0-beta.17 → 9.0.0-beta.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/lib/commonjs/components/UIComponents/BottomSheetModal.js +120 -125
  2. package/lib/commonjs/components/UIComponents/BottomSheetModal.js.map +1 -1
  3. package/lib/commonjs/components/UIComponents/PortalWhileClosingView.js +4 -3
  4. package/lib/commonjs/components/UIComponents/PortalWhileClosingView.js.map +1 -1
  5. package/lib/commonjs/contexts/overlayContext/MessageOverlayHostLayer.js +70 -31
  6. package/lib/commonjs/contexts/overlayContext/MessageOverlayHostLayer.js.map +1 -1
  7. package/lib/commonjs/version.json +1 -1
  8. package/lib/module/components/UIComponents/BottomSheetModal.js +120 -125
  9. package/lib/module/components/UIComponents/BottomSheetModal.js.map +1 -1
  10. package/lib/module/components/UIComponents/PortalWhileClosingView.js +4 -3
  11. package/lib/module/components/UIComponents/PortalWhileClosingView.js.map +1 -1
  12. package/lib/module/contexts/overlayContext/MessageOverlayHostLayer.js +70 -31
  13. package/lib/module/contexts/overlayContext/MessageOverlayHostLayer.js.map +1 -1
  14. package/lib/module/version.json +1 -1
  15. package/lib/typescript/components/UIComponents/BottomSheetModal.d.ts.map +1 -1
  16. package/lib/typescript/components/UIComponents/PortalWhileClosingView.d.ts.map +1 -1
  17. package/lib/typescript/contexts/overlayContext/MessageOverlayHostLayer.d.ts.map +1 -1
  18. package/package.json +1 -1
  19. package/src/components/MessageInput/__tests__/__snapshots__/AttachButton.test.js.snap +15 -60
  20. package/src/components/MessageInput/__tests__/__snapshots__/SendButton.test.js.snap +10 -40
  21. package/src/components/UIComponents/BottomSheetModal.tsx +140 -173
  22. package/src/components/UIComponents/PortalWhileClosingView.tsx +5 -3
  23. package/src/contexts/overlayContext/MessageOverlayHostLayer.tsx +86 -31
  24. package/src/contexts/overlayContext/__tests__/MessageOverlayHostLayer.test.tsx +17 -8
  25. package/src/version.json +1 -1
@@ -789,16 +789,13 @@ exports[`SendButton should render a SendButton 1`] = `
789
789
  style={
790
790
  [
791
791
  {
792
- "height": 0,
792
+ "opacity": 0,
793
793
  },
794
794
  {
795
795
  "transform": [
796
796
  {
797
797
  "scale": 0,
798
798
  },
799
- {
800
- "translateY": 0,
801
- },
802
799
  ],
803
800
  },
804
801
  ]
@@ -821,18 +818,9 @@ exports[`SendButton should render a SendButton 1`] = `
821
818
  <View
822
819
  pointerEvents="box-none"
823
820
  style={
824
- [
825
- {
826
- "height": 0,
827
- },
828
- {
829
- "transform": [
830
- {
831
- "translateY": 0,
832
- },
833
- ],
834
- },
835
- ]
821
+ {
822
+ "height": 0,
823
+ }
836
824
  }
837
825
  testID="message-overlay-message"
838
826
  >
@@ -853,16 +841,13 @@ exports[`SendButton should render a SendButton 1`] = `
853
841
  style={
854
842
  [
855
843
  {
856
- "height": 0,
844
+ "opacity": 0,
857
845
  },
858
846
  {
859
847
  "transform": [
860
848
  {
861
849
  "scale": 0,
862
850
  },
863
- {
864
- "translateY": 0,
865
- },
866
851
  ],
867
852
  },
868
853
  ]
@@ -1676,16 +1661,13 @@ exports[`SendButton should render a disabled SendButton 1`] = `
1676
1661
  style={
1677
1662
  [
1678
1663
  {
1679
- "height": 0,
1664
+ "opacity": 0,
1680
1665
  },
1681
1666
  {
1682
1667
  "transform": [
1683
1668
  {
1684
1669
  "scale": 0,
1685
1670
  },
1686
- {
1687
- "translateY": 0,
1688
- },
1689
1671
  ],
1690
1672
  },
1691
1673
  ]
@@ -1708,18 +1690,9 @@ exports[`SendButton should render a disabled SendButton 1`] = `
1708
1690
  <View
1709
1691
  pointerEvents="box-none"
1710
1692
  style={
1711
- [
1712
- {
1713
- "height": 0,
1714
- },
1715
- {
1716
- "transform": [
1717
- {
1718
- "translateY": 0,
1719
- },
1720
- ],
1721
- },
1722
- ]
1693
+ {
1694
+ "height": 0,
1695
+ }
1723
1696
  }
1724
1697
  testID="message-overlay-message"
1725
1698
  >
@@ -1740,16 +1713,13 @@ exports[`SendButton should render a disabled SendButton 1`] = `
1740
1713
  style={
1741
1714
  [
1742
1715
  {
1743
- "height": 0,
1716
+ "opacity": 0,
1744
1717
  },
1745
1718
  {
1746
1719
  "transform": [
1747
1720
  {
1748
1721
  "scale": 0,
1749
1722
  },
1750
- {
1751
- "translateY": 0,
1752
- },
1753
1723
  ],
1754
1724
  },
1755
1725
  ]
@@ -1,4 +1,11 @@
1
- import React, { PropsWithChildren, useEffect, useLayoutEffect, useMemo, useState } from 'react';
1
+ import React, {
2
+ PropsWithChildren,
3
+ useEffect,
4
+ useLayoutEffect,
5
+ useMemo,
6
+ useRef,
7
+ useState,
8
+ } from 'react';
2
9
  import {
3
10
  EventSubscription,
4
11
  Keyboard,
@@ -13,7 +20,6 @@ import {
13
20
  import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';
14
21
  import type { KeyboardEventData } from 'react-native-keyboard-controller';
15
22
  import Animated, {
16
- cancelAnimation,
17
23
  Easing,
18
24
  FadeIn,
19
25
  runOnJS,
@@ -70,15 +76,21 @@ export const BottomSheetModal = (props: PropsWithChildren<BottomSheetModalProps>
70
76
 
71
77
  const baseHeight = Math.min(height, maxHeight);
72
78
  const snapPoints = useMemo(() => [baseHeight, maxHeight], [baseHeight, maxHeight]);
79
+ const snapPointsTranslateY = useMemo(
80
+ () => snapPoints.map((point) => maxHeight - point),
81
+ [maxHeight, snapPoints],
82
+ );
73
83
 
74
- const translateY = useSharedValue(maxHeight);
84
+ const sheetTranslateY = useSharedValue(maxHeight);
75
85
  const keyboardOffset = useSharedValue(0);
76
86
  const currentSnapIndex = useSharedValue(0);
77
87
 
78
88
  const isOpen = useSharedValue(false);
79
89
  const isOpening = useSharedValue(false);
80
90
 
81
- const panStartY = useSharedValue(0);
91
+ const panStartTranslateY = useSharedValue(0);
92
+ const hasCommittedVisibilityRef = useRef(false);
93
+ const wasVisibleRef = useRef(false);
82
94
 
83
95
  const [renderContent, setRenderContent] = useState(!lazy);
84
96
 
@@ -88,35 +100,47 @@ export const BottomSheetModal = (props: PropsWithChildren<BottomSheetModalProps>
88
100
  }
89
101
  });
90
102
 
91
- const hideContent = useStableCallback(() => {
92
- if (lazy) {
93
- setRenderContent(false);
103
+ const finishClose = useStableCallback((closeAnimationFinishedCallback?: () => void) => {
104
+ onClose();
105
+ if (closeAnimationFinishedCallback) {
106
+ Platform.OS === 'ios'
107
+ ? closeAnimationFinishedCallback()
108
+ : setTimeout(() => closeAnimationFinishedCallback(), 100);
94
109
  }
95
110
  });
96
111
 
112
+ const closeFromGesture = useStableCallback(() => {
113
+ requestAnimationFrame(() => {
114
+ isOpen.value = false;
115
+ isOpening.value = false;
116
+
117
+ sheetTranslateY.value = withTiming(
118
+ maxHeight,
119
+ { duration: 250, easing: Easing.out(Easing.cubic) },
120
+ (finished) => {
121
+ if (finished) {
122
+ runOnJS(onClose)();
123
+ }
124
+ },
125
+ );
126
+ });
127
+ });
128
+
97
129
  const close = useStableCallback((closeAnimationFinishedCallback?: () => void) => {
98
- // hide content immediately
99
- hideContent();
130
+ if (!visible || !isOpen.value) {
131
+ return;
132
+ }
100
133
 
101
134
  isOpen.value = false;
102
135
  isOpening.value = false;
103
136
 
104
- cancelAnimation(translateY);
105
-
106
- const closeCallback = () => {
107
- onClose();
108
- if (closeAnimationFinishedCallback) {
109
- Platform.OS === 'ios'
110
- ? closeAnimationFinishedCallback()
111
- : setTimeout(() => closeAnimationFinishedCallback(), 100);
112
- }
113
- };
114
-
115
- translateY.value = withTiming(
137
+ sheetTranslateY.value = withTiming(
116
138
  maxHeight,
117
139
  { duration: 250, easing: Easing.out(Easing.cubic) },
118
140
  (finished) => {
119
- if (finished) runOnJS(closeCallback)();
141
+ if (finished) {
142
+ runOnJS(finishClose)(closeAnimationFinishedCallback);
143
+ }
120
144
  },
121
145
  );
122
146
  });
@@ -124,57 +148,38 @@ export const BottomSheetModal = (props: PropsWithChildren<BottomSheetModalProps>
124
148
  // modal opening layout effect - we make sure to only show the content
125
149
  // after the animation has finished if `lazy` has been set to true
126
150
  useLayoutEffect(() => {
127
- if (!visible) return;
151
+ const wasVisible = hasCommittedVisibilityRef.current ? wasVisibleRef.current : false;
152
+ hasCommittedVisibilityRef.current = true;
153
+ wasVisibleRef.current = visible;
154
+
155
+ if (!visible || wasVisible) {
156
+ return;
157
+ }
128
158
 
129
159
  isOpen.value = true;
130
160
  isOpening.value = true;
131
161
  currentSnapIndex.value = 0;
162
+ sheetTranslateY.value = maxHeight;
132
163
 
133
- cancelAnimation(translateY);
134
-
135
- // start from closed
136
- translateY.value = maxHeight;
137
-
138
- // Snapshot current keyboard offset as the open target.
139
- // If keyboard changes during opening, we’ll adjust after.
140
- const initialTarget = keyboardOffset.value + (maxHeight - snapPoints[currentSnapIndex.value]);
141
-
142
- translateY.value = withTiming(
143
- initialTarget,
164
+ sheetTranslateY.value = withTiming(
165
+ snapPointsTranslateY[0],
144
166
  { duration: 250, easing: Easing.out(Easing.cubic) },
145
167
  (finished) => {
146
168
  if (!finished) return;
147
169
 
148
- // opening the modal has now truly finished
149
170
  isOpening.value = false;
150
-
151
- // reveal the content if we want to load it lazily
152
171
  runOnJS(showContent)();
153
-
154
- // if keyboard offset changed while we were opening, we do a
155
- // follow-up adjustment (we do not gate the content however)
156
- const latestTarget =
157
- keyboardOffset.value + (maxHeight - snapPoints[currentSnapIndex.value]);
158
- if (latestTarget !== initialTarget && isOpen.value) {
159
- cancelAnimation(translateY);
160
- translateY.value = withTiming(latestTarget, {
161
- duration: 250,
162
- easing: Easing.inOut(Easing.ease),
163
- });
164
- }
165
172
  },
166
173
  );
167
174
  }, [
168
175
  visible,
169
- hideContent,
170
176
  isOpen,
171
177
  isOpening,
172
- keyboardOffset,
173
178
  maxHeight,
174
- snapPoints,
175
179
  showContent,
176
- translateY,
180
+ sheetTranslateY,
177
181
  currentSnapIndex,
182
+ snapPointsTranslateY,
178
183
  ]);
179
184
 
180
185
  // if `visible` gets hard changed, we force a cleanup
@@ -185,39 +190,46 @@ export const BottomSheetModal = (props: PropsWithChildren<BottomSheetModalProps>
185
190
  isOpening.value = false;
186
191
  keyboardOffset.value = 0;
187
192
  currentSnapIndex.value = 0;
193
+ sheetTranslateY.value = maxHeight;
194
+ setRenderContent(!lazy);
195
+ }, [
196
+ visible,
197
+ lazy,
198
+ isOpen,
199
+ isOpening,
200
+ keyboardOffset,
201
+ maxHeight,
202
+ sheetTranslateY,
203
+ currentSnapIndex,
204
+ ]);
188
205
 
189
- cancelAnimation(translateY);
190
- translateY.value = maxHeight;
191
- }, [visible, maxHeight, isOpen, isOpening, keyboardOffset, translateY, currentSnapIndex]);
192
-
193
- const keyboardDidShowRN = useStableCallback((event: KeyboardEvent) => {
194
- const offset = -event.endCoordinates.height;
195
- keyboardOffset.value = offset;
196
-
197
- // We just record the offset, but we avoid cancelling the animation
198
- // if it's in the process of opening. The same logic applies to all
199
- // other keyboard related callbacks in this specific conditional.
200
- if (!isOpen.value || isOpening.value) return;
206
+ // Keep the sheet aligned with the active snap if dimensions change while visible.
207
+ useEffect(() => {
208
+ if (!visible || !isOpen.value || isOpening.value) {
209
+ return;
210
+ }
201
211
 
202
- cancelAnimation(translateY);
203
- translateY.value = withTiming(offset + (maxHeight - snapPoints[currentSnapIndex.value]), {
212
+ sheetTranslateY.value = withTiming(snapPointsTranslateY[currentSnapIndex.value], {
204
213
  duration: 250,
205
214
  easing: Easing.inOut(Easing.ease),
206
215
  });
207
- });
208
-
209
- const keyboardDidHide = useStableCallback(() => {
210
- keyboardOffset.value = 0;
211
-
212
- if (!isOpen.value || isOpening.value) return;
216
+ }, [visible, isOpen, isOpening, sheetTranslateY, currentSnapIndex, snapPointsTranslateY]);
213
217
 
214
- cancelAnimation(translateY);
215
- translateY.value = withTiming(maxHeight - snapPoints[currentSnapIndex.value], {
218
+ const animateKeyboardOffset = useStableCallback((offset: number) => {
219
+ keyboardOffset.value = withTiming(offset, {
216
220
  duration: 250,
217
221
  easing: Easing.inOut(Easing.ease),
218
222
  });
219
223
  });
220
224
 
225
+ const keyboardDidShowRN = useStableCallback((event: KeyboardEvent) => {
226
+ animateKeyboardOffset(event.endCoordinates.height);
227
+ });
228
+
229
+ const keyboardDidHide = useStableCallback(() => {
230
+ animateKeyboardOffset(0);
231
+ });
232
+
221
233
  useEffect(() => {
222
234
  if (!visible) return;
223
235
 
@@ -225,114 +237,67 @@ export const BottomSheetModal = (props: PropsWithChildren<BottomSheetModalProps>
225
237
 
226
238
  if (KeyboardControllerPackage?.KeyboardEvents) {
227
239
  const keyboardDidShowKC = (event: KeyboardEventData) => {
228
- const offset = -event.height;
229
- keyboardOffset.value = offset;
230
-
231
- if (!isOpen.value || isOpening.value) return;
232
-
233
- cancelAnimation(translateY);
234
- translateY.value = withTiming(offset + (maxHeight - snapPoints[currentSnapIndex.value]), {
235
- duration: 250,
236
- easing: Easing.inOut(Easing.ease),
237
- });
240
+ animateKeyboardOffset(event.height);
238
241
  };
239
242
 
240
243
  listeners.push(
241
244
  KeyboardControllerPackage.KeyboardEvents.addListener('keyboardDidShow', keyboardDidShowKC),
242
245
  KeyboardControllerPackage.KeyboardEvents.addListener('keyboardDidHide', keyboardDidHide),
243
246
  );
244
- } else {
245
- if (Platform.OS === 'ios') {
246
- listeners.push(Keyboard.addListener('keyboardWillShow', keyboardDidShowRN));
247
- listeners.push(Keyboard.addListener('keyboardWillHide', keyboardDidHide));
248
- }
247
+ } else if (Platform.OS === 'ios') {
248
+ listeners.push(Keyboard.addListener('keyboardWillShow', keyboardDidShowRN));
249
+ listeners.push(Keyboard.addListener('keyboardWillHide', keyboardDidHide));
249
250
  }
250
251
 
251
252
  return () => listeners.forEach((l) => l.remove());
252
- }, [
253
- visible,
254
- keyboardDidHide,
255
- keyboardDidShowRN,
256
- keyboardOffset,
257
- isOpen,
258
- isOpening,
259
- translateY,
260
- maxHeight,
261
- snapPoints,
262
- currentSnapIndex,
263
- ]);
253
+ }, [visible, animateKeyboardOffset, keyboardDidHide, keyboardDidShowRN]);
264
254
 
265
- const sheetAnimatedStyle = useAnimatedStyle(() => ({
266
- transform: [{ translateY: translateY.value }],
267
- paddingBottom: translateY.value,
255
+ const sheetViewportAnimatedStyle = useAnimatedStyle(() => ({
256
+ transform: [{ translateY: sheetTranslateY.value - keyboardOffset.value }],
268
257
  }));
269
258
 
270
- const backdropThreshold = baseHeight;
271
-
272
259
  const overlayAnimatedStyle = useAnimatedStyle(() => {
273
- const visibleHeight = Math.max(0, maxHeight - (translateY.value - keyboardOffset.value));
274
- const threshold = Math.max(1, Math.min(backdropThreshold, maxHeight));
260
+ const visibleHeight = Math.max(0, maxHeight - sheetTranslateY.value);
261
+ const threshold = Math.max(1, Math.min(baseHeight, maxHeight));
275
262
  const progress = Math.min(1, visibleHeight / threshold);
276
263
  return { opacity: progress };
277
264
  });
278
265
 
279
- const snapPointsTranslateY = useMemo(
280
- () => snapPoints.map((point) => maxHeight - point),
281
- [maxHeight, snapPoints],
282
- );
283
-
284
266
  const panGesture = useMemo(
285
267
  () =>
286
268
  Gesture.Pan()
287
- // disable pan until content is rendered (prevents canceling the opening timing).
288
269
  .enabled(renderContent)
289
270
  .onBegin(() => {
290
- cancelAnimation(translateY);
291
- panStartY.value = translateY.value;
271
+ panStartTranslateY.value = sheetTranslateY.value;
292
272
  })
293
273
  .onUpdate((event) => {
294
- const minY = keyboardOffset.value + (maxHeight - snapPoints[snapPoints.length - 1]);
295
- const next = panStartY.value + event.translationY;
296
- translateY.value = Math.max(next, minY);
274
+ const nextTranslateY = panStartTranslateY.value + event.translationY;
275
+ sheetTranslateY.value = Math.min(Math.max(nextTranslateY, 0), maxHeight);
297
276
  })
298
277
  .onEnd((event) => {
299
- const openY = keyboardOffset.value + (maxHeight - snapPoints[currentSnapIndex.value]);
300
- const draggedDown = Math.max(translateY.value - openY, 0);
278
+ const openTranslateY = snapPointsTranslateY[currentSnapIndex.value];
279
+ const draggedDown = Math.max(sheetTranslateY.value - openTranslateY, 0);
301
280
  const topSnapIndex = snapPoints.length - 1;
302
281
  const isAtTopSnap = currentSnapIndex.value === topSnapIndex;
303
- const snap0Y = keyboardOffset.value + (maxHeight - snapPoints[0]);
304
- const projectedY = translateY.value + event.velocityY * 0.2;
282
+ const snap0TranslateY = snapPointsTranslateY[0];
283
+ const projectedTranslateY = sheetTranslateY.value + event.velocityY * 0.2;
305
284
 
306
- // From lower snaps, keep the previous close behavior.
307
285
  const shouldCloseFromLowerSnap = event.velocityY > 500 || draggedDown > maxHeight / 2;
308
- // From top snap, close only for clearly hard downward intent.
309
286
  const shouldCloseFromTopSnap =
310
- event.velocityY > 2200 || projectedY > snap0Y + (maxHeight - snap0Y) * 0.96;
287
+ event.velocityY > 2200 ||
288
+ projectedTranslateY > snap0TranslateY + (maxHeight - snap0TranslateY) * 0.96;
311
289
 
312
290
  const shouldClose = isAtTopSnap ? shouldCloseFromTopSnap : shouldCloseFromLowerSnap;
313
291
 
314
- cancelAnimation(translateY);
315
-
316
292
  if (shouldClose) {
317
- isOpen.value = false;
318
- isOpening.value = false;
319
-
320
- translateY.value = withTiming(
321
- maxHeight,
322
- { duration: 250, easing: Easing.out(Easing.cubic) },
323
- (finished) => {
324
- if (finished) runOnJS(onClose)();
325
- },
326
- );
293
+ runOnJS(closeFromGesture)();
327
294
  } else {
328
295
  isOpen.value = true;
329
- // snap to the nearest point
330
296
  let nearestIndex = 0;
331
297
  let minDistance = Number.POSITIVE_INFINITY;
332
- const baseOffset = keyboardOffset.value;
333
298
  for (let i = 0; i < snapPointsTranslateY.length; i += 1) {
334
- const candidate = baseOffset + snapPointsTranslateY[i];
335
- const distance = Math.abs(translateY.value - candidate);
299
+ const candidate = snapPointsTranslateY[i];
300
+ const distance = Math.abs(sheetTranslateY.value - candidate);
336
301
  if (distance < minDistance) {
337
302
  minDistance = distance;
338
303
  nearestIndex = i;
@@ -347,8 +312,9 @@ export const BottomSheetModal = (props: PropsWithChildren<BottomSheetModalProps>
347
312
  if (isAtTopSnap && event.velocityY > 120) {
348
313
  nearestIndex = 0;
349
314
  }
315
+
350
316
  currentSnapIndex.value = nearestIndex;
351
- translateY.value = withTiming(baseOffset + snapPointsTranslateY[nearestIndex], {
317
+ sheetTranslateY.value = withTiming(snapPointsTranslateY[nearestIndex], {
352
318
  duration: 250,
353
319
  easing: Easing.inOut(Easing.ease),
354
320
  });
@@ -357,15 +323,13 @@ export const BottomSheetModal = (props: PropsWithChildren<BottomSheetModalProps>
357
323
  [
358
324
  currentSnapIndex,
359
325
  isOpen,
360
- isOpening,
361
- keyboardOffset,
362
326
  maxHeight,
363
- onClose,
364
- panStartY,
327
+ closeFromGesture,
328
+ panStartTranslateY,
365
329
  renderContent,
366
330
  snapPoints,
367
331
  snapPointsTranslateY,
368
- translateY,
332
+ sheetTranslateY,
369
333
  ],
370
334
  );
371
335
 
@@ -382,30 +346,33 @@ export const BottomSheetModal = (props: PropsWithChildren<BottomSheetModalProps>
382
346
  return (
383
347
  <Modal onRequestClose={onClose} transparent visible={visible}>
384
348
  <GestureHandlerRootView style={styles.sheetContentContainer}>
385
- <GestureDetector gesture={panGesture}>
386
- <View style={[styles.overlay, overlayTheme]}>
387
- <Animated.View pointerEvents='none' style={[styles.backdrop, overlayAnimatedStyle]} />
388
- <Pressable onPress={onBackdropPress} style={StyleSheet.absoluteFillObject} />
389
-
390
- <Animated.View
391
- style={[styles.container, { height: maxHeight }, sheetAnimatedStyle, container]}
392
- >
393
- <View style={[styles.handle, handle]} />
394
- <View style={[styles.contentContainer, contentContainer]}>
395
- {renderContent ? (
396
- <BottomSheetProvider value={bottomSheetModalContextValue}>
397
- <Animated.View
398
- entering={FadeIn.duration(250)}
399
- style={styles.sheetContentContainer}
400
- >
401
- {children}
402
- </Animated.View>
403
- </BottomSheetProvider>
404
- ) : null}
405
- </View>
406
- </Animated.View>
407
- </View>
408
- </GestureDetector>
349
+ <View style={[styles.overlay, overlayTheme]}>
350
+ <Animated.View pointerEvents='none' style={[styles.backdrop, overlayAnimatedStyle]} />
351
+ <Pressable onPress={onBackdropPress} style={StyleSheet.absoluteFillObject} />
352
+
353
+ <Animated.View
354
+ pointerEvents='box-none'
355
+ style={[{ height: maxHeight }, sheetViewportAnimatedStyle]}
356
+ >
357
+ <GestureDetector gesture={panGesture}>
358
+ <Animated.View style={[styles.container, { height: maxHeight }, container]}>
359
+ <View style={[styles.handle, handle]} />
360
+ <View style={[styles.contentContainer, contentContainer]}>
361
+ {renderContent ? (
362
+ <BottomSheetProvider value={bottomSheetModalContextValue}>
363
+ <Animated.View
364
+ entering={FadeIn.duration(250)}
365
+ style={styles.sheetContentContainer}
366
+ >
367
+ {children}
368
+ </Animated.View>
369
+ </BottomSheetProvider>
370
+ ) : null}
371
+ </View>
372
+ </Animated.View>
373
+ </GestureDetector>
374
+ </Animated.View>
375
+ </View>
409
376
  </GestureHandlerRootView>
410
377
  </Modal>
411
378
  );
@@ -12,6 +12,7 @@ import {
12
12
  setClosingPortalLayout,
13
13
  useShouldTeleportToClosingPortal,
14
14
  useHasActiveId,
15
+ useIsOverlayClosing,
15
16
  } from '../../state-store';
16
17
 
17
18
  type PortalWhileClosingViewProps = {
@@ -116,9 +117,10 @@ const useSyncingApi = (portalHostName: string, registrationId: string) => {
116
117
  const placeholderLayout = useSharedValue({ h: 0, w: 0 });
117
118
  const insets = useSafeAreaInsets();
118
119
  const hasActiveId = useHasActiveId();
120
+ const isClosing = useIsOverlayClosing();
119
121
 
120
122
  const syncPortalLayout = useStableCallback(() => {
121
- if (!hasActiveId) {
123
+ if (!hasActiveId && !isClosing) {
122
124
  return;
123
125
  }
124
126
 
@@ -143,10 +145,10 @@ const useSyncingApi = (portalHostName: string, registrationId: string) => {
143
145
  });
144
146
 
145
147
  useEffect(() => {
146
- if (hasActiveId) {
148
+ if (hasActiveId || isClosing) {
147
149
  syncPortalLayout();
148
150
  }
149
- }, [insets.bottom, hasActiveId, syncPortalLayout]);
151
+ }, [insets.bottom, isClosing, hasActiveId, syncPortalLayout]);
150
152
 
151
153
  return useMemo(
152
154
  () => ({ syncPortalLayout, containerRef, placeholderLayout }),