diff --git a/packages/app/ui/components/BottomSheetWrapper.native.tsx b/packages/app/ui/components/BottomSheetWrapper.native.tsx index ed74544862..59bca4aee0 100644 --- a/packages/app/ui/components/BottomSheetWrapper.native.tsx +++ b/packages/app/ui/components/BottomSheetWrapper.native.tsx @@ -363,6 +363,17 @@ export const BottomSheetWrapper = forwardRef< [dismissOnSnapToBottom, onOpenChange] ); + // Gorhom skips onChange(-1) when the sheet is dismissed before its open + // animation finishes (e.g. a backdrop tap mid-animation), so + // handleSheetChanges alone can leave `open` stuck at true. onDismiss fires + // on every modal dismissal path; if the parent still thinks the sheet is + // open at that point, sync it so the next open isn't a no-op. + const handleModalDismiss = useCallback(() => { + if (open) { + onOpenChange(false); + } + }, [open, onOpenChange]); + const renderBackdrop = useCallback( (props: any) => showOverlay ? ( @@ -467,6 +478,7 @@ export const BottomSheetWrapper = forwardRef< ref={bottomSheetModalRef} accessibilityViewIsModal={true} stackBehavior={stackBehavior} + onDismiss={handleModalDismiss} {...commonProps} {...commonOverrides} > diff --git a/patches/@gorhom__bottom-sheet@5.2.14.patch b/patches/@gorhom__bottom-sheet@5.2.14.patch index 050bad8dbd..a7cffd7559 100644 --- a/patches/@gorhom__bottom-sheet@5.2.14.patch +++ b/patches/@gorhom__bottom-sheet@5.2.14.patch @@ -53,3 +53,30 @@ index 4a69a6892ff553836b72216b598451c56f6284cc..7cdacc259c532844f5ec378e111f2f16 return { paddingBottom: animate({ point: paddingBottom, +diff --git a/src/components/bottomSheetModal/BottomSheetModal.tsx b/src/components/bottomSheetModal/BottomSheetModal.tsx +index 2954b3f4282e28c1973ab88f479eb2d7889913c5..ec2ea40cdb40688fffb297bad212097529bea7e5 100644 +--- a/src/components/bottomSheetModal/BottomSheetModal.tsx ++++ b/src/components/bottomSheetModal/BottomSheetModal.tsx +@@ -275,11 +275,19 @@ function BottomSheetModalComponent( + /** + * if the modal position is already in a closed position, + * then we unmount the node and early exit. ++ * ++ * INITIAL is included so that dismissing a modal that was never ++ * presented (or that already fully dismissed itself, e.g. via a ++ * backdrop press) stays idempotent. Without it, the status gets ++ * stuck at DISMISSING and every later present() silently no-ops. ++ * https://github.com/gorhom/react-native-bottom-sheet/issues/2669 + */ + if ( +- [MODAL_STATUS.CLOSED, MODAL_STATUS.MINIMIZED].includes( +- statusRef.current +- ) || ++ [ ++ MODAL_STATUS.INITIAL, ++ MODAL_STATUS.CLOSED, ++ MODAL_STATUS.MINIMIZED, ++ ].includes(statusRef.current) || + (statusRef.current === MODAL_STATUS.DISMISSING && + currentIndexRef.current === -1) + ) { diff --git a/patches/README.md b/patches/README.md index bd27b29bf1..22b8a7d803 100644 --- a/patches/README.md +++ b/patches/README.md @@ -11,6 +11,13 @@ When adding a patch, document: ## @gorhom/bottom-sheet@5.2.14 +Local patch: +`patches/@gorhom__bottom-sheet@5.2.14.patch` + +This patch carries two independent fixes. + +### 1. First-open layout of flex:1 sheet content + Why: On the first open of a bottom sheet whose content is a `flex:1` ScrollView/View with content larger than the eventual viewport (a long scrollable list with a @@ -33,9 +40,6 @@ fix (enabling Yoga's `WebFlexBasis` flag) requires building React Native from source, which we currently don't do; the writeup is in the closed draft PR linked below. -Local patch: -`patches/@gorhom__bottom-sheet@5.2.14.patch` - Background and reproduction details: PR #5790 (closed, kept for reference). Validation: @@ -44,11 +48,40 @@ than the viewport plus a footer (e.g. CreateChatSheet). The footer should be visible at the bottom of the sheet on first open. Removal: -Drop this patch once we either move to building React Native from source +Drop this hunk once we either move to building React Native from source (so we can flip the Yoga `WebFlexBasis` flag and fix the bug at the layout-engine level), or once `@gorhom/bottom-sheet` ships an equivalent workaround upstream. +### 2. Modal dismiss() bricks the modal when already dismissed + +Why: +`BottomSheetModal.dismiss()` called while the modal's status is `INITIAL` +(never presented, or already fully dismissed and reset) falls through the +already-closed early-exit, permanently sets the internal status to +`DISMISSING`, and every later `present()` silently no-ops. Our +`BottomSheetWrapper` calls `dismiss()` whenever `open` flips false — which +is always the case right after a user-initiated close (backdrop tap / swipe +down) has already dismissed the modal internally — so modal sheets (e.g. the +personal invite sheet) could only be opened once per mount. + +What it does: +Adds `MODAL_STATUS.INITIAL` to the already-closed early-exit in +`handleDismiss` (`src/components/bottomSheetModal/BottomSheetModal.tsx`), +making `dismiss()` idempotent. + +Upstream: +- issue: `gorhom/react-native-bottom-sheet#2669` +- fix submitted: `gorhom/react-native-bottom-sheet#2711` + +Validation: +- Home header → AddPerson opens the invite sheet; close it via the backdrop; + tap AddPerson again — the sheet must open again (repeat a few times). + +Removal: +Drop this hunk once `gorhom/react-native-bottom-sheet#2711` (or an +equivalent fix) ships in a release we use. + ## react-native@0.85.3 Local patch: diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c5e1e2ad4c..741659b259 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -31,7 +31,7 @@ patchedDependencies: hash: tfmnpfof2myqgpz3kv2tl4djpm path: patches/@10play__tentap-editor@0.5.21.patch '@gorhom/bottom-sheet': - hash: 3r63mh5ero7blgeewu2xcfbxqy + hash: xno6vrsagsnp3lsnzxd4islqsm path: patches/@gorhom__bottom-sheet@5.2.14.patch '@simform_solutions/react-native-audio-waveform': hash: a4fa2r3d43epbulffgzabkfham @@ -191,7 +191,7 @@ importers: version: 18.8.2(react-native@0.85.3(@babel/core@7.29.0)(@react-native-community/cli@13.6.9(encoding@0.1.13))(@react-native/jest-preset@0.85.3(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.3(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) '@gorhom/bottom-sheet': specifier: ^5.2.14 - version: 5.2.14(patch_hash=3r63mh5ero7blgeewu2xcfbxqy)(k57kres2cv7iqg4x6tibma75u4) + version: 5.2.14(patch_hash=xno6vrsagsnp3lsnzxd4islqsm)(k57kres2cv7iqg4x6tibma75u4) '@op-engineering/op-sqlite': specifier: 15.2.5 version: 15.2.5(react-native@0.85.3(@babel/core@7.29.0)(@react-native-community/cli@13.6.9(encoding@0.1.13))(@react-native/jest-preset@0.85.3(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.3(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) @@ -18221,7 +18221,7 @@ snapshots: - supports-color - utf-8-validate - '@gorhom/bottom-sheet@5.2.14(patch_hash=3r63mh5ero7blgeewu2xcfbxqy)(k57kres2cv7iqg4x6tibma75u4)': + '@gorhom/bottom-sheet@5.2.14(patch_hash=xno6vrsagsnp3lsnzxd4islqsm)(k57kres2cv7iqg4x6tibma75u4)': dependencies: '@gorhom/portal': 1.0.14(react-native@0.85.3(@babel/core@7.29.0)(@react-native-community/cli@13.6.9(encoding@0.1.13))(@react-native/jest-preset@0.85.3(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.3(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) invariant: 2.2.4