diff options
Diffstat (limited to 'src/view/com/composer/text-input/mobile/Autocomplete.tsx')
-rw-r--r-- | src/view/com/composer/text-input/mobile/Autocomplete.tsx | 110 |
1 files changed, 69 insertions, 41 deletions
diff --git a/src/view/com/composer/text-input/mobile/Autocomplete.tsx b/src/view/com/composer/text-input/mobile/Autocomplete.tsx index 7806241f1..c9b8b84b1 100644 --- a/src/view/com/composer/text-input/mobile/Autocomplete.tsx +++ b/src/view/com/composer/text-input/mobile/Autocomplete.tsx @@ -5,6 +5,8 @@ import {UserAutocompleteModel} from 'state/models/discovery/user-autocomplete' import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' import {usePalette} from 'lib/hooks/usePalette' import {Text} from 'view/com/util/text/Text' +import {UserAvatar} from 'view/com/util/UserAvatar' +import {useGrapheme} from '../hooks/useGrapheme' export const Autocomplete = observer( ({ @@ -16,6 +18,7 @@ export const Autocomplete = observer( }) => { const pal = usePalette('default') const positionInterp = useAnimatedValue(0) + const {getGraphemeString} = useGrapheme() useEffect(() => { Animated.timing(positionInterp, { @@ -35,58 +38,83 @@ export const Autocomplete = observer( }, ], } + return ( - <View style={[styles.container, view.isActive && styles.visible]}> - <Animated.View - style={[ - styles.animatedContainer, - pal.view, - pal.border, - topAnimStyle, - view.isActive && styles.visible, - ]}> - {view.suggestions.slice(0, 5).map(item => ( - <TouchableOpacity - testID="autocompleteButton" - key={item.handle} - style={[pal.border, styles.item]} - onPress={() => onSelect(item.handle)} - accessibilityLabel={`Select ${item.handle}`} - accessibilityHint={`Autocompletes to ${item.handle}`}> - <Text type="md-medium" style={pal.text}> - {item.displayName || item.handle} - <Text type="sm" style={pal.textLight}> - @{item.handle} - </Text> + <Animated.View style={topAnimStyle}> + {view.isActive ? ( + <View style={[pal.view, styles.container, pal.border]}> + {view.suggestions.length > 0 ? ( + view.suggestions.slice(0, 5).map(item => { + // Eventually use an average length + const MAX_CHARS = 40 + const MAX_HANDLE_CHARS = 20 + + // Using this approach because styling is not respecting + // bounding box wrapping (before converting to ellipsis) + const {name: displayHandle, remainingCharacters} = + getGraphemeString(item.handle, MAX_HANDLE_CHARS) + + const {name: displayName} = getGraphemeString( + item.displayName ?? item.handle, + MAX_CHARS - + MAX_HANDLE_CHARS + + (remainingCharacters > 0 ? remainingCharacters : 0), + ) + + return ( + <TouchableOpacity + testID="autocompleteButton" + key={item.handle} + style={[pal.border, styles.item]} + onPress={() => onSelect(item.handle)} + accessibilityLabel={`Select ${item.handle}`} + accessibilityHint=""> + <View style={styles.avatarAndHandle}> + <UserAvatar avatar={item.avatar ?? null} size={24} /> + <Text type="md-medium" style={pal.text}> + {displayName} + </Text> + </View> + <Text type="sm" style={pal.textLight} numberOfLines={1}> + @{displayHandle} + </Text> + </TouchableOpacity> + ) + }) + ) : ( + <Text type="sm" style={[pal.text, pal.border, styles.noResults]}> + No result </Text> - </TouchableOpacity> - ))} - </Animated.View> - </View> + )} + </View> + ) : null} + </Animated.View> ) }, ) const styles = StyleSheet.create({ container: { - display: 'none', - height: 250, - }, - animatedContainer: { - display: 'none', - position: 'absolute', - left: -64, - right: 0, - top: 0, + marginLeft: -54, + top: 10, borderTopWidth: 1, }, - visible: { - display: 'flex', - }, item: { borderBottomWidth: 1, - paddingVertical: 16, - paddingHorizontal: 16, - height: 50, + paddingVertical: 12, + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + gap: 6, + }, + avatarAndHandle: { + display: 'flex', + flexDirection: 'row', + gap: 6, + alignItems: 'center', + }, + noResults: { + paddingVertical: 12, }, }) |