diff --git a/src/screens/Messages/Conversation/MessageInput.tsx b/src/screens/Messages/Conversation/MessageInput.tsx index 1e33efdf..bb55bd3a 100644 --- a/src/screens/Messages/Conversation/MessageInput.tsx +++ b/src/screens/Messages/Conversation/MessageInput.tsx @@ -1,13 +1,16 @@ import React from 'react' +import {Pressable, TextInput, useWindowDimensions, View} from 'react-native' import { - Dimensions, - Keyboard, - NativeSyntheticEvent, - Pressable, - TextInput, - TextInputContentSizeChangeEventData, - View, -} from 'react-native' + useFocusedInputHandler, + useReanimatedKeyboardAnimation, +} from 'react-native-keyboard-controller' +import Animated, { + measure, + useAnimatedProps, + useAnimatedRef, + useAnimatedStyle, + useSharedValue, +} from 'react-native-reanimated' import {useSafeAreaInsets} from 'react-native-safe-area-context' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' @@ -25,6 +28,8 @@ import {atoms as a, useTheme} from '#/alf' import {useSharedInputStyles} from '#/components/forms/TextField' import {PaperPlane_Stroke2_Corner0_Rounded as PaperPlane} from '#/components/icons/PaperPlane' +const AnimatedTextInput = Animated.createAnimatedComponent(TextInput) + export function MessageInput({ onSendMessage, }: { @@ -34,15 +39,19 @@ export function MessageInput({ const t = useTheme() const playHaptic = useHaptics() const {getDraft, clearDraft} = useMessageDraft() - const [message, setMessage] = React.useState(getDraft) - const [maxHeight, setMaxHeight] = React.useState() - const [isInputScrollable, setIsInputScrollable] = React.useState(false) + // Input layout const {top: topInset} = useSafeAreaInsets() + const {height: windowHeight} = useWindowDimensions() + const {height: keyboardHeight} = useReanimatedKeyboardAnimation() + const maxHeight = useSharedValue(undefined) + const isInputScrollable = useSharedValue(false) + // const [isInputScrollable, setIsInputScrollable] = React.useState(false) const inputStyles = useSharedInputStyles() const [isFocused, setIsFocused] = React.useState(false) - const inputRef = React.useRef(null) + const [message, setMessage] = React.useState(getDraft) + const inputRef = useAnimatedRef() useSaveMessageDraft(message) @@ -64,22 +73,33 @@ export function MessageInput({ setTimeout(() => { inputRef.current?.focus() }, 100) - }, [message, onSendMessage, playHaptic, _, clearDraft]) + }, [message, clearDraft, onSendMessage, playHaptic, _, inputRef]) - const onInputLayout = React.useCallback( - (e: NativeSyntheticEvent) => { - const keyboardHeight = Keyboard.metrics()?.height ?? 0 - const windowHeight = Dimensions.get('window').height + useFocusedInputHandler( + { + onChangeText: () => { + 'worklet' + const measurement = measure(inputRef) + if (!measurement) return - const max = windowHeight - keyboardHeight - topInset - 150 - const availableSpace = max - e.nativeEvent.contentSize.height + const max = windowHeight - -keyboardHeight.value - topInset - 150 + const availableSpace = max - measurement.height - setMaxHeight(max) - setIsInputScrollable(availableSpace < 30) + maxHeight.value = max + isInputScrollable.value = availableSpace < 30 + }, }, - [topInset], + [windowHeight, topInset], ) + const animatedStyle = useAnimatedStyle(() => ({ + maxHeight: maxHeight.value, + })) + + const animatedProps = useAnimatedProps(() => ({ + scrollEnabled: isInputScrollable.value, + })) + return ( - setIsFocused(true)} onBlur={() => setIsFocused(false)} - onContentSizeChange={onInputLayout} ref={inputRef} hitSlop={HITSLOP_10} + animatedProps={animatedProps} />