Detached QPs and hidden replies (#4878)

Co-authored-by: Hailey <me@haileyok.com>
This commit is contained in:
Eric Bailey 2024-08-21 21:20:45 -05:00 committed by GitHub
parent 56ab5e177f
commit 6616a6467e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
41 changed files with 2584 additions and 622 deletions

View file

@ -4,6 +4,7 @@ import {
AppBskyActorDefs,
AppBskyFeedDefs,
AppBskyFeedPost,
AppBskyFeedThreadgate,
AtUri,
ModerationDecision,
RichText as RichTextAPI,
@ -21,6 +22,7 @@ import {POST_TOMBSTONE, Shadow, usePostShadow} from '#/state/cache/post-shadow'
import {useFeedFeedbackContext} from '#/state/feed-feedback'
import {useSession} from '#/state/session'
import {useComposerControls} from '#/state/shell/composer'
import {useThreadgateHiddenReplyUris} from '#/state/threadgate-hidden-replies'
import {isReasonFeedSource, ReasonFeedSource} from 'lib/api/feed/types'
import {MAX_POST_LINES} from 'lib/constants'
import {usePalette} from 'lib/hooks/usePalette'
@ -33,6 +35,7 @@ import {precacheProfile} from 'state/queries/profile'
import {atoms as a} from '#/alf'
import {Repost_Stroke2_Corner2_Rounded as Repost} from '#/components/icons/Repost'
import {ContentHider} from '#/components/moderation/ContentHider'
import {AppModerationCause} from '#/components/Pills'
import {ProfileHoverCard} from '#/components/ProfileHoverCard'
import {RichText} from '#/components/RichText'
import {LabelsOnMyPost} from '../../../components/moderation/LabelsOnMe'
@ -80,7 +83,11 @@ export function FeedItem({
hideTopBorder,
isParentBlocked,
isParentNotFound,
}: FeedItemProps & {post: AppBskyFeedDefs.PostView}): React.ReactNode {
rootPost,
}: FeedItemProps & {
post: AppBskyFeedDefs.PostView
rootPost: AppBskyFeedDefs.PostView
}): React.ReactNode {
const postShadowed = usePostShadow(post)
const richText = useMemo(
() =>
@ -112,6 +119,7 @@ export function FeedItem({
hideTopBorder={hideTopBorder}
isParentBlocked={isParentBlocked}
isParentNotFound={isParentNotFound}
rootPost={rootPost}
/>
)
}
@ -133,9 +141,11 @@ let FeedItemInner = ({
hideTopBorder,
isParentBlocked,
isParentNotFound,
rootPost,
}: FeedItemProps & {
richText: RichTextAPI
post: Shadow<AppBskyFeedDefs.PostView>
rootPost: AppBskyFeedDefs.PostView
}): React.ReactNode => {
const queryClient = useQueryClient()
const {openComposer} = useComposerControls()
@ -217,6 +227,12 @@ let FeedItemInner = ({
AppBskyFeedDefs.isReasonRepost(reason) &&
reason.by.did === currentAccount?.did
const threadgateRecord = AppBskyFeedThreadgate.isRecord(
rootPost.threadgate?.record,
)
? rootPost.threadgate.record
: undefined
return (
<Link
testID={`feedItem-by-${post.author.handle}`}
@ -363,6 +379,8 @@ let FeedItemInner = ({
postEmbed={post.embed}
postAuthor={post.author}
onOpenEmbed={onOpenEmbed}
post={post}
threadgateRecord={threadgateRecord}
/>
<VideoDebug />
<PostCtrls
@ -372,6 +390,7 @@ let FeedItemInner = ({
onPressReply={onPressReply}
logContext="FeedItem"
feedContext={feedContext}
threadgateRecord={threadgateRecord}
/>
</View>
</View>
@ -381,23 +400,63 @@ let FeedItemInner = ({
FeedItemInner = memo(FeedItemInner)
let PostContent = ({
post,
moderation,
richText,
postEmbed,
postAuthor,
onOpenEmbed,
threadgateRecord,
}: {
moderation: ModerationDecision
richText: RichTextAPI
postEmbed: AppBskyFeedDefs.PostView['embed']
postAuthor: AppBskyFeedDefs.PostView['author']
onOpenEmbed: () => void
post: AppBskyFeedDefs.PostView
threadgateRecord?: AppBskyFeedThreadgate.Record
}): React.ReactNode => {
const pal = usePalette('default')
const {_} = useLingui()
const {currentAccount} = useSession()
const [limitLines, setLimitLines] = useState(
() => countLines(richText.text) >= MAX_POST_LINES,
)
const {uris: hiddenReplyUris, recentlyUnhiddenUris} =
useThreadgateHiddenReplyUris()
const additionalPostAlerts: AppModerationCause[] = React.useMemo(() => {
const isPostHiddenByHiddenReplyCache = hiddenReplyUris.has(post.uri)
const isPostHiddenByThreadgate =
!recentlyUnhiddenUris.has(post.uri) &&
!!threadgateRecord?.hiddenReplies?.includes(post.uri)
const isHidden = isPostHiddenByHiddenReplyCache || isPostHiddenByThreadgate
const isControlledByViewer =
isPostHiddenByHiddenReplyCache ||
(threadgateRecord &&
new AtUri(threadgateRecord.post).host === currentAccount?.did)
if (!isControlledByViewer) return []
const alertSource =
threadgateRecord && isPostHiddenByThreadgate
? new AtUri(threadgateRecord.post).host
: isPostHiddenByHiddenReplyCache
? currentAccount?.did
: undefined
return isHidden && alertSource
? [
{
type: 'reply-hidden',
source: {type: 'user', did: alertSource},
priority: 6,
},
]
: []
}, [
post,
hiddenReplyUris,
recentlyUnhiddenUris,
threadgateRecord,
currentAccount?.did,
])
const onPressShowMore = React.useCallback(() => {
setLimitLines(false)
@ -409,7 +468,11 @@ let PostContent = ({
modui={moderation.ui('contentList')}
ignoreMute
childContainerStyle={styles.contentHiderChild}>
<PostAlerts modui={moderation.ui('contentList')} style={[a.py_2xs]} />
<PostAlerts
modui={moderation.ui('contentList')}
style={[a.py_2xs]}
additionalCauses={additionalPostAlerts}
/>
{richText.text ? (
<View style={styles.postTextContainer}>
<RichText
@ -460,7 +523,7 @@ function ReplyToLabel({
if (blocked) {
label = <Trans context="description">Reply to a blocked post</Trans>
} else if (notFound) {
label = <Trans context="description">Reply to an unknown post</Trans>
label = <Trans context="description">Reply to a post</Trans>
} else if (profile != null) {
const isMe = profile.did === currentAccount?.did
if (isMe) {