Focus the text input on tap during the composer

zio/stable
Paul Frazee 2023-01-18 13:21:23 -06:00
parent 302acaccb6
commit 726ff6bb01
1 changed files with 154 additions and 144 deletions

View File

@ -9,6 +9,7 @@ import {
StyleSheet, StyleSheet,
TextInput, TextInput,
TouchableOpacity, TouchableOpacity,
TouchableWithoutFeedback,
View, View,
} from 'react-native' } from 'react-native'
import LinearGradient from 'react-native-linear-gradient' import LinearGradient from 'react-native-linear-gradient'
@ -88,6 +89,9 @@ export const ComposePost = observer(function ComposePost({
} }
}, []) }, [])
const onPressContainer = () => {
textInput.current?.focus()
}
const onPressSelectPhotos = () => { const onPressSelectPhotos = () => {
if (isSelectingPhotos) { if (isSelectingPhotos) {
setIsSelectingPhotos(false) setIsSelectingPhotos(false)
@ -183,156 +187,162 @@ export const ComposePost = observer(function ComposePost({
testID="composePostView" testID="composePostView"
behavior={Platform.OS === 'ios' ? 'padding' : 'height'} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={[pal.view, styles.outer]}> style={[pal.view, styles.outer]}>
<SafeAreaView style={s.flex1}> <TouchableWithoutFeedback onPressIn={onPressContainer}>
<View style={styles.topbar}> <SafeAreaView style={s.flex1}>
<TouchableOpacity <View style={styles.topbar}>
testID="composerCancelButton"
onPress={onPressCancel}>
<Text style={[pal.link, s.f18]}>Cancel</Text>
</TouchableOpacity>
<View style={s.flex1} />
{isProcessing ? (
<View style={styles.postBtn}>
<ActivityIndicator />
</View>
) : canPost ? (
<TouchableOpacity <TouchableOpacity
testID="composerPublishButton" testID="composerCancelButton"
onPress={onPressPublish}> onPress={onPressCancel}>
<LinearGradient <Text style={[pal.link, s.f18]}>Cancel</Text>
colors={[gradients.blueLight.start, gradients.blueLight.end]}
start={{x: 0, y: 0}}
end={{x: 1, y: 1}}
style={styles.postBtn}>
<Text style={[s.white, s.f16, s.bold]}>
{replyTo ? 'Reply' : 'Post'}
</Text>
</LinearGradient>
</TouchableOpacity> </TouchableOpacity>
) : ( <View style={s.flex1} />
<View style={[styles.postBtn, pal.btn]}> {isProcessing ? (
<Text style={[pal.textLight, s.f16, s.bold]}>Post</Text> <View style={styles.postBtn}>
</View> <ActivityIndicator />
)}
</View>
{isProcessing ? (
<View style={[pal.btn, styles.processingLine]}>
<Text style={s.black}>{processingState}</Text>
</View>
) : undefined}
{error !== '' && (
<View style={styles.errorLine}>
<View style={styles.errorIcon}>
<FontAwesomeIcon
icon="exclamation"
style={{color: colors.red4}}
size={10}
/>
</View>
<Text style={[s.red4, s.flex1]}>{error}</Text>
</View>
)}
<ScrollView style={s.flex1}>
{replyTo ? (
<View style={[pal.border, styles.replyToLayout]}>
<UserAvatar
handle={replyTo.author.handle}
displayName={replyTo.author.displayName}
avatar={replyTo.author.avatar}
size={50}
/>
<View style={styles.replyToPost}>
<TextLink
type="xl-medium"
href={`/profile/${replyTo.author.handle}`}
text={replyTo.author.displayName || replyTo.author.handle}
style={[pal.text]}
/>
<Text type="post-text" style={pal.text} numberOfLines={6}>
{replyTo.text}
</Text>
</View> </View>
</View> ) : canPost ? (
) : undefined} <TouchableOpacity
<View testID="composerPublishButton"
style={[pal.border, styles.textInputLayout, selectTextInputLayout]}> onPress={onPressPublish}>
<UserAvatar <LinearGradient
handle={store.me.handle || ''} colors={[gradients.blueLight.start, gradients.blueLight.end]}
displayName={store.me.displayName} start={{x: 0, y: 0}}
avatar={store.me.avatar} end={{x: 1, y: 1}}
size={50} style={styles.postBtn}>
/> <Text style={[s.white, s.f16, s.bold]}>
<TextInput {replyTo ? 'Reply' : 'Post'}
testID="composerTextInput" </Text>
ref={textInput} </LinearGradient>
multiline </TouchableOpacity>
scrollEnabled
onChangeText={(text: string) => onChangeText(text)}
placeholder={selectTextInputPlaceholder}
placeholderTextColor={pal.colors.textLight}
style={[pal.text, styles.textInput]}>
{textDecorated}
</TextInput>
</View>
<SelectedPhoto
selectedPhotos={selectedPhotos}
onSelectPhotos={onSelectPhotos}
/>
</ScrollView>
{isSelectingPhotos &&
localPhotos.photos != null &&
selectedPhotos.length < 4 && (
<PhotoCarouselPicker
selectedPhotos={selectedPhotos}
onSelectPhotos={onSelectPhotos}
localPhotos={localPhotos}
/>
)}
<View style={[pal.border, styles.bottomBar]}>
<TouchableOpacity
testID="composerSelectPhotosButton"
onPress={onPressSelectPhotos}
style={[s.pl5]}
hitSlop={HITSLOP}>
<FontAwesomeIcon
icon={['far', 'image']}
style={selectedPhotos.length < 4 ? pal.link : pal.textLight}
size={24}
/>
</TouchableOpacity>
<View style={s.flex1} />
<Text style={[s.mr10, {color: progressColor}]}>
{MAX_TEXT_LENGTH - text.length}
</Text>
<View>
{text.length > DANGER_TEXT_LENGTH ? (
<ProgressPie
size={30}
borderWidth={4}
borderColor={progressColor}
color={progressColor}
progress={Math.min(
(text.length - MAX_TEXT_LENGTH) / MAX_TEXT_LENGTH,
1,
)}
/>
) : ( ) : (
<ProgressCircle <View style={[styles.postBtn, pal.btn]}>
size={30} <Text style={[pal.textLight, s.f16, s.bold]}>Post</Text>
borderWidth={1} </View>
borderColor={colors.gray2}
color={progressColor}
progress={text.length / MAX_TEXT_LENGTH}
/>
)} )}
</View> </View>
</View> {isProcessing ? (
<Autocomplete <View style={[pal.btn, styles.processingLine]}>
active={autocompleteView.isActive} <Text style={s.black}>{processingState}</Text>
items={autocompleteView.suggestions} </View>
onSelect={onSelectAutocompleteItem} ) : undefined}
/> {error !== '' && (
</SafeAreaView> <View style={styles.errorLine}>
<View style={styles.errorIcon}>
<FontAwesomeIcon
icon="exclamation"
style={{color: colors.red4}}
size={10}
/>
</View>
<Text style={[s.red4, s.flex1]}>{error}</Text>
</View>
)}
<ScrollView style={s.flex1}>
{replyTo ? (
<View style={[pal.border, styles.replyToLayout]}>
<UserAvatar
handle={replyTo.author.handle}
displayName={replyTo.author.displayName}
avatar={replyTo.author.avatar}
size={50}
/>
<View style={styles.replyToPost}>
<TextLink
type="xl-medium"
href={`/profile/${replyTo.author.handle}`}
text={replyTo.author.displayName || replyTo.author.handle}
style={[pal.text]}
/>
<Text type="post-text" style={pal.text} numberOfLines={6}>
{replyTo.text}
</Text>
</View>
</View>
) : undefined}
<View
style={[
pal.border,
styles.textInputLayout,
selectTextInputLayout,
]}>
<UserAvatar
handle={store.me.handle || ''}
displayName={store.me.displayName}
avatar={store.me.avatar}
size={50}
/>
<TextInput
testID="composerTextInput"
ref={textInput}
multiline
scrollEnabled
onChangeText={(text: string) => onChangeText(text)}
placeholder={selectTextInputPlaceholder}
placeholderTextColor={pal.colors.textLight}
style={[pal.text, styles.textInput]}>
{textDecorated}
</TextInput>
</View>
<SelectedPhoto
selectedPhotos={selectedPhotos}
onSelectPhotos={onSelectPhotos}
/>
</ScrollView>
{isSelectingPhotos &&
localPhotos.photos != null &&
selectedPhotos.length < 4 && (
<PhotoCarouselPicker
selectedPhotos={selectedPhotos}
onSelectPhotos={onSelectPhotos}
localPhotos={localPhotos}
/>
)}
<View style={[pal.border, styles.bottomBar]}>
<TouchableOpacity
testID="composerSelectPhotosButton"
onPress={onPressSelectPhotos}
style={[s.pl5]}
hitSlop={HITSLOP}>
<FontAwesomeIcon
icon={['far', 'image']}
style={selectedPhotos.length < 4 ? pal.link : pal.textLight}
size={24}
/>
</TouchableOpacity>
<View style={s.flex1} />
<Text style={[s.mr10, {color: progressColor}]}>
{MAX_TEXT_LENGTH - text.length}
</Text>
<View>
{text.length > DANGER_TEXT_LENGTH ? (
<ProgressPie
size={30}
borderWidth={4}
borderColor={progressColor}
color={progressColor}
progress={Math.min(
(text.length - MAX_TEXT_LENGTH) / MAX_TEXT_LENGTH,
1,
)}
/>
) : (
<ProgressCircle
size={30}
borderWidth={1}
borderColor={colors.gray2}
color={progressColor}
progress={text.length / MAX_TEXT_LENGTH}
/>
)}
</View>
</View>
<Autocomplete
active={autocompleteView.isActive}
items={autocompleteView.suggestions}
onSelect={onSelectAutocompleteItem}
/>
</SafeAreaView>
</TouchableWithoutFeedback>
</KeyboardAvoidingView> </KeyboardAvoidingView>
) )
}) })