Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

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

Remove the 'Who can reply' element except when viewing root, and add "edit" (#4615)

* Remove the 'Who can reply' element except when viewing root, and add the edit text to authors

* Switch to icon

authored by

Paul Frazee and committed by
GitHub
f769564e 0a0c7387

+164 -236
+150 -155
src/view/com/post-thread/PostThreadItem.tsx
··· 34 34 import {LabelsOnMyPost} from '../../../components/moderation/LabelsOnMe' 35 35 import {PostAlerts} from '../../../components/moderation/PostAlerts' 36 36 import {PostHider} from '../../../components/moderation/PostHider' 37 + import {WhoCanReply} from '../../../components/WhoCanReply' 37 38 import {getTranslatorLink, isPostInLanguage} from '../../../locale/helpers' 38 - import {WhoCanReplyBlock, WhoCanReplyInline} from '../threadgate/WhoCanReply' 39 39 import {ErrorMessage} from '../util/error/ErrorMessage' 40 40 import {Link, TextLink} from '../util/Link' 41 41 import {formatCount} from '../util/numeric/format' ··· 406 406 const isThreadedChildAdjacentBot = 407 407 isThreadedChild && nextPost?.ctx.depth === depth 408 408 return ( 409 - <> 410 - <PostOuterWrapper 411 - post={post} 412 - depth={depth} 413 - showParentReplyLine={!!showParentReplyLine} 414 - treeView={treeView} 415 - hasPrecedingItem={hasPrecedingItem} 416 - hideTopBorder={hideTopBorder}> 417 - <PostHider 418 - testID={`postThreadItem-by-${post.author.handle}`} 419 - href={postHref} 420 - disabled={overrideBlur} 421 - style={[pal.view]} 422 - modui={moderation.ui('contentList')} 423 - iconSize={isThreadedChild ? 26 : 38} 424 - iconStyles={ 425 - isThreadedChild 426 - ? {marginRight: 4} 427 - : {marginLeft: 2, marginRight: 2} 428 - } 429 - profile={post.author} 430 - interpretFilterAsBlur> 431 - <View 432 - style={{ 433 - flexDirection: 'row', 434 - gap: 10, 435 - paddingLeft: 8, 436 - height: isThreadedChildAdjacentTop ? 8 : 16, 437 - }}> 438 - <View style={{width: 38}}> 439 - {!isThreadedChild && showParentReplyLine && ( 409 + <PostOuterWrapper 410 + post={post} 411 + depth={depth} 412 + showParentReplyLine={!!showParentReplyLine} 413 + treeView={treeView} 414 + hasPrecedingItem={hasPrecedingItem} 415 + hideTopBorder={hideTopBorder}> 416 + <PostHider 417 + testID={`postThreadItem-by-${post.author.handle}`} 418 + href={postHref} 419 + disabled={overrideBlur} 420 + style={[pal.view]} 421 + modui={moderation.ui('contentList')} 422 + iconSize={isThreadedChild ? 26 : 38} 423 + iconStyles={ 424 + isThreadedChild ? {marginRight: 4} : {marginLeft: 2, marginRight: 2} 425 + } 426 + profile={post.author} 427 + interpretFilterAsBlur> 428 + <View 429 + style={{ 430 + flexDirection: 'row', 431 + gap: 10, 432 + paddingLeft: 8, 433 + height: isThreadedChildAdjacentTop ? 8 : 16, 434 + }}> 435 + <View style={{width: 38}}> 436 + {!isThreadedChild && showParentReplyLine && ( 437 + <View 438 + style={[ 439 + styles.replyLine, 440 + { 441 + flexGrow: 1, 442 + backgroundColor: pal.colors.replyLine, 443 + marginBottom: 4, 444 + }, 445 + ]} 446 + /> 447 + )} 448 + </View> 449 + </View> 450 + 451 + <View 452 + style={[ 453 + styles.layout, 454 + { 455 + paddingBottom: 456 + showChildReplyLine && !isThreadedChild 457 + ? 0 458 + : isThreadedChildAdjacentBot 459 + ? 4 460 + : 8, 461 + }, 462 + ]}> 463 + {/* If we are in threaded mode, the avatar is rendered in PostMeta */} 464 + {!isThreadedChild && ( 465 + <View style={styles.layoutAvi}> 466 + <PreviewableUserAvatar 467 + size={38} 468 + profile={post.author} 469 + moderation={moderation.ui('avatar')} 470 + type={post.author.associated?.labeler ? 'labeler' : 'user'} 471 + /> 472 + 473 + {showChildReplyLine && ( 440 474 <View 441 475 style={[ 442 476 styles.replyLine, 443 477 { 444 478 flexGrow: 1, 445 479 backgroundColor: pal.colors.replyLine, 446 - marginBottom: 4, 480 + marginTop: 4, 447 481 }, 448 482 ]} 449 483 /> 450 484 )} 451 485 </View> 452 - </View> 486 + )} 453 487 454 488 <View 455 - style={[ 456 - styles.layout, 457 - { 458 - paddingBottom: 459 - showChildReplyLine && !isThreadedChild 460 - ? 0 461 - : isThreadedChildAdjacentBot 462 - ? 4 463 - : 8, 464 - }, 465 - ]}> 466 - {/* If we are in threaded mode, the avatar is rendered in PostMeta */} 467 - {!isThreadedChild && ( 468 - <View style={styles.layoutAvi}> 469 - <PreviewableUserAvatar 470 - size={38} 471 - profile={post.author} 472 - moderation={moderation.ui('avatar')} 473 - type={post.author.associated?.labeler ? 'labeler' : 'user'} 474 - /> 475 - 476 - {showChildReplyLine && ( 477 - <View 478 - style={[ 479 - styles.replyLine, 480 - { 481 - flexGrow: 1, 482 - backgroundColor: pal.colors.replyLine, 483 - marginTop: 4, 484 - }, 485 - ]} 486 - /> 487 - )} 488 - </View> 489 - )} 490 - 491 - <View 489 + style={ 490 + isThreadedChild 491 + ? styles.layoutContentThreaded 492 + : styles.layoutContent 493 + }> 494 + <PostMeta 495 + author={post.author} 496 + moderation={moderation} 497 + authorHasWarning={!!post.author.labels?.length} 498 + timestamp={post.indexedAt} 499 + postHref={postHref} 500 + showAvatar={isThreadedChild} 501 + avatarModeration={moderation.ui('avatar')} 502 + avatarSize={28} 503 + displayNameType="md-bold" 504 + displayNameStyle={isThreadedChild && s.ml2} 492 505 style={ 493 - isThreadedChild 494 - ? styles.layoutContentThreaded 495 - : styles.layoutContent 496 - }> 497 - <PostMeta 498 - author={post.author} 499 - moderation={moderation} 500 - authorHasWarning={!!post.author.labels?.length} 501 - timestamp={post.indexedAt} 502 - postHref={postHref} 503 - showAvatar={isThreadedChild} 504 - avatarModeration={moderation.ui('avatar')} 505 - avatarSize={28} 506 - displayNameType="md-bold" 507 - displayNameStyle={isThreadedChild && s.ml2} 508 - style={ 509 - isThreadedChild && { 510 - alignItems: 'center', 511 - paddingBottom: isWeb ? 5 : 2, 512 - } 506 + isThreadedChild && { 507 + alignItems: 'center', 508 + paddingBottom: isWeb ? 5 : 2, 513 509 } 514 - /> 515 - <LabelsOnMyPost post={post} /> 516 - <PostAlerts 517 - modui={moderation.ui('contentList')} 518 - style={[a.pt_2xs, a.pb_2xs]} 519 - /> 520 - {richText?.text ? ( 521 - <View style={styles.postTextContainer}> 522 - <RichText 523 - enableTags 524 - value={richText} 525 - style={[a.flex_1, a.text_md]} 526 - numberOfLines={limitLines ? MAX_POST_LINES : undefined} 527 - authorHandle={post.author.handle} 528 - /> 529 - </View> 530 - ) : undefined} 531 - {limitLines ? ( 532 - <TextLink 533 - text={_(msg`Show More`)} 534 - style={pal.link} 535 - onPress={onPressShowMore} 536 - href="#" 510 + } 511 + /> 512 + <LabelsOnMyPost post={post} /> 513 + <PostAlerts 514 + modui={moderation.ui('contentList')} 515 + style={[a.pt_2xs, a.pb_2xs]} 516 + /> 517 + {richText?.text ? ( 518 + <View style={styles.postTextContainer}> 519 + <RichText 520 + enableTags 521 + value={richText} 522 + style={[a.flex_1, a.text_md]} 523 + numberOfLines={limitLines ? MAX_POST_LINES : undefined} 524 + authorHandle={post.author.handle} 537 525 /> 538 - ) : undefined} 539 - {post.embed && ( 540 - <View style={[a.pb_xs]}> 541 - <PostEmbeds embed={post.embed} moderation={moderation} /> 542 - </View> 543 - )} 544 - <PostCtrls 545 - post={post} 546 - record={record} 547 - richText={richText} 548 - onPressReply={onPressReply} 549 - logContext="PostThreadItem" 526 + </View> 527 + ) : undefined} 528 + {limitLines ? ( 529 + <TextLink 530 + text={_(msg`Show More`)} 531 + style={pal.link} 532 + onPress={onPressShowMore} 533 + href="#" 550 534 /> 551 - </View> 535 + ) : undefined} 536 + {post.embed && ( 537 + <View style={[a.pb_xs]}> 538 + <PostEmbeds embed={post.embed} moderation={moderation} /> 539 + </View> 540 + )} 541 + <PostCtrls 542 + post={post} 543 + record={record} 544 + richText={richText} 545 + onPressReply={onPressReply} 546 + logContext="PostThreadItem" 547 + /> 552 548 </View> 553 - {hasMore ? ( 554 - <Link 555 - style={[ 556 - styles.loadMore, 557 - { 558 - paddingLeft: treeView ? 8 : 70, 559 - paddingTop: 0, 560 - paddingBottom: treeView ? 4 : 12, 561 - }, 562 - ]} 563 - href={postHref} 564 - title={itemTitle} 565 - noFeedback> 566 - <Text type="sm-medium" style={pal.textLight}> 567 - <Trans>More</Trans> 568 - </Text> 569 - <FontAwesomeIcon 570 - icon="angle-right" 571 - color={pal.colors.textLight} 572 - size={14} 573 - /> 574 - </Link> 575 - ) : undefined} 576 - </PostHider> 577 - </PostOuterWrapper> 578 - <WhoCanReplyBlock post={post} isThreadAuthor={isThreadAuthor} /> 579 - </> 549 + </View> 550 + {hasMore ? ( 551 + <Link 552 + style={[ 553 + styles.loadMore, 554 + { 555 + paddingLeft: treeView ? 8 : 70, 556 + paddingTop: 0, 557 + paddingBottom: treeView ? 4 : 12, 558 + }, 559 + ]} 560 + href={postHref} 561 + title={itemTitle} 562 + noFeedback> 563 + <Text type="sm-medium" style={pal.textLight}> 564 + <Trans>More</Trans> 565 + </Text> 566 + <FontAwesomeIcon 567 + icon="angle-right" 568 + color={pal.colors.textLight} 569 + size={14} 570 + /> 571 + </Link> 572 + ) : undefined} 573 + </PostHider> 574 + </PostOuterWrapper> 580 575 ) 581 576 } 582 577 } ··· 671 666 s.mb10, 672 667 ]}> 673 668 <Text style={[a.text_sm, pal.textLight]}>{niceDate(post.indexedAt)}</Text> 674 - <WhoCanReplyInline post={post} isThreadAuthor={isThreadAuthor} /> 669 + <WhoCanReply post={post} isThreadAuthor={isThreadAuthor} /> 675 670 {needsTranslation && ( 676 671 <> 677 672 <Text style={[a.text_sm, pal.textLight]}>&middot;</Text>
+14 -81
src/view/com/threadgate/WhoCanReply.tsx src/components/WhoCanReply.tsx
··· 33 33 import {Earth_Stroke2_Corner0_Rounded as Earth} from '#/components/icons/Globe' 34 34 import {Group3_Stroke2_Corner0_Rounded as Group} from '#/components/icons/Group' 35 35 import {Text} from '#/components/Typography' 36 - import {TextLink} from '../util/Link' 36 + import {TextLink} from '../view/com/util/Link' 37 + import {PencilLine_Stroke2_Corner0_Rounded as PencilLine} from './icons/Pencil' 37 38 38 39 interface WhoCanReplyProps { 39 40 post: AppBskyFeedDefs.PostView ··· 41 42 style?: StyleProp<ViewStyle> 42 43 } 43 44 44 - export function WhoCanReplyInline({ 45 - post, 46 - isThreadAuthor, 47 - style, 48 - }: WhoCanReplyProps) { 45 + export function WhoCanReply({post, isThreadAuthor, style}: WhoCanReplyProps) { 49 46 const {_} = useLingui() 50 47 const t = useTheme() 51 48 const infoDialogControl = useDialogControl() ··· 90 87 ]}> 91 88 {description} 92 89 </Text> 90 + {isThreadAuthor && ( 91 + <PencilLine width={12} fill={t.palette.primary_500} /> 92 + )} 93 93 </View> 94 94 )} 95 95 </Button> 96 - <InfoDialog control={infoDialogControl} post={post} settings={settings} /> 97 - </> 98 - ) 99 - } 100 - 101 - export function WhoCanReplyBlock({ 102 - post, 103 - isThreadAuthor, 104 - style, 105 - }: WhoCanReplyProps) { 106 - const {_} = useLingui() 107 - const t = useTheme() 108 - const infoDialogControl = useDialogControl() 109 - const {settings, isRootPost, onPressEdit} = useWhoCanReply(post) 110 - 111 - if (!isRootPost) { 112 - return null 113 - } 114 - if (!settings.length && !isThreadAuthor) { 115 - return null 116 - } 117 - 118 - const isEverybody = settings.length === 0 119 - const isNobody = !!settings.find(gate => gate.type === 'nobody') 120 - const description = isEverybody 121 - ? _(msg`Everybody can reply`) 122 - : isNobody 123 - ? _(msg`Replies on this thread are disabled`) 124 - : _(msg`Some people can reply`) 125 - 126 - return ( 127 - <> 128 - <Button 129 - label={ 130 - isThreadAuthor ? _(msg`Edit who can reply`) : _(msg`Who can reply`) 131 - } 132 - onPress={isThreadAuthor ? onPressEdit : infoDialogControl.open} 133 - hitSlop={HITSLOP_10}> 134 - {({hovered}) => ( 135 - <View 136 - style={[ 137 - a.flex_1, 138 - a.flex_row, 139 - a.align_center, 140 - a.py_sm, 141 - a.pr_lg, 142 - style, 143 - ]}> 144 - <View style={[{paddingLeft: 25, paddingRight: 18}]}> 145 - <Icon color={t.palette.contrast_300} settings={settings} /> 146 - </View> 147 - <Text 148 - style={[ 149 - a.text_sm, 150 - a.leading_tight, 151 - t.atoms.text_contrast_medium, 152 - hovered && a.underline, 153 - ]}> 154 - {description} 155 - </Text> 156 - </View> 157 - )} 158 - </Button> 159 - <InfoDialog control={infoDialogControl} post={post} settings={settings} /> 96 + <WhoCanReplyDialog control={infoDialogControl} post={post} /> 160 97 </> 161 98 ) 162 99 } ··· 176 113 return <IconComponent fill={color} width={width} /> 177 114 } 178 115 179 - function InfoDialog({ 116 + export function WhoCanReplyDialog({ 180 117 control, 181 118 post, 182 - settings, 183 119 }: { 184 120 control: Dialog.DialogControlProps 185 121 post: AppBskyFeedDefs.PostView 186 - settings: ThreadgateSetting[] 187 122 }) { 188 123 return ( 189 124 <Dialog.Outer control={control}> 190 125 <Dialog.Handle /> 191 - <InfoDialogInner post={post} settings={settings} /> 126 + <WhoCanReplyDialogInner post={post} /> 192 127 </Dialog.Outer> 193 128 ) 194 129 } 195 130 196 - function InfoDialogInner({ 197 - post, 198 - settings, 199 - }: { 200 - post: AppBskyFeedDefs.PostView 201 - settings: ThreadgateSetting[] 202 - }) { 131 + function WhoCanReplyDialogInner({post}: {post: AppBskyFeedDefs.PostView}) { 203 132 const {_} = useLingui() 133 + const {settings} = useWhoCanReply(post) 204 134 return ( 205 135 <Dialog.ScrollableInner 206 136 label={_(msg`Who can reply dialog`)} ··· 334 264 name: 'threadgate', 335 265 settings, 336 266 async onConfirm(newSettings: ThreadgateSetting[]) { 267 + if (JSON.stringify(settings) === JSON.stringify(newSettings)) { 268 + return 269 + } 337 270 try { 338 271 if (newSettings.length) { 339 272 await createThreadgate(agent, post.uri, newSettings)