Android sheets edge to edge (#8342)

This commit is contained in:
Samuel Newman
2026-02-10 21:01:58 +00:00
committed by GitHub
parent e2058c8ecc
commit 338016ed5c
19 changed files with 161 additions and 228 deletions
+1 -1
View File
@@ -44,6 +44,6 @@ android {
dependencies {
implementation project(':expo-modules-core')
implementation 'com.google.android.material:material:1.12.0'
implementation 'com.google.android.material:material:1.13.0'
implementation "com.facebook.react:react-native:+"
}
@@ -5,8 +5,12 @@ import android.util.DisplayMetrics
import android.view.View
import android.view.ViewGroup
import android.view.ViewStructure
import android.view.Window
import android.view.accessibility.AccessibilityEvent
import android.widget.FrameLayout
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.core.view.allViews
import com.facebook.react.bridge.LifecycleEventListener
import com.facebook.react.bridge.ReactContext
@@ -15,6 +19,7 @@ import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.uimanager.events.EventDispatcher
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.internal.EdgeToEdgeUtils
import expo.modules.kotlin.AppContext
import expo.modules.kotlin.viewevent.EventDispatcher
import expo.modules.kotlin.views.ExpoView
@@ -29,22 +34,26 @@ class BottomSheetView(
private lateinit var dialogRootViewGroup: DialogRootViewGroup
private var eventDispatcher: EventDispatcher? = null
private var isKeyboardVisible: Boolean = false
private val rawScreenHeight =
private val screenHeight =
context.resources.displayMetrics.heightPixels
.toFloat()
private val safeScreenHeight = (rawScreenHeight - getNavigationBarHeight()).toFloat()
private fun getNavigationBarHeight(): Int {
val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
return if (resourceId > 0) resources.getDimensionPixelSize(resourceId) else 0
}
private fun getStatusBarHeight(): Int {
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
return if (resourceId > 0) resources.getDimensionPixelSize(resourceId) else 0
}
private val onAttemptDismiss by EventDispatcher()
private val onSnapPointChange by EventDispatcher()
private val onStateChange by EventDispatcher()
// Props
var disableDrag = false
set(value) {
field = value
@@ -56,48 +65,31 @@ class BottomSheetView(
field = value
this.dialog?.setCancelable(!value)
}
var preventExpansion = false
var minHeight = 0f
set(value) {
field =
if (value < 0) {
0f
} else {
dpToPx(value)
}
field = if (value < 0) 0f else dpToPx(value)
}
var maxHeight = this.safeScreenHeight
var maxHeight = this.screenHeight
set(value) {
val px = dpToPx(value)
field =
if (px > this.safeScreenHeight) {
this.safeScreenHeight
} else {
px
}
field = if (px > this.screenHeight) this.screenHeight else px
}
private var isOpen: Boolean = false
set(value) {
field = value
onStateChange(
mapOf(
"state" to if (value) "open" else "closed",
),
)
onStateChange(mapOf("state" to if (value) "open" else "closed"))
}
private var isOpening: Boolean = false
set(value) {
field = value
if (value) {
onStateChange(
mapOf(
"state" to "opening",
),
)
onStateChange(mapOf("state" to "opening"))
}
}
@@ -105,33 +97,21 @@ class BottomSheetView(
set(value) {
field = value
if (value) {
onStateChange(
mapOf(
"state" to "closing",
),
)
onStateChange(mapOf("state" to "closing"))
}
}
private var selectedSnapPoint = 0
set(value) {
if (field == value) return
field = value
onSnapPointChange(
mapOf(
"snapPoint" to value,
),
)
onSnapPointChange(mapOf("snapPoint" to value))
}
// Lifecycle
init {
(appContext.reactContext as? ReactContext)?.let {
it.addLifecycleEventListener(this)
this.eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(it, this.id)
this.dialogRootViewGroup = DialogRootViewGroup(context)
this.dialogRootViewGroup.eventDispatcher = this.eventDispatcher
}
@@ -161,27 +141,55 @@ class BottomSheetView(
private fun getHalfExpandedRatio(contentHeight: Float): Float =
when {
// Full height sheets
contentHeight >= safeScreenHeight -> 0.99f
// Medium height sheets (>50% but <100%)
contentHeight >= safeScreenHeight / 2 ->
this.clampRatio(this.getTargetHeight() / safeScreenHeight)
// Small height sheets (<50%)
else ->
this.clampRatio(this.getTargetHeight() / rawScreenHeight)
contentHeight >= screenHeight -> 0.99f
else -> this.clampRatio(this.getTargetHeight() / screenHeight)
}
private fun present() {
if (this.isOpen || this.isOpening || this.isClosing) return
val contentHeight = this.getContentHeight()
val dialog = BottomSheetDialog(context)
var activityWindow: Window? = null
var currentContext = context
while (currentContext != null) {
if (currentContext is android.app.Activity) {
activityWindow = currentContext.window
break
}
currentContext = (currentContext as? android.content.ContextWrapper)?.baseContext
}
val originalStatusBarAppearance =
activityWindow?.let { window ->
WindowInsetsControllerCompat(window, window.decorView).isAppearanceLightStatusBars
}
val originalNavBarAppearance =
activityWindow?.let { window ->
WindowInsetsControllerCompat(window, window.decorView).isAppearanceLightNavigationBars
}
val dialog = BottomSheetDialog(context, R.style.EdgeToEdgeBottomSheetDialogTheme)
dialog.setContentView(dialogRootViewGroup)
dialog.setCancelable(!preventDismiss)
dialog.setDismissWithAnimation(true)
dialog.setOnDismissListener {
this.isClosing = true
this.destroy()
}
dialog.setOnShowListener {
dialog.window?.let { window ->
val insetsController = WindowInsetsControllerCompat(window, window.decorView)
if (originalNavBarAppearance != null) {
insetsController.isAppearanceLightNavigationBars = originalNavBarAppearance
}
if (originalStatusBarAppearance != null) {
EdgeToEdgeUtils.setLightStatusBar(window, originalStatusBarAppearance)
}
}
}
val bottomSheet = dialog.findViewById<FrameLayout>(com.google.android.material.R.id.design_bottom_sheet)
bottomSheet?.let {
it.setBackgroundColor(0)
@@ -194,7 +202,17 @@ class BottomSheetView(
behavior.isDraggable = true
behavior.isHideable = true
if (contentHeight >= this.safeScreenHeight || this.minHeight >= this.safeScreenHeight) {
if (preventExpansion) {
behavior.maxHeight = (behavior.halfExpandedRatio * screenHeight).toInt()
} else {
behavior.maxHeight = (screenHeight - getStatusBarHeight()).toInt()
}
val targetHeight = this.getTargetHeight()
val availableHeight = screenHeight - getStatusBarHeight() - getNavigationBarHeight()
val shouldBeExpanded = targetHeight >= availableHeight
if (shouldBeExpanded) {
behavior.state = BottomSheetBehavior.STATE_EXPANDED
this.selectedSnapPoint = 2
} else {
@@ -209,18 +227,10 @@ class BottomSheetView(
newState: Int,
) {
when (newState) {
BottomSheetBehavior.STATE_EXPANDED -> {
selectedSnapPoint = 2
}
BottomSheetBehavior.STATE_COLLAPSED -> {
selectedSnapPoint = 1
}
BottomSheetBehavior.STATE_HALF_EXPANDED -> {
selectedSnapPoint = 1
}
BottomSheetBehavior.STATE_HIDDEN -> {
selectedSnapPoint = 0
}
BottomSheetBehavior.STATE_EXPANDED -> selectedSnapPoint = 2
BottomSheetBehavior.STATE_COLLAPSED -> selectedSnapPoint = 1
BottomSheetBehavior.STATE_HALF_EXPANDED -> selectedSnapPoint = 1
BottomSheetBehavior.STATE_HIDDEN -> selectedSnapPoint = 0
}
}
@@ -231,9 +241,26 @@ class BottomSheetView(
},
)
}
this.isOpening = true
dialog.show()
this.dialog = dialog
ViewCompat.setOnApplyWindowInsetsListener(dialogRootViewGroup) { view, insets ->
val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
val bottomSheet = dialog.findViewById<FrameLayout>(com.google.android.material.R.id.design_bottom_sheet)
val behavior = bottomSheet?.let { BottomSheetBehavior.from(it) }
val wasKeyboardVisible = isKeyboardVisible
isKeyboardVisible = imeVisible
if (imeVisible && behavior?.state == BottomSheetBehavior.STATE_HALF_EXPANDED) {
behavior.state = BottomSheetBehavior.STATE_EXPANDED
} else if (!imeVisible && wasKeyboardVisible) {
updateLayout()
}
insets
}
}
fun updateLayout() {
@@ -246,12 +273,24 @@ class BottomSheetView(
val currentState = behavior.state
val oldRatio = behavior.halfExpandedRatio
var newRatio = getHalfExpandedRatio(contentHeight)
val newRatio = getHalfExpandedRatio(contentHeight)
behavior.halfExpandedRatio = newRatio
if (contentHeight > this.safeScreenHeight && behavior.state != BottomSheetBehavior.STATE_EXPANDED) {
if (preventExpansion) {
behavior.maxHeight = (behavior.halfExpandedRatio * screenHeight).toInt()
}
val targetHeight = this.getTargetHeight()
val availableHeight = screenHeight - getStatusBarHeight() - getNavigationBarHeight()
val shouldBeExpanded = targetHeight >= availableHeight
if (isKeyboardVisible) {
if (behavior.state != BottomSheetBehavior.STATE_EXPANDED) {
behavior.state = BottomSheetBehavior.STATE_EXPANDED
}
} else if (shouldBeExpanded && behavior.state != BottomSheetBehavior.STATE_EXPANDED && !preventExpansion) {
behavior.state = BottomSheetBehavior.STATE_EXPANDED
} else if (contentHeight < this.safeScreenHeight && behavior.state != BottomSheetBehavior.STATE_HALF_EXPANDED) {
} else if (!shouldBeExpanded && behavior.state != BottomSheetBehavior.STATE_HALF_EXPANDED) {
behavior.state = BottomSheetBehavior.STATE_HALF_EXPANDED
} else if (currentState == BottomSheetBehavior.STATE_HALF_EXPANDED && oldRatio != newRatio) {
behavior.state = BottomSheetBehavior.STATE_HALF_EXPANDED
@@ -279,25 +318,19 @@ class BottomSheetView(
private fun getTargetHeight(): Float {
val contentHeight = this.getContentHeight()
val height =
if (contentHeight > maxHeight) {
maxHeight
} else if (contentHeight < minHeight) {
minHeight
} else {
contentHeight
}
return height
return when {
contentHeight > maxHeight -> maxHeight
contentHeight < minHeight -> minHeight
else -> contentHeight
}
}
private fun clampRatio(ratio: Float): Float {
if (ratio < 0.01) {
return 0.01f
} else if (ratio > 0.99) {
return 0.99f
private fun clampRatio(ratio: Float): Float =
when {
ratio < 0.01 -> 0.01f
ratio > 0.99 -> 0.99f
else -> ratio
}
return ratio
}
private fun setDraggable(draggable: Boolean) {
val dialog = this.dialog ?: return
@@ -322,9 +355,7 @@ class BottomSheetView(
// View overrides to pass to DialogRootViewGroup instead
override fun dispatchProvideStructure(structure: ViewStructure?) {
if (structure == null) {
return
}
if (structure == null) return
dialogRootViewGroup.dispatchProvideStructure(structure)
}
@@ -363,7 +394,6 @@ class BottomSheetView(
// https://stackoverflow.com/questions/11862391/getheight-px-or-dpi
fun dpToPx(dp: Float): Float {
val displayMetrics = context.resources.displayMetrics
val px = dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT)
return px
return dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT)
}
}
@@ -52,6 +52,8 @@ class DialogRootViewGroup(
if (ReactFeatureFlags.dispatchPointerEvents) {
jSPointerDispatcher = JSPointerDispatcher(this)
}
fitsSystemWindows = false
}
override fun onSizeChanged(
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="EdgeToEdgeBottomSheetDialogTheme" parent="Theme.Material3.DayNight.BottomSheetDialog">
<!-- Enable edge-to-edge -->
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowIsFloating">false</item>
<item name="enableEdgeToEdge">true</item>
<!-- Configure bottom sheet to respect system window insets -->
<item name="bottomSheetStyle">@style/EdgeToEdgeBottomSheet</item>
</style>
<style name="EdgeToEdgeBottomSheet" parent="Widget.Material3.BottomSheet">
<item name="paddingBottomSystemWindowInsets">false</item>
<item name="paddingLeftSystemWindowInsets">true</item>
<item name="paddingRightSystemWindowInsets">true</item>
<item name="paddingTopSystemWindowInsets">false</item>
</style>
</resources>
@@ -175,6 +175,7 @@ function BottomSheetNativeComponentInner({
Platform.OS === 'android' && {
borderTopLeftRadius: cornerRadius,
borderTopRightRadius: cornerRadius,
overflow: 'hidden',
},
extraStyles,
]}>
@@ -34,12 +34,15 @@ class NotificationPrefs(
is Boolean -> {
putBoolean(key, value)
}
is String -> {
putString(key, value)
}
is Array<*> -> {
putStringSet(key, value.map { it.toString() }.toSet())
}
is Map<*, *> -> {
putStringSet(key, value.map { it.toString() }.toSet())
}
@@ -117,7 +117,7 @@ class ExpoReceiveAndroidIntentsModule : Module() {
private fun handleImageIntents(
uris: List<Uri>,
text: String?
text: String?,
) {
var allParams = ""
@@ -145,7 +145,7 @@ class ExpoReceiveAndroidIntentsModule : Module() {
private fun handleVideoIntents(
uris: List<Uri>,
text: String?
text: String?,
) {
val uri = uris[0]
// If there is no extension for the file, substringAfterLast returns the original string - not
+1 -1
View File
@@ -202,7 +202,7 @@
"react-native-edge-to-edge": "^1.6.0",
"react-native-gesture-handler": "~2.28.0",
"react-native-get-random-values": "~1.11.0",
"react-native-keyboard-controller": "1.18.5",
"react-native-keyboard-controller": "^1.20.7",
"react-native-pager-view": "6.8.0",
"react-native-progress": "bluesky-social/react-native-progress",
"react-native-qrcode-styled": "^0.3.3",
+1 -1
View File
@@ -3,6 +3,7 @@ import '#/view/icons'
import React, {useEffect, useState} from 'react'
import {GestureHandlerRootView} from 'react-native-gesture-handler'
import {KeyboardProvider as KeyboardControllerProvider} from 'react-native-keyboard-controller'
import {
initialWindowMetrics,
SafeAreaProvider,
@@ -14,7 +15,6 @@ import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import * as Sentry from '@sentry/react-native'
import {KeyboardControllerProvider} from '#/lib/hooks/useEnableKeyboardController'
import {Provider as HideBottomBarBorderProvider} from '#/lib/hooks/useHideBottomBarBorder'
import {QueryProvider} from '#/lib/react-query'
import {s} from '#/lib/styles'
+3 -7
View File
@@ -11,6 +11,7 @@ import {
} from 'react-native'
import {
KeyboardAwareScrollView,
type KeyboardAwareScrollViewRef,
useKeyboardHandler,
useReanimatedKeyboardAnimation,
} from 'react-native-keyboard-controller'
@@ -23,7 +24,6 @@ import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useEnableKeyboardController} from '#/lib/hooks/useEnableKeyboardController'
import {ScrollProvider} from '#/lib/ScrollContext'
import {logger} from '#/logger'
import {useA11y} from '#/state/a11y'
@@ -209,10 +209,9 @@ export const ScrollableInner = React.forwardRef<ScrollView, DialogInnerProps>(
const {nativeSnapPoint, disableDrag, setDisableDrag} = useDialogContext()
const insets = useSafeAreaInsets()
useEnableKeyboardController(IS_IOS)
const [keyboardHeight, setKeyboardHeight] = React.useState(0)
// note: iOS-only. keyboard-controller doesn't seem to work inside the sheets on Android
useKeyboardHandler(
{
onEnd: e => {
@@ -231,7 +230,6 @@ export const ScrollableInner = React.forwardRef<ScrollView, DialogInnerProps>(
}
paddingBottom = Math.max(paddingBottom, tokens.space._2xl)
} else {
paddingBottom += keyboardHeight
if (nativeSnapPoint === BottomSheetSnapPoint.Full) {
paddingBottom += insets.top
}
@@ -259,7 +257,7 @@ export const ScrollableInner = React.forwardRef<ScrollView, DialogInnerProps>(
{paddingBottom},
contentContainerStyle,
]}
ref={ref}
ref={ref as React.Ref<KeyboardAwareScrollViewRef>}
showsVerticalScrollIndicator={IS_ANDROID ? false : undefined}
{...props}
bounces={nativeSnapPoint === BottomSheetSnapPoint.Full}
@@ -289,8 +287,6 @@ export const InnerFlatList = React.forwardRef<
const insets = useSafeAreaInsets()
const {nativeSnapPoint, disableDrag, setDisableDrag} = useDialogContext()
useEnableKeyboardController(IS_IOS)
const onScroll = (e: ScrollEvent) => {
'worklet'
if (!IS_ANDROID) {
@@ -28,7 +28,7 @@ export function VerifierDialog({
verificationState: FullVerificationState
}) {
return (
<Dialog.Outer control={control}>
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
<Dialog.Handle />
<Inner
control={control}
@@ -123,7 +123,6 @@ function Inner({
}),
)}
size="small"
variant="solid"
color="primary"
style={[a.justify_center]}
onPress={() => {
@@ -138,7 +137,6 @@ function Inner({
<Button
label={_(msg`Close dialog`)}
size="small"
variant="solid"
color="secondary"
onPress={() => {
control.close()
@@ -1,107 +0,0 @@
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useRef,
} from 'react'
import {
KeyboardProvider,
useKeyboardController,
} from 'react-native-keyboard-controller'
import {useFocusEffect} from '@react-navigation/native'
const KeyboardControllerRefCountContext = createContext<{
incrementRefCount: () => void
decrementRefCount: () => void
}>({
incrementRefCount: () => {},
decrementRefCount: () => {},
})
KeyboardControllerRefCountContext.displayName =
'KeyboardControllerRefCountContext'
export function KeyboardControllerProvider({
children,
}: {
children: React.ReactNode
}) {
return (
<KeyboardProvider enabled={false} preload={false}>
<KeyboardControllerProviderInner>
{children}
</KeyboardControllerProviderInner>
</KeyboardProvider>
)
}
function KeyboardControllerProviderInner({
children,
}: {
children: React.ReactNode
}) {
const {setEnabled} = useKeyboardController()
const refCount = useRef(0)
const value = useMemo(
() => ({
incrementRefCount: () => {
refCount.current++
setEnabled(refCount.current > 0)
},
decrementRefCount: () => {
refCount.current--
setEnabled(refCount.current > 0)
if (__DEV__ && refCount.current < 0) {
console.error('KeyboardController ref count < 0')
}
},
}),
[setEnabled],
)
return (
<KeyboardControllerRefCountContext.Provider value={value}>
{children}
</KeyboardControllerRefCountContext.Provider>
)
}
export function useEnableKeyboardController(shouldEnable: boolean) {
const {incrementRefCount, decrementRefCount} = useContext(
KeyboardControllerRefCountContext,
)
useEffect(() => {
if (!shouldEnable) {
return
}
incrementRefCount()
return () => {
decrementRefCount()
}
}, [shouldEnable, incrementRefCount, decrementRefCount])
}
/**
* Like `useEnableKeyboardController`, but using `useFocusEffect`
*/
export function useEnableKeyboardControllerScreen(shouldEnable: boolean) {
const {incrementRefCount, decrementRefCount} = useContext(
KeyboardControllerRefCountContext,
)
useFocusEffect(
useCallback(() => {
if (!shouldEnable) {
return
}
incrementRefCount()
return () => {
decrementRefCount()
}
}, [shouldEnable, incrementRefCount, decrementRefCount]),
)
}
-3
View File
@@ -4,7 +4,6 @@ import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {usePreventRemove} from '@react-navigation/native'
import {useEnableKeyboardControllerScreen} from '#/lib/hooks/useEnableKeyboardController'
import {
type AllNavigatorParams,
type NativeStackScreenProps,
@@ -37,8 +36,6 @@ export function FindContactsFlowScreen({navigation}: Props) {
})
})
useEnableKeyboardControllerScreen(true)
const setMinimalShellMode = useSetMinimalShellMode()
const effect = useCallback(() => {
setMinimalShellMode(true)
-3
View File
@@ -15,7 +15,6 @@ import {
} from '@react-navigation/native'
import {type NativeStackScreenProps} from '@react-navigation/native-stack'
import {useEnableKeyboardControllerScreen} from '#/lib/hooks/useEnableKeyboardController'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
import {
type CommonNavigatorParams,
@@ -68,8 +67,6 @@ export function MessagesConversationScreenInner({route}: Props) {
const convoId = route.params.conversation
const {setCurrentConvoId} = useCurrentConvoId()
useEnableKeyboardControllerScreen(true)
useFocusEffect(
useCallback(() => {
setCurrentConvoId(convoId)
-3
View File
@@ -2,7 +2,6 @@ import {useMemo, useReducer} from 'react'
import {View} from 'react-native'
import * as bcp47Match from 'bcp-47-match'
import {useEnableKeyboardControllerScreen} from '#/lib/hooks/useEnableKeyboardController'
import {useLanguagePrefs} from '#/state/preferences'
import {
Layout,
@@ -60,8 +59,6 @@ export function Onboarding() {
)
const [contactsFlowState, contactsFlowDispatch] = useFindContactsFlowState()
useEnableKeyboardControllerScreen(true)
return (
<Portal>
<View style={[a.absolute, a.inset_0, t.atoms.bg]}>
-3
View File
@@ -16,7 +16,6 @@ import {useFocusEffect, useNavigation} from '@react-navigation/native'
import {type NativeStackScreenProps} from '@react-navigation/native-stack'
import {STARTER_PACK_MAX_SIZE} from '#/lib/constants'
import {useEnableKeyboardControllerScreen} from '#/lib/hooks/useEnableKeyboardController'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
import {
type CommonNavigatorParams,
@@ -185,8 +184,6 @@ function WizardInner({
})
}, [navigation])
useEnableKeyboardControllerScreen(true)
useFocusEffect(
React.useCallback(() => {
setMinimalShellMode(true)
-3
View File
@@ -12,7 +12,6 @@ import {
BLUESKY_MOD_SERVICE_HEADERS,
MAX_REPORT_REASON_GRAPHEME_LENGTH,
} from '#/lib/constants'
import {useEnableKeyboardController} from '#/lib/hooks/useEnableKeyboardController'
import {cleanError} from '#/lib/strings/errors'
import {useAgent, useSession, useSessionApi} from '#/state/session'
import {CharProgress} from '#/view/com/composer/char-progress/CharProgress'
@@ -121,8 +120,6 @@ export function Takendown() {
const webLayout = IS_WEB && gtMobile
useEnableKeyboardController(true)
return (
<View style={[a.util_screen_outer, a.flex_1]}>
<KeyboardAwareScrollView style={[a.flex_1, t.atoms.bg]} centerContent>
@@ -5,6 +5,7 @@ import {msg, Plural, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {MAX_ALT_TEXT} from '#/lib/constants'
import {useIsKeyboardVisible} from '#/lib/hooks/useIsKeyboardVisible'
import {enforceLen} from '#/lib/strings/helpers'
import {type ComposerImage} from '#/state/gallery'
import {AltTextCounterWrapper} from '#/view/com/composer/AltTextCounterWrapper'
@@ -28,6 +29,7 @@ export const ImageAltTextDialog = ({
image,
onChange,
}: Props): React.ReactNode => {
const {height: minHeight} = useWindowDimensions()
const [altText, setAltText] = React.useState(image.alt)
return (
@@ -38,7 +40,8 @@ export const ImageAltTextDialog = ({
...image,
alt: enforceLen(altText, MAX_ALT_TEXT, true),
})
}}>
}}
nativeOptions={{minHeight}}>
<Dialog.Handle />
<ImageAltTextInner
control={control}
@@ -65,6 +68,8 @@ const ImageAltTextInner = ({
const t = useTheme()
const windim = useWindowDimensions()
const [isKeyboardVisible] = useIsKeyboardVisible()
const imageStyle = React.useMemo<ImageStyle>(() => {
const maxWidth = IS_WEB ? 450 : windim.width
const source = image.transformed ?? image.source
@@ -165,7 +170,7 @@ const ImageAltTextInner = ({
</AltTextCounterWrapper>
</View>
{/* Maybe fix this later -h */}
{IS_ANDROID ? <View style={{height: 300}} /> : null}
{IS_ANDROID && isKeyboardVisible ? <View style={{height: 300}} /> : null}
</Dialog.ScrollableInner>
)
}
+4 -4
View File
@@ -17163,10 +17163,10 @@ react-native-is-edge-to-edge@^1.2.1:
resolved "https://registry.yarnpkg.com/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.2.1.tgz#64e10851abd9d176cbf2b40562f751622bde3358"
integrity sha512-FLbPWl/MyYQWz+KwqOZsSyj2JmLKglHatd3xLZWskXOpRaio4LfEDEz8E/A6uD8QoTHW6Aobw1jbEwK7KMgR7Q==
react-native-keyboard-controller@1.18.5:
version "1.18.5"
resolved "https://registry.yarnpkg.com/react-native-keyboard-controller/-/react-native-keyboard-controller-1.18.5.tgz#ae12131f2019c574178479d2c55784f55e08bb68"
integrity sha512-wbYN6Tcu3G5a05dhRYBgjgd74KqoYWuUmroLpigRg9cXy5uYo7prTMIvMgvLtARQtUF7BOtFggUnzgoBOgk0TQ==
react-native-keyboard-controller@^1.20.7:
version "1.20.7"
resolved "https://registry.yarnpkg.com/react-native-keyboard-controller/-/react-native-keyboard-controller-1.20.7.tgz#e1be1c15a5eb10b96a40a0812d8472e6e4bd8f29"
integrity sha512-G8S5jz1FufPrcL1vPtReATx+jJhT/j+sTqxMIb30b1z7cYEfMlkIzOCyaHgf6IMB2KA9uBmnA5M6ve2A9Ou4kw==
dependencies:
react-native-is-edge-to-edge "^1.2.1"