Bluesky app fork with some witchin' additions ๐Ÿ’ซ
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

[๐Ÿด] Make message input layout resizing synchronous (#4123)

* make input resizing synchronous

* remove a log

* make scroll enable/disable sync

* lint

* start as undefined

authored by

Hailey and committed by
GitHub
52a885ad 994af145

+46 -26
+46 -26
src/screens/Messages/Conversation/MessageInput.tsx
··· 1 1 import React from 'react' 2 + import {Pressable, TextInput, useWindowDimensions, View} from 'react-native' 2 3 import { 3 - Dimensions, 4 - Keyboard, 5 - NativeSyntheticEvent, 6 - Pressable, 7 - TextInput, 8 - TextInputContentSizeChangeEventData, 9 - View, 10 - } from 'react-native' 4 + useFocusedInputHandler, 5 + useReanimatedKeyboardAnimation, 6 + } from 'react-native-keyboard-controller' 7 + import Animated, { 8 + measure, 9 + useAnimatedProps, 10 + useAnimatedRef, 11 + useAnimatedStyle, 12 + useSharedValue, 13 + } from 'react-native-reanimated' 11 14 import {useSafeAreaInsets} from 'react-native-safe-area-context' 12 15 import {msg} from '@lingui/macro' 13 16 import {useLingui} from '@lingui/react' ··· 25 28 import {useSharedInputStyles} from '#/components/forms/TextField' 26 29 import {PaperPlane_Stroke2_Corner0_Rounded as PaperPlane} from '#/components/icons/PaperPlane' 27 30 31 + const AnimatedTextInput = Animated.createAnimatedComponent(TextInput) 32 + 28 33 export function MessageInput({ 29 34 onSendMessage, 30 35 }: { ··· 34 39 const t = useTheme() 35 40 const playHaptic = useHaptics() 36 41 const {getDraft, clearDraft} = useMessageDraft() 37 - const [message, setMessage] = React.useState(getDraft) 38 - const [maxHeight, setMaxHeight] = React.useState<number | undefined>() 39 - const [isInputScrollable, setIsInputScrollable] = React.useState(false) 40 42 43 + // Input layout 41 44 const {top: topInset} = useSafeAreaInsets() 45 + const {height: windowHeight} = useWindowDimensions() 46 + const {height: keyboardHeight} = useReanimatedKeyboardAnimation() 47 + const maxHeight = useSharedValue<undefined | number>(undefined) 48 + const isInputScrollable = useSharedValue(false) 49 + // const [isInputScrollable, setIsInputScrollable] = React.useState(false) 42 50 43 51 const inputStyles = useSharedInputStyles() 44 52 const [isFocused, setIsFocused] = React.useState(false) 45 - const inputRef = React.useRef<TextInput>(null) 53 + const [message, setMessage] = React.useState(getDraft) 54 + const inputRef = useAnimatedRef<TextInput>() 46 55 47 56 useSaveMessageDraft(message) 48 57 ··· 64 73 setTimeout(() => { 65 74 inputRef.current?.focus() 66 75 }, 100) 67 - }, [message, onSendMessage, playHaptic, _, clearDraft]) 76 + }, [message, clearDraft, onSendMessage, playHaptic, _, inputRef]) 68 77 69 - const onInputLayout = React.useCallback( 70 - (e: NativeSyntheticEvent<TextInputContentSizeChangeEventData>) => { 71 - const keyboardHeight = Keyboard.metrics()?.height ?? 0 72 - const windowHeight = Dimensions.get('window').height 78 + useFocusedInputHandler( 79 + { 80 + onChangeText: () => { 81 + 'worklet' 82 + const measurement = measure(inputRef) 83 + if (!measurement) return 73 84 74 - const max = windowHeight - keyboardHeight - topInset - 150 75 - const availableSpace = max - e.nativeEvent.contentSize.height 85 + const max = windowHeight - -keyboardHeight.value - topInset - 150 86 + const availableSpace = max - measurement.height 76 87 77 - setMaxHeight(max) 78 - setIsInputScrollable(availableSpace < 30) 88 + maxHeight.value = max 89 + isInputScrollable.value = availableSpace < 30 90 + }, 79 91 }, 80 - [topInset], 92 + [windowHeight, topInset], 81 93 ) 82 94 95 + const animatedStyle = useAnimatedStyle(() => ({ 96 + maxHeight: maxHeight.value, 97 + })) 98 + 99 + const animatedProps = useAnimatedProps(() => ({ 100 + scrollEnabled: isInputScrollable.value, 101 + })) 102 + 83 103 return ( 84 104 <View style={[a.px_md, a.pb_sm, a.pt_xs]}> 85 105 <View ··· 96 116 }, 97 117 isFocused && inputStyles.chromeFocus, 98 118 ]}> 99 - <TextInput 119 + <AnimatedTextInput 100 120 accessibilityLabel={_(msg`Message input field`)} 101 121 accessibilityHint={_(msg`Type your message here`)} 102 122 placeholder={_(msg`Write a message`)} ··· 109 129 a.text_md, 110 130 a.px_sm, 111 131 t.atoms.text, 112 - {maxHeight, paddingBottom: isIOS ? 5 : 0}, 132 + {paddingBottom: isIOS ? 5 : 0}, 133 + animatedStyle, 113 134 ]} 114 135 keyboardAppearance={t.name === 'light' ? 'light' : 'dark'} 115 - scrollEnabled={isInputScrollable} 116 136 blurOnSubmit={false} 117 137 onFocus={() => setIsFocused(true)} 118 138 onBlur={() => setIsFocused(false)} 119 - onContentSizeChange={onInputLayout} 120 139 ref={inputRef} 121 140 hitSlop={HITSLOP_10} 141 + animatedProps={animatedProps} 122 142 /> 123 143 <Pressable 124 144 accessibilityRole="button"