Native module:
- Add `fullHeight` prop to BottomSheetView on Android and iOS
- iOS: fullHeight sets detent to .large, skips content observation
- Android: fullHeight uses isFitToContents=false with expandedOffset=statusBarHeight
Android bottom sheet behavior overhaul:
- Switch normal sheets to isFitToContents=false with expandedOffset, enabling
proper 3-state snapping (half-expanded ↔ expanded ↔ hidden). Previously
isFitToContents=true prevented settling at half-expanded during gestures,
making it impossible to swipe back down from expanded.
- preventExpansion sheets keep isFitToContents=true with maxHeight cap, plus
a state callback safety net that bounces EXPANDED→HALF_EXPANDED. This
catches an edge case where rapid content resizes during opening can invert
the expanded/half-expanded offsets, causing the sheet to dismiss.
- Add requestLayout() after maxHeight changes in updateLayout() so the
FrameLayout actually re-measures (fixes keyboard padding not resizing sheet)
- Set backgroundTint=transparent in the theme to remove Material3 surface
tint that was visible in gaps between sheet and screen edge
Keyboard handling:
- Remove native keyboard expansion logic (insets listener, isKeyboardVisible
tracking, manual STATE_EXPANDED on keyboard show)
- Replace with JS-side keyboard padding: ScrollableInner listens for RN
Keyboard events and adds bottom padding equal to keyboard height
- Generalize useOnKeyboardDidShow → useOnKeyboard(eventName, cb)
Callers:
- Replace minHeight: screenHeight hack with fullHeight: true in EditProfile,
ChangeHandle, AddAppPassword, ImageAltText, GifAltText, LanguageSelect,
FollowDialog, GifSelect, StarterPack, Select, NewChat, ShareViaChat,
DraftsList, WizardEditList, ListAddRemoveUsers, CreateOrEditList
- Remove unused useWindowDimensions imports
- ServerInput: simplify to just preventExpansion (was platform-specific)
- ImageAltTextDialog: remove old {height: 300} keyboard spacer hack
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Now that both platforms observe content height natively (iOS via KVO,
Android via OnLayoutChangeListener), the JS-callable updateLayout()
is unused. Remove:
- updateLayout() and prevLayoutDetentIdentifier from iOS SheetView
- AsyncFunction("updateLayout") from both platform module definitions
- updateLayout method from the JS BottomSheetNativeComponent
Android's updateLayout() is kept as a private implementation detail
since the keyboard insets listener and native layout observer both
call it internally.
https://claude.ai/code/session_01Rp7ef1h3fKh6fhStcjqLJ5
The KVO callback was calling updateLayout(), which has two problems for
content-driven updates:
1. The prevLayoutDetentIdentifier guard can block updates. When
updateDetents triggers animateChanges, the delegate callback
sheetPresentationControllerDidChangeSelectedDetentIdentifier fires
and updates selectedDetentIdentifier to the system-assigned custom
detent identifier. On the next KVO callback, prevLayoutDetentIdentifier
(.medium) no longer matches selectedDetentIdentifier, so the update
is silently dropped.
2. Re-reading frame.size.height from the view hierarchy instead of using
the observed bounds can return a stale value during rapid layout passes.
Fix: have the KVO callback call updateDetents directly with the observed
height, bypassing both the guard and the frame re-read. The guard in
updateLayout() is preserved for the manual/fallback code path.
https://claude.ai/code/session_01Rp7ef1h3fKh6fhStcjqLJ5
Replace the async JS bridge round-trip (onLayout → updateLayout()) with
native KVO observation on the content view's bounds. When React Native's
Yoga layout engine sets the frame, the KVO callback fires synchronously
on the same run loop iteration and calls updateLayout() directly. This
eliminates the timing issues that surfaced after enabling iOS 26 design.
https://claude.ai/code/session_01Rp7ef1h3fKh6fhStcjqLJ5