mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at schema-errors 269 lines 7.5 kB view raw
1import React from 'react' 2import {Pressable, ScrollView, StyleSheet, View} from 'react-native' 3import {AppBskyActorDefs, moderateProfile} from '@atproto/api' 4import { 5 FontAwesomeIcon, 6 FontAwesomeIconStyle, 7} from '@fortawesome/react-native-fontawesome' 8import {msg, Trans} from '@lingui/macro' 9import {useLingui} from '@lingui/react' 10 11import {useProfileShadow} from '#/state/cache/profile-shadow' 12import {useModerationOpts} from '#/state/preferences/moderation-opts' 13import {useProfileFollowMutationQueue} from '#/state/queries/profile' 14import {useSuggestedFollowsByActorQuery} from '#/state/queries/suggested-follows' 15import {useAnalytics} from 'lib/analytics/analytics' 16import {usePalette} from 'lib/hooks/usePalette' 17import {makeProfileLink} from 'lib/routes/links' 18import {sanitizeDisplayName} from 'lib/strings/display-names' 19import {sanitizeHandle} from 'lib/strings/handles' 20import {isWeb} from 'platform/detection' 21import {Button} from 'view/com/util/forms/Button' 22import {Link} from 'view/com/util/Link' 23import {Text} from 'view/com/util/text/Text' 24import {PreviewableUserAvatar} from 'view/com/util/UserAvatar' 25import * as Toast from '../util/Toast' 26 27const OUTER_PADDING = 10 28const INNER_PADDING = 14 29const TOTAL_HEIGHT = 250 30 31export function ProfileHeaderSuggestedFollows({ 32 actorDid, 33 requestDismiss, 34}: { 35 actorDid: string 36 requestDismiss: () => void 37}) { 38 const pal = usePalette('default') 39 const {isLoading, data} = useSuggestedFollowsByActorQuery({ 40 did: actorDid, 41 }) 42 return ( 43 <View 44 style={{paddingVertical: OUTER_PADDING, height: TOTAL_HEIGHT}} 45 pointerEvents="box-none"> 46 <View 47 pointerEvents="box-none" 48 style={{ 49 backgroundColor: pal.viewLight.backgroundColor, 50 height: '100%', 51 paddingTop: INNER_PADDING / 2, 52 }}> 53 <View 54 pointerEvents="box-none" 55 style={{ 56 flexDirection: 'row', 57 justifyContent: 'space-between', 58 alignItems: 'center', 59 paddingTop: 4, 60 paddingBottom: INNER_PADDING / 2, 61 paddingLeft: INNER_PADDING, 62 paddingRight: INNER_PADDING / 2, 63 }}> 64 <Text type="sm-bold" style={[pal.textLight]}> 65 <Trans>Suggested for you</Trans> 66 </Text> 67 68 <Pressable 69 accessibilityRole="button" 70 onPress={requestDismiss} 71 hitSlop={10} 72 style={{padding: INNER_PADDING / 2}}> 73 <FontAwesomeIcon 74 icon="x" 75 size={12} 76 style={pal.textLight as FontAwesomeIconStyle} 77 /> 78 </Pressable> 79 </View> 80 81 <ScrollView 82 horizontal={true} 83 showsHorizontalScrollIndicator={isWeb} 84 persistentScrollbar={true} 85 scrollIndicatorInsets={{bottom: 0}} 86 scrollEnabled={true} 87 contentContainerStyle={{ 88 alignItems: 'flex-start', 89 paddingLeft: INNER_PADDING / 2, 90 paddingBottom: INNER_PADDING, 91 }}> 92 {isLoading ? ( 93 <> 94 <SuggestedFollowSkeleton /> 95 <SuggestedFollowSkeleton /> 96 <SuggestedFollowSkeleton /> 97 <SuggestedFollowSkeleton /> 98 <SuggestedFollowSkeleton /> 99 <SuggestedFollowSkeleton /> 100 </> 101 ) : data ? ( 102 data.suggestions 103 .filter(s => (s.associated?.labeler ? false : true)) 104 .map(profile => ( 105 <SuggestedFollow key={profile.did} profile={profile} /> 106 )) 107 ) : ( 108 <View /> 109 )} 110 </ScrollView> 111 </View> 112 </View> 113 ) 114} 115 116function SuggestedFollowSkeleton() { 117 const pal = usePalette('default') 118 return ( 119 <View 120 style={[ 121 styles.suggestedFollowCardOuter, 122 { 123 backgroundColor: pal.view.backgroundColor, 124 }, 125 ]}> 126 <View 127 style={{ 128 height: 60, 129 width: 60, 130 borderRadius: 60, 131 backgroundColor: pal.viewLight.backgroundColor, 132 opacity: 0.6, 133 }} 134 /> 135 <View 136 style={{ 137 height: 17, 138 width: 70, 139 borderRadius: 4, 140 backgroundColor: pal.viewLight.backgroundColor, 141 marginTop: 12, 142 marginBottom: 4, 143 }} 144 /> 145 <View 146 style={{ 147 height: 12, 148 width: 70, 149 borderRadius: 4, 150 backgroundColor: pal.viewLight.backgroundColor, 151 marginBottom: 12, 152 opacity: 0.6, 153 }} 154 /> 155 <View 156 style={{ 157 height: 32, 158 borderRadius: 32, 159 width: '100%', 160 backgroundColor: pal.viewLight.backgroundColor, 161 }} 162 /> 163 </View> 164 ) 165} 166 167function SuggestedFollow({ 168 profile: profileUnshadowed, 169}: { 170 profile: AppBskyActorDefs.ProfileView 171}) { 172 const {track} = useAnalytics() 173 const pal = usePalette('default') 174 const {_} = useLingui() 175 const moderationOpts = useModerationOpts() 176 const profile = useProfileShadow(profileUnshadowed) 177 const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue( 178 profile, 179 'ProfileHeaderSuggestedFollows', 180 ) 181 182 const onPressFollow = React.useCallback(async () => { 183 try { 184 track('ProfileHeader:SuggestedFollowFollowed') 185 await queueFollow() 186 } catch (e: any) { 187 if (e?.name !== 'AbortError') { 188 Toast.show(_(msg`An issue occurred, please try again.`)) 189 } 190 } 191 }, [queueFollow, track, _]) 192 193 const onPressUnfollow = React.useCallback(async () => { 194 try { 195 await queueUnfollow() 196 } catch (e: any) { 197 if (e?.name !== 'AbortError') { 198 Toast.show(_(msg`An issue occurred, please try again.`)) 199 } 200 } 201 }, [queueUnfollow, _]) 202 203 if (!moderationOpts) { 204 return null 205 } 206 const moderation = moderateProfile(profile, moderationOpts) 207 const following = profile.viewer?.following 208 return ( 209 <Link 210 href={makeProfileLink(profile)} 211 title={profile.handle} 212 asAnchor 213 anchorNoUnderline> 214 <View 215 style={[ 216 styles.suggestedFollowCardOuter, 217 { 218 backgroundColor: pal.view.backgroundColor, 219 }, 220 ]}> 221 <PreviewableUserAvatar 222 size={60} 223 profile={profile} 224 avatar={profile.avatar} 225 moderation={moderation.ui('avatar')} 226 /> 227 228 <View style={{width: '100%', paddingVertical: 12}}> 229 <Text 230 type="xs-medium" 231 style={[pal.text, {textAlign: 'center'}]} 232 numberOfLines={1}> 233 {sanitizeDisplayName( 234 profile.displayName || sanitizeHandle(profile.handle), 235 moderation.ui('displayName'), 236 )} 237 </Text> 238 <Text 239 type="xs-medium" 240 style={[pal.textLight, {textAlign: 'center'}]} 241 numberOfLines={1}> 242 {sanitizeHandle(profile.handle, '@')} 243 </Text> 244 </View> 245 246 <Button 247 label={following ? _(msg`Unfollow`) : _(msg`Follow`)} 248 type="inverted" 249 labelStyle={{textAlign: 'center'}} 250 onPress={following ? onPressUnfollow : onPressFollow} 251 /> 252 </View> 253 </Link> 254 ) 255} 256 257const styles = StyleSheet.create({ 258 suggestedFollowCardOuter: { 259 marginHorizontal: INNER_PADDING / 2, 260 paddingTop: 10, 261 paddingBottom: 12, 262 paddingHorizontal: 10, 263 borderRadius: 8, 264 width: 130, 265 alignItems: 'center', 266 overflow: 'hidden', 267 flexShrink: 1, 268 }, 269})