Bluesky app fork with some witchin' additions 馃挮
at linkat-integration 192 lines 6.2 kB view raw
1import {Pressable, ScrollView, View} from 'react-native' 2import {moderateProfile, type ModerationOpts} from '@atproto/api' 3import {msg, Trans} from '@lingui/macro' 4import {useLingui} from '@lingui/react' 5 6import {createHitslop, HITSLOP_10} from '#/lib/constants' 7import {makeProfileLink} from '#/lib/routes/links' 8import {sanitizeDisplayName} from '#/lib/strings/display-names' 9import {sanitizeHandle} from '#/lib/strings/handles' 10import {useEnableSquareButtons} from '#/state/preferences/enable-square-buttons' 11import {useModerationOpts} from '#/state/preferences/moderation-opts' 12import {UserAvatar} from '#/view/com/util/UserAvatar' 13import {BlockDrawerGesture} from '#/view/shell/BlockDrawerGesture' 14import {atoms as a} from '#/alf' 15import {Button, ButtonIcon} from '#/components/Button' 16import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times' 17import * as Layout from '#/components/Layout' 18import {Link} from '#/components/Link' 19import {Text} from '#/components/Typography' 20import {useSimpleVerificationState} from '#/components/verification' 21import {VerificationCheck} from '#/components/verification/VerificationCheck' 22import type * as bsky from '#/types/bsky' 23 24export function SearchHistory({ 25 searchHistory, 26 selectedProfiles, 27 onItemClick, 28 onProfileClick, 29 onRemoveItemClick, 30 onRemoveProfileClick, 31}: { 32 searchHistory: string[] 33 selectedProfiles: bsky.profile.AnyProfileView[] 34 onItemClick: (item: string) => void 35 onProfileClick: (profile: bsky.profile.AnyProfileView) => void 36 onRemoveItemClick: (item: string) => void 37 onRemoveProfileClick: (profile: bsky.profile.AnyProfileView) => void 38}) { 39 const {_} = useLingui() 40 const moderationOpts = useModerationOpts() 41 42 const enableSquareButtons = useEnableSquareButtons() 43 44 return ( 45 <Layout.Content 46 keyboardDismissMode="interactive" 47 keyboardShouldPersistTaps="handled"> 48 <View style={[a.w_full, a.gap_md]}> 49 {(searchHistory.length > 0 || selectedProfiles.length > 0) && ( 50 <View style={[a.px_lg, a.pt_sm]}> 51 <Text style={[a.text_md, a.font_semi_bold]}> 52 <Trans>Recent Searches</Trans> 53 </Text> 54 </View> 55 )} 56 57 {selectedProfiles.length > 0 && ( 58 <View> 59 <BlockDrawerGesture> 60 <ScrollView 61 horizontal 62 keyboardShouldPersistTaps="handled" 63 showsHorizontalScrollIndicator={false} 64 contentContainerStyle={[ 65 a.px_lg, 66 a.flex_row, 67 a.flex_nowrap, 68 a.gap_xl, 69 ]}> 70 {moderationOpts && 71 selectedProfiles.map(profile => ( 72 <RecentProfileItem 73 key={profile.did} 74 profile={profile} 75 moderationOpts={moderationOpts} 76 onPress={() => onProfileClick(profile)} 77 onRemove={() => onRemoveProfileClick(profile)} 78 /> 79 ))} 80 </ScrollView> 81 </BlockDrawerGesture> 82 </View> 83 )} 84 85 {searchHistory.length > 0 && ( 86 <View style={[a.px_lg, a.pt_sm]}> 87 {searchHistory.slice(0, 5).map((historyItem, index) => ( 88 <View key={index} style={[a.flex_row, a.align_center]}> 89 <Pressable 90 accessibilityRole="button" 91 onPress={() => onItemClick(historyItem)} 92 hitSlop={HITSLOP_10} 93 style={[a.flex_1, a.py_sm]}> 94 <Text style={[a.text_md]}>{historyItem}</Text> 95 </Pressable> 96 <Button 97 label={_(msg`Remove ${historyItem}`)} 98 onPress={() => onRemoveItemClick(historyItem)} 99 size="small" 100 variant="ghost" 101 color="secondary" 102 shape={enableSquareButtons ? 'square' : 'round'}> 103 <ButtonIcon icon={XIcon} /> 104 </Button> 105 </View> 106 ))} 107 </View> 108 )} 109 </View> 110 </Layout.Content> 111 ) 112} 113 114function RecentProfileItem({ 115 profile, 116 moderationOpts, 117 onPress, 118 onRemove, 119}: { 120 profile: bsky.profile.AnyProfileView 121 moderationOpts: ModerationOpts 122 onPress: () => void 123 onRemove: () => void 124}) { 125 const {_} = useLingui() 126 const width = 80 127 128 const moderation = moderateProfile(profile, moderationOpts) 129 const name = sanitizeDisplayName( 130 profile.displayName || sanitizeHandle(profile.handle), 131 moderation.ui('displayName'), 132 ) 133 const verification = useSimpleVerificationState({profile}) 134 135 const enableSquareButtons = useEnableSquareButtons() 136 137 return ( 138 <View style={[a.relative]}> 139 <Link 140 to={makeProfileLink(profile)} 141 label={profile.handle} 142 onPress={onPress} 143 style={[ 144 a.flex_col, 145 a.align_center, 146 a.gap_xs, 147 { 148 width, 149 }, 150 ]}> 151 <UserAvatar 152 avatar={profile.avatar} 153 type={profile.associated?.labeler ? 'labeler' : 'user'} 154 size={width - 8} 155 moderation={moderation.ui('avatar')} 156 /> 157 <View style={[a.flex_row, a.align_center, a.justify_center, a.w_full]}> 158 <Text emoji style={[a.text_xs, a.leading_snug]} numberOfLines={1}> 159 {name} 160 </Text> 161 {verification.showBadge && ( 162 <View style={[a.pl_2xs]}> 163 <VerificationCheck 164 width={10} 165 verifier={verification.role === 'verifier'} 166 /> 167 </View> 168 )} 169 </View> 170 </Link> 171 <Button 172 label={_(msg`Remove profile`)} 173 hitSlop={createHitslop(6)} 174 size="tiny" 175 variant="outline" 176 color="secondary" 177 shape={enableSquareButtons ? 'square' : 'round'} 178 onPress={onRemove} 179 style={[ 180 a.absolute, 181 { 182 top: 0, 183 right: 0, 184 height: 18, 185 width: 18, 186 }, 187 ]}> 188 <ButtonIcon icon={XIcon} /> 189 </Button> 190 </View> 191 ) 192}