get iOS working properly with contentInset
This commit is contained in:
@@ -148,6 +148,12 @@ export function MessagesList({
|
|||||||
// we will not scroll whenever new items get prepended to the top.
|
// we will not scroll whenever new items get prepended to the top.
|
||||||
const onContentSizeChange = useCallback(
|
const onContentSizeChange = useCallback(
|
||||||
(_: number, height: number) => {
|
(_: number, height: number) => {
|
||||||
|
// ignore very small changes
|
||||||
|
if (Math.abs(prevContentHeight.current - height) < 0.01) {
|
||||||
|
prevContentHeight.current = height
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Because web does not have `maintainVisibleContentPosition` support, we will need to manually scroll to the
|
// Because web does not have `maintainVisibleContentPosition` support, we will need to manually scroll to the
|
||||||
// previous off whenever we add new content to the previous offset whenever we add new content to the list.
|
// previous off whenever we add new content to the previous offset whenever we add new content to the list.
|
||||||
if (isWeb && isAtTop.get() && hasScrolled) {
|
if (isWeb && isAtTop.get() && hasScrolled) {
|
||||||
@@ -249,6 +255,7 @@ export function MessagesList({
|
|||||||
|
|
||||||
const keyboardHeight = useSharedValue(0)
|
const keyboardHeight = useSharedValue(0)
|
||||||
const keyboardIsOpening = useSharedValue(false)
|
const keyboardIsOpening = useSharedValue(false)
|
||||||
|
const keyboardIsInteractive = useSharedValue(false)
|
||||||
|
|
||||||
// In some cases - like when the emoji piker opens - we don't want to animate the scroll in the list onLayout event.
|
// In some cases - like when the emoji piker opens - we don't want to animate the scroll in the list onLayout event.
|
||||||
// We use this value to keep track of when we want to disable the animation.
|
// We use this value to keep track of when we want to disable the animation.
|
||||||
@@ -272,21 +279,27 @@ export function MessagesList({
|
|||||||
onMove: e => {
|
onMove: e => {
|
||||||
'worklet'
|
'worklet'
|
||||||
keyboardHeight.set(e.height)
|
keyboardHeight.set(e.height)
|
||||||
if (e.height > footerHeight.get()) {
|
if (
|
||||||
|
(e.height > footerHeight.get() || isIOS) &&
|
||||||
|
!keyboardIsInteractive.get()
|
||||||
|
) {
|
||||||
scrollTo(flatListRef, 0, 1e7, false)
|
scrollTo(flatListRef, 0, 1e7, false)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onInteractive: e => {
|
onInteractive: e => {
|
||||||
'worklet'
|
'worklet'
|
||||||
keyboardHeight.set(e.height)
|
keyboardHeight.set(e.height)
|
||||||
if (e.height > footerHeight.get()) {
|
if (!keyboardIsInteractive.get()) {
|
||||||
scrollTo(flatListRef, 0, 1e7, false)
|
keyboardIsInteractive.set(true)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onEnd: e => {
|
onEnd: e => {
|
||||||
'worklet'
|
'worklet'
|
||||||
keyboardHeight.set(e.height)
|
keyboardHeight.set(e.height)
|
||||||
if (e.height > footerHeight.get()) {
|
if (
|
||||||
|
(e.height > footerHeight.get() || isIOS) &&
|
||||||
|
!keyboardIsInteractive.get()
|
||||||
|
) {
|
||||||
scrollTo(flatListRef, 0, 1e7, false)
|
scrollTo(flatListRef, 0, 1e7, false)
|
||||||
}
|
}
|
||||||
keyboardIsOpening.set(false)
|
keyboardIsOpening.set(false)
|
||||||
@@ -295,17 +308,17 @@ export function MessagesList({
|
|||||||
[],
|
[],
|
||||||
)
|
)
|
||||||
|
|
||||||
const animatedListProps = useAnimatedProps(
|
const animatedListProps = useAnimatedProps(() => {
|
||||||
() =>
|
return {
|
||||||
({
|
contentInset: {
|
||||||
contentInset: {
|
top: 0,
|
||||||
top: 0,
|
bottom:
|
||||||
bottom: Math.max(keyboardHeight.get(), inputAreaHeight),
|
Math.max(keyboardHeight.get(), footerHeight.get()) + inputAreaHeight,
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
},
|
},
|
||||||
} satisfies Partial<ListProps>),
|
} satisfies Partial<ListProps>
|
||||||
)
|
})
|
||||||
|
|
||||||
const animatedStickyViewStyle = useAnimatedStyle(() => ({
|
const animatedStickyViewStyle = useAnimatedStyle(() => ({
|
||||||
transform: [
|
transform: [
|
||||||
@@ -394,15 +407,24 @@ export function MessagesList({
|
|||||||
)
|
)
|
||||||
|
|
||||||
// -- List layout changes (opening emoji keyboard, etc.)
|
// -- List layout changes (opening emoji keyboard, etc.)
|
||||||
|
const prevLayoutHeight = useRef(0)
|
||||||
const onListLayout = useCallback(
|
const onListLayout = useCallback(
|
||||||
(e: LayoutChangeEvent) => {
|
(e: LayoutChangeEvent) => {
|
||||||
|
const diff = e.nativeEvent.layout.height - prevLayoutHeight.current
|
||||||
|
// is there a better way to detect this? -sfn
|
||||||
|
const isInsetAdjustment = Math.abs(diff - inputAreaHeight) < 1
|
||||||
|
|
||||||
|
prevLayoutHeight.current = e.nativeEvent.layout.height
|
||||||
layoutHeight.set(e.nativeEvent.layout.height)
|
layoutHeight.set(e.nativeEvent.layout.height)
|
||||||
|
|
||||||
if (isWeb || !keyboardIsOpening.get()) {
|
if (isWeb || (!keyboardIsOpening.get() && !isInsetAdjustment)) {
|
||||||
|
const skipAnimation = layoutScrollWithoutAnimation.get()
|
||||||
flatListRef.current?.scrollToEnd({
|
flatListRef.current?.scrollToEnd({
|
||||||
animated: !layoutScrollWithoutAnimation.get(),
|
animated: !skipAnimation,
|
||||||
})
|
})
|
||||||
layoutScrollWithoutAnimation.set(false)
|
if (skipAnimation) {
|
||||||
|
layoutScrollWithoutAnimation.set(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
@@ -410,9 +432,16 @@ export function MessagesList({
|
|||||||
keyboardIsOpening,
|
keyboardIsOpening,
|
||||||
layoutScrollWithoutAnimation,
|
layoutScrollWithoutAnimation,
|
||||||
layoutHeight,
|
layoutHeight,
|
||||||
|
inputAreaHeight,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const onInputAreaLayout = useCallback(
|
||||||
|
(evt: LayoutChangeEvent) =>
|
||||||
|
setInputAreaHeight(evt.nativeEvent.layout.height),
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
|
||||||
const scrollToEndOnPress = useCallback(() => {
|
const scrollToEndOnPress = useCallback(() => {
|
||||||
flatListRef.current?.scrollToOffset({
|
flatListRef.current?.scrollToOffset({
|
||||||
offset: prevContentHeight.current,
|
offset: prevContentHeight.current,
|
||||||
@@ -434,8 +463,10 @@ export function MessagesList({
|
|||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
keyExtractor={keyExtractor}
|
keyExtractor={keyExtractor}
|
||||||
animatedProps={animatedListProps}
|
animatedProps={animatedListProps}
|
||||||
|
// required - otherwise it will overwrite the value in animatedProps
|
||||||
|
automaticallyAdjustContentInsets={false}
|
||||||
|
automaticallyAdjustsScrollIndicatorInsets={false}
|
||||||
disableFullWindowScroll={true}
|
disableFullWindowScroll={true}
|
||||||
style={{backgroundColor: 'red'}}
|
|
||||||
disableVirtualization={true}
|
disableVirtualization={true}
|
||||||
// The extra two items account for the header and the footer components
|
// The extra two items account for the header and the footer components
|
||||||
initialNumToRender={isNative ? 32 : 62}
|
initialNumToRender={isNative ? 32 : 62}
|
||||||
@@ -461,8 +492,12 @@ export function MessagesList({
|
|||||||
</KeyboardGestureArea>
|
</KeyboardGestureArea>
|
||||||
</ScrollProvider>
|
</ScrollProvider>
|
||||||
<Animated.View
|
<Animated.View
|
||||||
style={animatedStickyViewStyle}
|
style={[
|
||||||
onLayout={evt => setInputAreaHeight(evt.nativeEvent.layout.height)}>
|
a.absolute,
|
||||||
|
{bottom: 0, left: 0, right: 0},
|
||||||
|
animatedStickyViewStyle,
|
||||||
|
]}
|
||||||
|
onLayout={onInputAreaLayout}>
|
||||||
<BlurWrapper>
|
<BlurWrapper>
|
||||||
{convoState.status === ConvoStatus.Disabled ? (
|
{convoState.status === ConvoStatus.Disabled ? (
|
||||||
<ChatDisabled />
|
<ChatDisabled />
|
||||||
|
|||||||
Reference in New Issue
Block a user