about summary refs log tree commit diff
path: root/src/view/com/composer/text-input/mobile/Autocomplete.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/composer/text-input/mobile/Autocomplete.tsx')
-rw-r--r--src/view/com/composer/text-input/mobile/Autocomplete.tsx110
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}>
-                  &nbsp;@{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,
   },
 })