* Improve layout in composer to ensure the mentions autocomplete is visible (closes #326) * Dont dismiss the keyboard in the composerzio/stable
parent
df6a712834
commit
8a3601c07c
|
@ -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,
|
||||||
},
|
},
|
||||||
|
|
|
@ -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',
|
||||||
},
|
},
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue