diff options
author | Hailey <me@haileyok.com> | 2024-05-20 13:01:21 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-20 13:01:21 -0700 |
commit | 52a885ad19b6120d29d5f3edb94b31e265ed140e (patch) | |
tree | b018f0e929ae604bf520076c5a1039d2aad39469 /src | |
parent | 994af1454f668af8ae051d1bd8395e626f6f19e8 (diff) | |
download | voidsky-52a885ad19b6120d29d5f3edb94b31e265ed140e.tar.zst |
[🐴] Make message input layout resizing synchronous (#4123)
* make input resizing synchronous * remove a log * make scroll enable/disable sync * lint * start as undefined
Diffstat (limited to 'src')
-rw-r--r-- | src/screens/Messages/Conversation/MessageInput.tsx | 72 |
1 files changed, 46 insertions, 26 deletions
diff --git a/src/screens/Messages/Conversation/MessageInput.tsx b/src/screens/Messages/Conversation/MessageInput.tsx index 1e33efdf6..bb55bd3a5 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<number | undefined>() - const [isInputScrollable, setIsInputScrollable] = React.useState(false) + // Input layout const {top: topInset} = useSafeAreaInsets() + const {height: windowHeight} = useWindowDimensions() + const {height: keyboardHeight} = useReanimatedKeyboardAnimation() + const maxHeight = useSharedValue<undefined | number>(undefined) + const isInputScrollable = useSharedValue(false) + // const [isInputScrollable, setIsInputScrollable] = React.useState(false) const inputStyles = useSharedInputStyles() const [isFocused, setIsFocused] = React.useState(false) - const inputRef = React.useRef<TextInput>(null) + const [message, setMessage] = React.useState(getDraft) + const inputRef = useAnimatedRef<TextInput>() 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<TextInputContentSizeChangeEventData>) => { - 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 ( <View style={[a.px_md, a.pb_sm, a.pt_xs]}> <View @@ -96,7 +116,7 @@ export function MessageInput({ }, isFocused && inputStyles.chromeFocus, ]}> - <TextInput + <AnimatedTextInput accessibilityLabel={_(msg`Message input field`)} accessibilityHint={_(msg`Type your message here`)} placeholder={_(msg`Write a message`)} @@ -109,16 +129,16 @@ export function MessageInput({ a.text_md, a.px_sm, t.atoms.text, - {maxHeight, paddingBottom: isIOS ? 5 : 0}, + {paddingBottom: isIOS ? 5 : 0}, + animatedStyle, ]} keyboardAppearance={t.name === 'light' ? 'light' : 'dark'} - scrollEnabled={isInputScrollable} blurOnSubmit={false} onFocus={() => setIsFocused(true)} onBlur={() => setIsFocused(false)} - onContentSizeChange={onInputLayout} ref={inputRef} hitSlop={HITSLOP_10} + animatedProps={animatedProps} /> <Pressable accessibilityRole="button" |