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