Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

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

Fix sloppy filter(Boolean) types (#4830)

* Fix sloppy filter(Boolean) in threadgate

* Fix sloppy filter(Boolean) in Explore

* Fix sloppy filter(Boolean) in post-feed

* Harden FeedPostSliceItem.reason type def

* Harden parentAuthor types

* Fix lying component types, handle blocks

authored by

dan and committed by
GitHub
4291711f fac1af43

+88 -56
+21 -8
src/state/queries/post-feed.ts
··· 78 78 uri: string 79 79 post: AppBskyFeedDefs.PostView 80 80 record: AppBskyFeedPost.Record 81 - reason?: AppBskyFeedDefs.ReasonRepost | ReasonFeedSource 81 + reason?: 82 + | AppBskyFeedDefs.ReasonRepost 83 + | ReasonFeedSource 84 + | {[k: string]: unknown; $type: string} 82 85 feedContext: string | undefined 83 86 moderation: ModerationDecision 84 87 parentAuthor?: AppBskyActorDefs.ProfileViewBasic ··· 323 326 ) 324 327 } 325 328 326 - return { 329 + const feedPostSlice: FeedPostSlice = { 327 330 _reactKey: slice._reactKey, 328 331 _isFeedPostSlice: true, 329 332 rootUri: slice.rootItem.post.uri, ··· 341 344 AppBskyFeedPost.validateRecord(item.post.record) 342 345 .success 343 346 ) { 344 - const parentAuthor = 345 - item.reply?.parent?.author ?? 346 - slice.items[i + 1]?.reply?.grandparentAuthor 347 + const parent = item.reply?.parent 348 + let parentAuthor: 349 + | AppBskyActorDefs.ProfileViewBasic 350 + | undefined 351 + if (AppBskyFeedDefs.isPostView(parent)) { 352 + parentAuthor = parent.author 353 + } 354 + if (!parentAuthor) { 355 + parentAuthor = 356 + slice.items[i + 1]?.reply?.grandparentAuthor 357 + } 347 358 const replyRef = item.reply 348 359 const isParentBlocked = AppBskyFeedDefs.isBlockedPost( 349 360 replyRef?.parent, 350 361 ) 351 362 352 - return { 363 + const feedPostSliceItem: FeedPostSliceItem = { 353 364 _reactKey: `${slice._reactKey}-${i}-${item.post.uri}`, 354 365 uri: item.post.uri, 355 366 post: item.post, ··· 363 374 parentAuthor, 364 375 isParentBlocked, 365 376 } 377 + return feedPostSliceItem 366 378 } 367 379 return undefined 368 380 }) 369 - .filter(Boolean) as FeedPostSliceItem[], 381 + .filter(<T>(n?: T): n is T => Boolean(n)), 370 382 } 383 + return feedPostSlice 371 384 }) 372 - .filter(Boolean) as FeedPostSlice[], 385 + .filter(<T>(n?: T): n is T => Boolean(n)), 373 386 })), 374 387 ], 375 388 }
+11 -11
src/state/queries/threadgate.ts
··· 4 4 | {type: 'nobody'} 5 5 | {type: 'mention'} 6 6 | {type: 'following'} 7 - | {type: 'list'; list: string} 7 + | {type: 'list'; list: unknown} 8 8 9 9 export function threadgateViewToSettings( 10 10 threadgate: AppBskyFeedDefs.ThreadgateView | undefined, ··· 21 21 if (!record.allow?.length) { 22 22 return [{type: 'nobody'}] 23 23 } 24 - return record.allow 24 + const settings: ThreadgateSetting[] = record.allow 25 25 .map(allow => { 26 + let setting: ThreadgateSetting | undefined 26 27 if (allow.$type === 'app.bsky.feed.threadgate#mentionRule') { 27 - return {type: 'mention'} 28 - } 29 - if (allow.$type === 'app.bsky.feed.threadgate#followingRule') { 30 - return {type: 'following'} 31 - } 32 - if (allow.$type === 'app.bsky.feed.threadgate#listRule') { 33 - return {type: 'list', list: allow.list} 28 + setting = {type: 'mention'} 29 + } else if (allow.$type === 'app.bsky.feed.threadgate#followingRule') { 30 + setting = {type: 'following'} 31 + } else if (allow.$type === 'app.bsky.feed.threadgate#listRule') { 32 + setting = {type: 'list', list: allow.list} 34 33 } 35 - return undefined 34 + return setting 36 35 }) 37 - .filter(Boolean) as ThreadgateSetting[] 36 + .filter(<T>(n?: T): n is T => Boolean(n)) 37 + return settings 38 38 }
+47 -29
src/view/com/posts/FeedItem.tsx
··· 48 48 49 49 interface FeedItemProps { 50 50 record: AppBskyFeedPost.Record 51 - reason: AppBskyFeedDefs.ReasonRepost | ReasonFeedSource | undefined 51 + reason: 52 + | AppBskyFeedDefs.ReasonRepost 53 + | ReasonFeedSource 54 + | {[k: string]: unknown; $type: string} 55 + | undefined 52 56 moderation: ModerationDecision 53 57 parentAuthor: AppBskyActorDefs.ProfileViewBasic | undefined 54 58 showReplyTo: boolean ··· 337 341 postHref={href} 338 342 onOpenAuthor={onOpenAuthor} 339 343 /> 340 - {!isThreadChild && showReplyTo && parentAuthor && ( 341 - <ReplyToLabel blocked={isParentBlocked} profile={parentAuthor} /> 342 - )} 344 + {!isThreadChild && 345 + showReplyTo && 346 + (parentAuthor || isParentBlocked) && ( 347 + <ReplyToLabel blocked={isParentBlocked} profile={parentAuthor} /> 348 + )} 343 349 <LabelsOnMyPost post={post} /> 344 350 <PostContent 345 351 moderation={moderation} ··· 431 437 profile, 432 438 blocked, 433 439 }: { 434 - profile: AppBskyActorDefs.ProfileViewBasic 440 + profile: AppBskyActorDefs.ProfileViewBasic | undefined 435 441 blocked?: boolean 436 442 }) { 437 443 const pal = usePalette('default') 438 444 const {currentAccount} = useSession() 439 - const isMe = profile.did === currentAccount?.did 445 + 446 + let label 447 + if (blocked) { 448 + label = <Trans context="description">Reply to a blocked post</Trans> 449 + } else if (profile != null) { 450 + const isMe = profile.did === currentAccount?.did 451 + if (isMe) { 452 + label = <Trans context="description">Reply to you</Trans> 453 + } else { 454 + label = ( 455 + <Trans context="description"> 456 + Reply to{' '} 457 + <ProfileHoverCard inline did={profile.did}> 458 + <TextLinkOnWebOnly 459 + type="md" 460 + style={pal.textLight} 461 + lineHeight={1.2} 462 + numberOfLines={1} 463 + href={makeProfileLink(profile)} 464 + text={ 465 + profile.displayName 466 + ? sanitizeDisplayName(profile.displayName) 467 + : sanitizeHandle(profile.handle) 468 + } 469 + /> 470 + </ProfileHoverCard> 471 + </Trans> 472 + ) 473 + } 474 + } 475 + 476 + if (!label) { 477 + // Should not happen. 478 + return null 479 + } 440 480 441 481 return ( 442 482 <View style={[s.flexRow, s.mb2, s.alignCenter]}> ··· 450 490 style={[pal.textLight, s.mr2]} 451 491 lineHeight={1.2} 452 492 numberOfLines={1}> 453 - {isMe ? ( 454 - <Trans context="description">Reply to you</Trans> 455 - ) : blocked ? ( 456 - <Trans context="description">Reply to a blocked post</Trans> 457 - ) : ( 458 - <Trans context="description"> 459 - Reply to{' '} 460 - <ProfileHoverCard inline did={profile.did}> 461 - <TextLinkOnWebOnly 462 - type="md" 463 - style={pal.textLight} 464 - lineHeight={1.2} 465 - numberOfLines={1} 466 - href={makeProfileLink(profile)} 467 - text={ 468 - profile.displayName 469 - ? sanitizeDisplayName(profile.displayName) 470 - : sanitizeHandle(profile.handle) 471 - } 472 - /> 473 - </ProfileHoverCard> 474 - </Trans> 475 - )} 493 + {label} 476 494 </Text> 477 495 </View> 478 496 )
+9 -8
src/view/screens/Search/Explore.tsx
··· 75 75 ) 76 76 } 77 77 78 - type LoadMoreItems = 78 + type LoadMoreItem = 79 79 | { 80 80 type: 'profile' 81 81 key: string 82 - avatar: string 82 + avatar: string | undefined 83 83 moderation: ModerationDecision 84 84 } 85 85 | { 86 86 type: 'feed' 87 87 key: string 88 - avatar: string 88 + avatar: string | undefined 89 89 moderation: undefined 90 90 } 91 91 ··· 98 98 }) { 99 99 const t = useTheme() 100 100 const {_} = useLingui() 101 - const items = React.useMemo(() => { 101 + const items: LoadMoreItem[] = React.useMemo(() => { 102 102 return item.items 103 103 .map(_item => { 104 + let loadMoreItem: LoadMoreItem | undefined 104 105 if (_item.type === 'profile') { 105 - return { 106 + loadMoreItem = { 106 107 type: 'profile', 107 108 key: _item.profile.did, 108 109 avatar: _item.profile.avatar, 109 110 moderation: moderateProfile(_item.profile, moderationOpts!), 110 111 } 111 112 } else if (_item.type === 'feed') { 112 - return { 113 + loadMoreItem = { 113 114 type: 'feed', 114 115 key: _item.feed.uri, 115 116 avatar: _item.feed.avatar, 116 117 moderation: undefined, 117 118 } 118 119 } 119 - return undefined 120 + return loadMoreItem 120 121 }) 121 - .filter(Boolean) as LoadMoreItems[] 122 + .filter(<T,>(n?: T): n is T => Boolean(n)) 122 123 }, [item.items, moderationOpts]) 123 124 124 125 if (items.length === 0) return null