make scrollview automatically adapt to footer height

This commit is contained in:
Samuel Newman
2025-06-19 22:46:12 +03:00
parent 95f9cc1ed5
commit df975ddcfa
10 changed files with 199 additions and 161 deletions
@@ -279,14 +279,14 @@ export function MessagesList({
onMove: e => {
'worklet'
keyboardHeight.set(e.height)
if (e.height > footerHeight.get()) {
if (e.height > footerHeight) {
scrollTo(flatListRef, 0, 1e7, false)
}
},
onEnd: e => {
'worklet'
keyboardHeight.set(e.height)
if (e.height > footerHeight.get()) {
if (e.height > footerHeight) {
scrollTo(flatListRef, 0, 1e7, false)
}
keyboardIsOpening.set(false)
@@ -296,13 +296,11 @@ export function MessagesList({
)
const animatedListStyle = useAnimatedStyle(() => ({
marginBottom: Math.max(keyboardHeight.get(), footerHeight.get()),
marginBottom: Math.max(keyboardHeight.get(), footerHeight),
}))
const animatedStickyViewStyle = useAnimatedStyle(() => ({
transform: [
{translateY: -Math.max(keyboardHeight.get(), footerHeight.get())},
],
transform: [{translateY: -Math.max(keyboardHeight.get(), footerHeight)}],
}))
// -- Message sending
@@ -446,6 +444,8 @@ export function MessagesList({
ListHeaderComponent={
<MaybeLoader isLoading={convoState.isFetchingHistory} />
}
contentInset={{bottom: 0}}
contentContainerStyle={{paddingBottom: 0}}
/>
</ScrollProvider>
<Animated.View style={animatedStickyViewStyle}>
+23 -13
View File
@@ -1,6 +1,5 @@
import {useCallback, useEffect, useMemo, useRef, useState} from 'react'
import {useWindowDimensions, View} from 'react-native'
import Animated, {useAnimatedStyle} from 'react-native-reanimated'
import {type LayoutChangeEvent, useWindowDimensions, View} from 'react-native'
import {Trans} from '@lingui/macro'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
@@ -60,6 +59,8 @@ export function PostThread({uri}: {uri: string}) {
hasSession,
)
const [promptHeight, setPromptHeight] = useState(43)
/*
* One query to rule them all
*/
@@ -519,6 +520,7 @@ export function PostThread({uri}: {uri: string}) {
)
const defaultListFooterHeight = hasParents ? windowHeight - 200 : undefined
const hasPrompt = !gtMobile && canReply && hasSession
return (
<PostThreadContextProvider context={thread.context}>
@@ -604,29 +606,37 @@ export function PostThread({uri}: {uri: string}) {
* Default: 50
*/
updateCellsBatchingPeriod={100}
footerExtensionHeight={hasPrompt ? promptHeight : 0}
/>
)}
{!gtMobile && canReply && hasSession && (
<MobileComposePrompt onPressReply={onReplyToAnchor} />
{hasPrompt && (
<MobileComposePrompt
onPressReply={onReplyToAnchor}
onLayout={evt =>
setPromptHeight(Math.round(evt.nativeEvent.layout.height))
}
/>
)}
</PostThreadContextProvider>
)
}
function MobileComposePrompt({onPressReply}: {onPressReply: () => unknown}) {
function MobileComposePrompt({
onPressReply,
onLayout,
}: {
onPressReply: () => unknown
onLayout?: (event: LayoutChangeEvent) => void
}) {
const {footerHeight} = useShellLayout()
const animatedStyle = useAnimatedStyle(() => {
return {
bottom: footerHeight.get(),
}
})
return (
<Animated.View style={[a.fixed, a.left_0, a.right_0, animatedStyle]}>
<View
style={[a.fixed, a.left_0, a.right_0, {bottom: footerHeight}]}
onLayout={onLayout}>
<ThreadComposePrompt onPressCompose={onPressReply} />
</Animated.View>
</View>
)
}