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