forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
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}