Bluesky app fork with some witchin' additions 💫

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

authored by

Paul Frazee and committed by
GitHub
8a3601c0 df6a7128

+67 -50
+14 -18
src/view/com/composer/Composer.tsx
··· 32 32 import {usePalette} from 'lib/hooks/usePalette' 33 33 import QuoteEmbed from '../util/PostEmbeds/QuoteEmbed' 34 34 import {useExternalLinkFetch} from './useExternalLinkFetch' 35 + import {isDesktopWeb} from 'platform/detection' 35 36 36 37 const MAX_TEXT_LENGTH = 256 37 38 ··· 188 189 189 190 const canPost = text.length <= MAX_TEXT_LENGTH 190 191 191 - const selectTextInputLayout = 192 - selectedPhotos.length !== 0 193 - ? styles.textInputLayoutWithPhoto 194 - : styles.textInputLayoutWithoutPhoto 195 192 const selectTextInputPlaceholder = replyTo 196 193 ? 'Write your reply' 197 194 : selectedPhotos.length !== 0 ··· 253 250 <Text style={[s.red4, s.flex1]}>{error}</Text> 254 251 </View> 255 252 )} 256 - <ScrollView style={s.flex1}> 253 + <ScrollView 254 + style={styles.scrollView} 255 + keyboardShouldPersistTaps="always"> 257 256 {replyTo ? ( 258 257 <View style={[pal.border, styles.replyToLayout]}> 259 258 <UserAvatar avatar={replyTo.author.avatar} size={50} /> ··· 268 267 </View> 269 268 ) : undefined} 270 269 271 - <View 272 - style={[ 273 - pal.border, 274 - styles.textInputLayout, 275 - selectTextInputLayout, 276 - ]}> 270 + <View style={[pal.border, styles.textInputLayout]}> 277 271 <UserAvatar avatar={store.me.avatar} size={50} /> 278 272 <TextInput 279 273 ref={textInput} ··· 346 340 outer: { 347 341 flexDirection: 'column', 348 342 flex: 1, 349 - padding: 15, 350 343 height: '100%', 351 344 }, 352 345 topbar: { 353 346 flexDirection: 'row', 354 347 alignItems: 'center', 348 + paddingTop: isDesktopWeb ? 10 : undefined, 355 349 paddingBottom: 10, 356 - paddingHorizontal: 5, 350 + paddingHorizontal: 20, 357 351 height: 55, 358 352 }, 359 353 postBtn: { ··· 365 359 borderRadius: 6, 366 360 paddingHorizontal: 8, 367 361 paddingVertical: 6, 362 + marginHorizontal: 15, 368 363 marginBottom: 6, 369 364 }, 370 365 errorLine: { 371 366 flexDirection: 'row', 372 367 backgroundColor: colors.red1, 373 368 borderRadius: 6, 369 + marginHorizontal: 15, 374 370 paddingHorizontal: 8, 375 371 paddingVertical: 6, 376 372 marginVertical: 6, ··· 386 382 justifyContent: 'center', 387 383 marginRight: 5, 388 384 }, 389 - textInputLayoutWithPhoto: { 390 - flexWrap: 'wrap', 391 - }, 392 - textInputLayoutWithoutPhoto: { 385 + scrollView: { 393 386 flex: 1, 387 + paddingHorizontal: 15, 394 388 }, 395 389 textInputLayout: { 390 + flex: isDesktopWeb ? undefined : 1, 396 391 flexDirection: 'row', 397 392 borderTopWidth: 1, 398 393 paddingTop: 16, ··· 418 413 bottomBar: { 419 414 flexDirection: 'row', 420 415 paddingVertical: 10, 421 - paddingRight: 5, 416 + paddingLeft: 15, 417 + paddingRight: 20, 422 418 alignItems: 'center', 423 419 borderTopWidth: 1, 424 420 },
+8 -2
src/view/com/composer/text-input/TextInput.tsx
··· 3 3 NativeSyntheticEvent, 4 4 StyleSheet, 5 5 TextInputSelectionChangeEventData, 6 + View, 6 7 } from 'react-native' 7 8 import PasteInput, { 8 9 PastedFile, ··· 185 186 }, [text, pal.link, pal.text]) 186 187 187 188 return ( 188 - <> 189 + <View style={styles.container}> 189 190 <PasteInput 190 191 testID="composerTextInput" 191 192 ref={textInput} ··· 202 203 view={autocompleteView} 203 204 onSelect={onSelectAutocompleteItem} 204 205 /> 205 - </> 206 + </View> 206 207 ) 207 208 }, 208 209 ) 209 210 210 211 const styles = StyleSheet.create({ 212 + container: { 213 + width: '100%', 214 + }, 211 215 textInput: { 212 216 flex: 1, 217 + minHeight: 80, 213 218 padding: 5, 219 + paddingBottom: 20, 214 220 marginLeft: 8, 215 221 alignSelf: 'flex-start', 216 222 },
+45 -30
src/view/com/composer/text-input/mobile/Autocomplete.tsx
··· 1 1 import React, {useEffect} from 'react' 2 - import { 3 - Animated, 4 - TouchableOpacity, 5 - StyleSheet, 6 - useWindowDimensions, 7 - } from 'react-native' 2 + import {Animated, TouchableOpacity, StyleSheet, View} from 'react-native' 8 3 import {observer} from 'mobx-react-lite' 9 4 import {UserAutocompleteViewModel} from 'state/models/user-autocomplete-view' 10 5 import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' ··· 20 15 onSelect: (item: string) => void 21 16 }) => { 22 17 const pal = usePalette('default') 23 - const winDim = useWindowDimensions() 24 18 const positionInterp = useAnimatedValue(0) 25 19 26 20 useEffect(() => { 27 21 Animated.timing(positionInterp, { 28 22 toValue: view.isActive ? 1 : 0, 29 23 duration: 200, 30 - useNativeDriver: false, 24 + useNativeDriver: true, 31 25 }).start() 32 26 }, [positionInterp, view.isActive]) 33 27 34 28 const topAnimStyle = { 35 - top: positionInterp.interpolate({ 36 - inputRange: [0, 1], 37 - outputRange: [winDim.height, winDim.height / 4], 38 - }), 29 + transform: [ 30 + { 31 + translateY: positionInterp.interpolate({ 32 + inputRange: [0, 1], 33 + outputRange: [200, 0], 34 + }), 35 + }, 36 + ], 39 37 } 40 38 return ( 41 - <Animated.View style={[styles.outer, pal.view, pal.border, topAnimStyle]}> 42 - {view.suggestions.map(item => ( 43 - <TouchableOpacity 44 - testID="autocompleteButton" 45 - key={item.handle} 46 - style={[pal.border, styles.item]} 47 - onPress={() => onSelect(item.handle)}> 48 - <Text type="md-medium" style={pal.text}> 49 - {item.displayName || item.handle} 50 - <Text type="sm" style={pal.textLight}> 51 - &nbsp;@{item.handle} 39 + <View style={[styles.container, view.isActive && styles.visible]}> 40 + <Animated.View 41 + style={[ 42 + styles.animatedContainer, 43 + pal.view, 44 + pal.border, 45 + topAnimStyle, 46 + view.isActive && styles.visible, 47 + ]}> 48 + {view.suggestions.slice(0, 5).map(item => ( 49 + <TouchableOpacity 50 + testID="autocompleteButton" 51 + key={item.handle} 52 + style={[pal.border, styles.item]} 53 + onPress={() => onSelect(item.handle)}> 54 + <Text type="md-medium" style={pal.text}> 55 + {item.displayName || item.handle} 56 + <Text type="sm" style={pal.textLight}> 57 + &nbsp;@{item.handle} 58 + </Text> 52 59 </Text> 53 - </Text> 54 - </TouchableOpacity> 55 - ))} 56 - </Animated.View> 60 + </TouchableOpacity> 61 + ))} 62 + </Animated.View> 63 + </View> 57 64 ) 58 65 }, 59 66 ) 60 67 61 68 const styles = StyleSheet.create({ 62 - outer: { 69 + container: { 70 + display: 'none', 71 + height: 250, 72 + }, 73 + animatedContainer: { 74 + display: 'none', 63 75 position: 'absolute', 64 - left: 0, 76 + left: -64, 65 77 right: 0, 66 - bottom: 0, 78 + top: 0, 67 79 borderTopWidth: 1, 80 + }, 81 + visible: { 82 + display: 'flex', 68 83 }, 69 84 item: { 70 85 borderBottomWidth: 1,