this repo has no description
1import {View} from 'react-native'
2import Animated, {FadeInDown, FadeOut} from 'react-native-reanimated'
3import {type AppBskyActorDefs} from '@atproto/api'
4import {Trans} from '@lingui/react/macro'
5
6import {PressableScale} from '#/lib/custom-animations/PressableScale'
7import {sanitizeDisplayName} from '#/lib/strings/display-names'
8import {sanitizeHandle} from '#/lib/strings/handles'
9import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete'
10import {UserAvatar} from '#/view/com/util/UserAvatar'
11import {atoms as a, platform, useTheme} from '#/alf'
12import {ProfileBadges} from '#/components/ProfileBadges'
13import {Text} from '#/components/Typography'
14
15export function Autocomplete({
16 prefix,
17 onSelect,
18}: {
19 prefix: string
20 onSelect: (item: string) => void
21}) {
22 const t = useTheme()
23
24 const isActive = !!prefix
25 const {data: suggestions, isFetching} = useActorAutocompleteQuery(
26 prefix,
27 true,
28 )
29
30 if (!isActive) return null
31
32 return (
33 <Animated.View
34 entering={FadeInDown.duration(200)}
35 exiting={FadeOut.duration(100)}
36 style={[
37 t.atoms.bg,
38 a.mt_sm,
39 a.border,
40 a.rounded_sm,
41 t.atoms.border_contrast_high,
42 {marginLeft: -62},
43 ]}>
44 {suggestions?.length ? (
45 suggestions.slice(0, 5).map((item, index, arr) => {
46 return (
47 <AutocompleteProfileCard
48 key={item.did}
49 profile={item}
50 itemIndex={index}
51 totalItems={arr.length}
52 onPress={() => {
53 onSelect(item.handle)
54 }}
55 />
56 )
57 })
58 ) : (
59 <Text style={[a.text_md, a.px_sm, a.py_md]}>
60 {isFetching ? <Trans>Loading...</Trans> : <Trans>No result</Trans>}
61 </Text>
62 )}
63 </Animated.View>
64 )
65}
66
67function AutocompleteProfileCard({
68 profile,
69 itemIndex,
70 totalItems,
71 onPress,
72}: {
73 profile: AppBskyActorDefs.ProfileViewBasic
74 itemIndex: number
75 totalItems: number
76 onPress: () => void
77}) {
78 const t = useTheme()
79 const displayName = sanitizeDisplayName(
80 profile.displayName || sanitizeHandle(profile.handle),
81 )
82 return (
83 <View
84 style={[
85 itemIndex !== totalItems - 1 && a.border_b,
86 t.atoms.border_contrast_high,
87 a.px_sm,
88 a.py_md,
89 ]}
90 key={profile.did}>
91 <PressableScale
92 testID="autocompleteButton"
93 style={[a.flex_row, a.gap_lg, a.justify_between, a.align_center]}
94 onPress={onPress}
95 accessibilityLabel={`Select ${profile.handle}`}
96 accessibilityHint="">
97 <View style={[a.flex_row, a.gap_sm, a.align_center, a.flex_1]}>
98 <UserAvatar
99 avatar={profile.avatar ?? null}
100 size={24}
101 type={profile.associated?.labeler ? 'labeler' : 'user'}
102 />
103 <View
104 style={[
105 a.flex_row,
106 a.align_center,
107 a.gap_xs,
108 platform({ios: a.flex_1}),
109 ]}>
110 <Text
111 style={[a.text_md, a.font_semi_bold, a.leading_snug]}
112 emoji
113 numberOfLines={1}>
114 {displayName}
115 </Text>
116 <ProfileBadges
117 profile={profile}
118 size="sm"
119 style={[
120 {
121 marginTop: platform({android: -2}),
122 },
123 ]}
124 />
125 </View>
126 </View>
127 <Text
128 style={[t.atoms.text_contrast_medium, a.text_right, a.leading_snug]}
129 numberOfLines={1}>
130 {sanitizeHandle(profile.handle, '@')}
131 </Text>
132 </PressableScale>
133 </View>
134 )
135}