Fix visibility of the mentions autocomplete in the composer (#326) (#329)

* Improve layout in composer to ensure the mentions autocomplete is visible (closes #326)

* Dont dismiss the keyboard in the composer
zio/stable
Paul Frazee 2023-03-20 16:10:29 -05:00 committed by GitHub
parent df6a712834
commit 8a3601c07c
3 changed files with 67 additions and 50 deletions

View File

@ -32,6 +32,7 @@ import {SelectedPhotos} from './photos/SelectedPhotos'
import {usePalette} from 'lib/hooks/usePalette' import {usePalette} from 'lib/hooks/usePalette'
import QuoteEmbed from '../util/PostEmbeds/QuoteEmbed' import QuoteEmbed from '../util/PostEmbeds/QuoteEmbed'
import {useExternalLinkFetch} from './useExternalLinkFetch' import {useExternalLinkFetch} from './useExternalLinkFetch'
import {isDesktopWeb} from 'platform/detection'
const MAX_TEXT_LENGTH = 256 const MAX_TEXT_LENGTH = 256
@ -188,10 +189,6 @@ export const ComposePost = observer(function ComposePost({
const canPost = text.length <= MAX_TEXT_LENGTH const canPost = text.length <= MAX_TEXT_LENGTH
const selectTextInputLayout =
selectedPhotos.length !== 0
? styles.textInputLayoutWithPhoto
: styles.textInputLayoutWithoutPhoto
const selectTextInputPlaceholder = replyTo const selectTextInputPlaceholder = replyTo
? 'Write your reply' ? 'Write your reply'
: selectedPhotos.length !== 0 : selectedPhotos.length !== 0
@ -253,7 +250,9 @@ export const ComposePost = observer(function ComposePost({
<Text style={[s.red4, s.flex1]}>{error}</Text> <Text style={[s.red4, s.flex1]}>{error}</Text>
</View> </View>
)} )}
<ScrollView style={s.flex1}> <ScrollView
style={styles.scrollView}
keyboardShouldPersistTaps="always">
{replyTo ? ( {replyTo ? (
<View style={[pal.border, styles.replyToLayout]}> <View style={[pal.border, styles.replyToLayout]}>
<UserAvatar avatar={replyTo.author.avatar} size={50} /> <UserAvatar avatar={replyTo.author.avatar} size={50} />
@ -268,12 +267,7 @@ export const ComposePost = observer(function ComposePost({
</View> </View>
) : undefined} ) : undefined}
<View <View style={[pal.border, styles.textInputLayout]}>
style={[
pal.border,
styles.textInputLayout,
selectTextInputLayout,
]}>
<UserAvatar avatar={store.me.avatar} size={50} /> <UserAvatar avatar={store.me.avatar} size={50} />
<TextInput <TextInput
ref={textInput} ref={textInput}
@ -346,14 +340,14 @@ const styles = StyleSheet.create({
outer: { outer: {
flexDirection: 'column', flexDirection: 'column',
flex: 1, flex: 1,
padding: 15,
height: '100%', height: '100%',
}, },
topbar: { topbar: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
paddingTop: isDesktopWeb ? 10 : undefined,
paddingBottom: 10, paddingBottom: 10,
paddingHorizontal: 5, paddingHorizontal: 20,
height: 55, height: 55,
}, },
postBtn: { postBtn: {
@ -365,12 +359,14 @@ const styles = StyleSheet.create({
borderRadius: 6, borderRadius: 6,
paddingHorizontal: 8, paddingHorizontal: 8,
paddingVertical: 6, paddingVertical: 6,
marginHorizontal: 15,
marginBottom: 6, marginBottom: 6,
}, },
errorLine: { errorLine: {
flexDirection: 'row', flexDirection: 'row',
backgroundColor: colors.red1, backgroundColor: colors.red1,
borderRadius: 6, borderRadius: 6,
marginHorizontal: 15,
paddingHorizontal: 8, paddingHorizontal: 8,
paddingVertical: 6, paddingVertical: 6,
marginVertical: 6, marginVertical: 6,
@ -386,13 +382,12 @@ const styles = StyleSheet.create({
justifyContent: 'center', justifyContent: 'center',
marginRight: 5, marginRight: 5,
}, },
textInputLayoutWithPhoto: { scrollView: {
flexWrap: 'wrap',
},
textInputLayoutWithoutPhoto: {
flex: 1, flex: 1,
paddingHorizontal: 15,
}, },
textInputLayout: { textInputLayout: {
flex: isDesktopWeb ? undefined : 1,
flexDirection: 'row', flexDirection: 'row',
borderTopWidth: 1, borderTopWidth: 1,
paddingTop: 16, paddingTop: 16,
@ -418,7 +413,8 @@ const styles = StyleSheet.create({
bottomBar: { bottomBar: {
flexDirection: 'row', flexDirection: 'row',
paddingVertical: 10, paddingVertical: 10,
paddingRight: 5, paddingLeft: 15,
paddingRight: 20,
alignItems: 'center', alignItems: 'center',
borderTopWidth: 1, borderTopWidth: 1,
}, },

View File

@ -3,6 +3,7 @@ import {
NativeSyntheticEvent, NativeSyntheticEvent,
StyleSheet, StyleSheet,
TextInputSelectionChangeEventData, TextInputSelectionChangeEventData,
View,
} from 'react-native' } from 'react-native'
import PasteInput, { import PasteInput, {
PastedFile, PastedFile,
@ -185,7 +186,7 @@ export const TextInput = React.forwardRef(
}, [text, pal.link, pal.text]) }, [text, pal.link, pal.text])
return ( return (
<> <View style={styles.container}>
<PasteInput <PasteInput
testID="composerTextInput" testID="composerTextInput"
ref={textInput} ref={textInput}
@ -202,15 +203,20 @@ export const TextInput = React.forwardRef(
view={autocompleteView} view={autocompleteView}
onSelect={onSelectAutocompleteItem} onSelect={onSelectAutocompleteItem}
/> />
</> </View>
) )
}, },
) )
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: {
width: '100%',
},
textInput: { textInput: {
flex: 1, flex: 1,
minHeight: 80,
padding: 5, padding: 5,
paddingBottom: 20,
marginLeft: 8, marginLeft: 8,
alignSelf: 'flex-start', alignSelf: 'flex-start',
}, },

View File

@ -1,10 +1,5 @@
import React, {useEffect} from 'react' import React, {useEffect} from 'react'
import { import {Animated, TouchableOpacity, StyleSheet, View} from 'react-native'
Animated,
TouchableOpacity,
StyleSheet,
useWindowDimensions,
} from 'react-native'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import {UserAutocompleteViewModel} from 'state/models/user-autocomplete-view' import {UserAutocompleteViewModel} from 'state/models/user-autocomplete-view'
import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' import {useAnimatedValue} from 'lib/hooks/useAnimatedValue'
@ -20,26 +15,37 @@ export const Autocomplete = observer(
onSelect: (item: string) => void onSelect: (item: string) => void
}) => { }) => {
const pal = usePalette('default') const pal = usePalette('default')
const winDim = useWindowDimensions()
const positionInterp = useAnimatedValue(0) const positionInterp = useAnimatedValue(0)
useEffect(() => { useEffect(() => {
Animated.timing(positionInterp, { Animated.timing(positionInterp, {
toValue: view.isActive ? 1 : 0, toValue: view.isActive ? 1 : 0,
duration: 200, duration: 200,
useNativeDriver: false, useNativeDriver: true,
}).start() }).start()
}, [positionInterp, view.isActive]) }, [positionInterp, view.isActive])
const topAnimStyle = { const topAnimStyle = {
top: positionInterp.interpolate({ transform: [
{
translateY: positionInterp.interpolate({
inputRange: [0, 1], inputRange: [0, 1],
outputRange: [winDim.height, winDim.height / 4], outputRange: [200, 0],
}), }),
},
],
} }
return ( return (
<Animated.View style={[styles.outer, pal.view, pal.border, topAnimStyle]}> <View style={[styles.container, view.isActive && styles.visible]}>
{view.suggestions.map(item => ( <Animated.View
style={[
styles.animatedContainer,
pal.view,
pal.border,
topAnimStyle,
view.isActive && styles.visible,
]}>
{view.suggestions.slice(0, 5).map(item => (
<TouchableOpacity <TouchableOpacity
testID="autocompleteButton" testID="autocompleteButton"
key={item.handle} key={item.handle}
@ -54,18 +60,27 @@ export const Autocomplete = observer(
</TouchableOpacity> </TouchableOpacity>
))} ))}
</Animated.View> </Animated.View>
</View>
) )
}, },
) )
const styles = StyleSheet.create({ const styles = StyleSheet.create({
outer: { container: {
display: 'none',
height: 250,
},
animatedContainer: {
display: 'none',
position: 'absolute', position: 'absolute',
left: 0, left: -64,
right: 0, right: 0,
bottom: 0, top: 0,
borderTopWidth: 1, borderTopWidth: 1,
}, },
visible: {
display: 'flex',
},
item: { item: {
borderBottomWidth: 1, borderBottomWidth: 1,
paddingVertical: 16, paddingVertical: 16,