Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

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

Suggested follows by actor (on profiles) updates (#5243)

* If fallback, return nothing

* Compress size a bit

* Hide on own profile

* Match load state

* Remove gcTime

* Filter out followed users

* Feedback

authored by

Eric Bailey and committed by
GitHub
d60a8f26 47bea320

+61 -49
+34 -30
src/components/FeedInterstitials.tsx
··· 60 60 export function SuggestedFollowPlaceholder() { 61 61 const t = useTheme() 62 62 return ( 63 - <CardOuter style={[a.gap_sm, t.atoms.border_contrast_low]}> 63 + <CardOuter style={[a.gap_md, t.atoms.border_contrast_low]}> 64 64 <ProfileCard.Header> 65 65 <ProfileCard.AvatarPlaceholder /> 66 + <ProfileCard.NameAndHandlePlaceholder /> 66 67 </ProfileCard.Header> 67 68 68 - <View style={[a.py_xs]}> 69 - <ProfileCard.NameAndHandlePlaceholder /> 70 - </View> 71 - 72 - <ProfileCard.DescriptionPlaceholder /> 69 + <ProfileCard.DescriptionPlaceholder numberOfLines={2} /> 73 70 </CardOuter> 74 71 ) 75 72 } ··· 176 173 } 177 174 178 175 export function SuggestedFollows({feed}: {feed: FeedDescriptor}) { 179 - const [feedType, feedUri] = feed.split('|') 176 + const {currentAccount} = useSession() 177 + const [feedType, feedUriOrDid] = feed.split('|') 180 178 if (feedType === 'author') { 181 - return <SuggestedFollowsProfile did={feedUri} /> 179 + if (currentAccount?.did === feedUriOrDid) { 180 + return null 181 + } else { 182 + return <SuggestedFollowsProfile did={feedUriOrDid} /> 183 + } 182 184 } else { 183 185 return <SuggestedFollowsHome /> 184 186 } ··· 197 199 isSuggestionsLoading={isSuggestionsLoading} 198 200 profiles={data?.suggestions ?? []} 199 201 error={error} 202 + viewContext="profile" 200 203 /> 201 204 ) 202 205 } ··· 212 215 isSuggestionsLoading={isSuggestionsLoading} 213 216 profiles={profiles} 214 217 error={error} 218 + viewContext="feed" 215 219 /> 216 220 ) 217 221 } ··· 220 224 isSuggestionsLoading, 221 225 error, 222 226 profiles, 227 + viewContext = 'feed', 223 228 }: { 224 229 isSuggestionsLoading: boolean 225 230 profiles: AppBskyActorDefs.ProfileViewDetailed[] 226 231 error: Error | null 232 + viewContext: 'profile' | 'feed' 227 233 }) { 228 234 const t = useTheme() 229 235 const {_} = useLingui() ··· 280 286 shape="round" 281 287 /> 282 288 </ProfileCard.Header> 283 - <ProfileCard.Description profile={profile} /> 289 + <ProfileCard.Description profile={profile} numberOfLines={2} /> 284 290 </ProfileCard.Outer> 285 291 </CardOuter> 286 292 )} ··· 297 303 return ( 298 304 <View 299 305 style={[a.border_t, t.atoms.border_contrast_low, t.atoms.bg_contrast_25]}> 300 - <View style={[a.pt_2xl, a.px_lg, a.flex_row, a.pb_xs]}> 301 - <Text 302 - style={[ 303 - a.flex_1, 304 - a.text_lg, 305 - a.font_bold, 306 - t.atoms.text_contrast_medium, 307 - ]}> 308 - <Trans>Suggested for you</Trans> 306 + <View 307 + style={[ 308 + a.p_lg, 309 + a.pb_xs, 310 + a.flex_row, 311 + a.align_center, 312 + a.justify_between, 313 + ]}> 314 + <Text style={[a.text_sm, a.font_bold, t.atoms.text_contrast_medium]}> 315 + {viewContext === 'profile' ? ( 316 + <Trans>Similar accounts</Trans> 317 + ) : ( 318 + <Trans>Suggested for you</Trans> 319 + )} 309 320 </Text> 310 - <Person fill={t.atoms.text_contrast_low.color} /> 321 + <Person fill={t.atoms.text_contrast_low.color} size="sm" /> 311 322 </View> 312 323 313 324 {gtMobile ? ( 314 - <View style={[a.flex_1, a.px_lg, a.pt_md, a.pb_xl, a.gap_md]}> 315 - <View style={[a.flex_1, a.flex_row, a.flex_wrap, a.gap_md]}> 325 + <View style={[a.flex_1, a.px_lg, a.pt_sm, a.pb_lg, a.gap_md]}> 326 + <View style={[a.flex_1, a.flex_row, a.flex_wrap, a.gap_sm]}> 316 327 {content} 317 328 </View> 318 329 319 - <View 320 - style={[ 321 - a.flex_row, 322 - a.justify_end, 323 - a.align_center, 324 - a.pt_xs, 325 - a.gap_md, 326 - ]}> 330 + <View style={[a.flex_row, a.justify_end, a.align_center, a.gap_md]}> 327 331 <InlineLinkText 328 332 label={_(msg`Browse more suggestions`)} 329 333 to="/search" ··· 339 343 showsHorizontalScrollIndicator={false} 340 344 snapToInterval={MOBILE_CARD_WIDTH + a.gap_md.gap} 341 345 decelerationRate="fast"> 342 - <View style={[a.px_lg, a.pt_md, a.pb_xl, a.flex_row, a.gap_md]}> 346 + <View style={[a.px_lg, a.pt_sm, a.pb_lg, a.flex_row, a.gap_md]}> 343 347 {content} 344 348 345 349 <Button
+22 -17
src/components/ProfileCard.tsx
··· 220 220 221 221 export function Description({ 222 222 profile: profileUnshadowed, 223 + numberOfLines = 3, 223 224 }: { 224 225 profile: AppBskyActorDefs.ProfileViewDetailed 226 + numberOfLines?: number 225 227 }) { 226 228 const profile = useProfileShadow(profileUnshadowed) 227 229 const {description} = profile ··· 244 246 <RichText 245 247 value={rt} 246 248 style={[a.leading_snug]} 247 - numberOfLines={3} 249 + numberOfLines={numberOfLines} 248 250 disableLinks 249 251 /> 250 252 </View> 251 253 ) 252 254 } 253 255 254 - export function DescriptionPlaceholder() { 256 + export function DescriptionPlaceholder({ 257 + numberOfLines = 3, 258 + }: { 259 + numberOfLines?: number 260 + }) { 255 261 const t = useTheme() 256 262 return ( 257 - <View style={[a.gap_xs]}> 258 - <View 259 - style={[a.rounded_xs, a.w_full, t.atoms.bg_contrast_50, {height: 12}]} 260 - /> 261 - <View 262 - style={[a.rounded_xs, a.w_full, t.atoms.bg_contrast_50, {height: 12}]} 263 - /> 264 - <View 265 - style={[ 266 - a.rounded_xs, 267 - a.w_full, 268 - t.atoms.bg_contrast_50, 269 - {height: 12, width: 100}, 270 - ]} 271 - /> 263 + <View style={[{gap: 8}]}> 264 + {Array(numberOfLines) 265 + .fill(0) 266 + .map((_, i) => ( 267 + <View 268 + key={i} 269 + style={[ 270 + a.rounded_xs, 271 + a.w_full, 272 + t.atoms.bg_contrast_50, 273 + {height: 12, width: i + 1 === numberOfLines ? '60%' : '100%'}, 274 + ]} 275 + /> 276 + ))} 272 277 </View> 273 278 ) 274 279 }
+5 -2
src/state/queries/suggested-follows.ts
··· 106 106 export function useSuggestedFollowsByActorQuery({did}: {did: string}) { 107 107 const agent = useAgent() 108 108 return useQuery<AppBskyGraphGetSuggestedFollowsByActor.OutputSchema, Error>({ 109 - gcTime: 0, 110 109 queryKey: suggestedFollowsByActorQueryKey(did), 111 110 queryFn: async () => { 112 111 const res = await agent.app.bsky.graph.getSuggestedFollowsByActor({ 113 112 actor: did, 114 113 }) 115 - return res.data 114 + const data = res.data.isFallback ? {suggestions: []} : res.data 115 + data.suggestions = data.suggestions.filter(profile => { 116 + return !profile.viewer?.following 117 + }) 118 + return data 116 119 }, 117 120 }) 118 121 }