Send FeedFeedback interactions in thread view (#8414)

This commit is contained in:
Samuel Newman
2025-05-28 22:09:28 +03:00
committed by GitHub
parent 665a0430a3
commit cf63c2ca07
15 changed files with 365 additions and 124 deletions
+79 -28
View File
@@ -1,4 +1,4 @@
import React, {memo, useMemo} from 'react'
import {memo, useCallback, useMemo, useState} from 'react'
import {
type GestureResponderEvent,
StyleSheet,
@@ -6,7 +6,7 @@ import {
View,
} from 'react-native'
import {
type AppBskyFeedDefs,
AppBskyFeedDefs,
AppBskyFeedPost,
type AppBskyFeedThreadgate,
AtUri,
@@ -35,10 +35,12 @@ import {
usePostShadow,
} from '#/state/cache/post-shadow'
import {useProfileShadow} from '#/state/cache/profile-shadow'
import {FeedFeedbackProvider, useFeedFeedback} from '#/state/feed-feedback'
import {useLanguagePrefs} from '#/state/preferences'
import {type ThreadPost} from '#/state/queries/post-thread'
import {useSession} from '#/state/session'
import {useMergedThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies'
import {useUnstablePostSource} from '#/state/unstable-post-source'
import {PostThreadFollowBtn} from '#/view/com/post-thread/PostThreadFollowBtn'
import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
import {Link, TextLink} from '#/view/com/util/Link'
@@ -201,18 +203,21 @@ let PostThreadItemLoaded = ({
hideTopBorder?: boolean
threadgateRecord?: AppBskyFeedThreadgate.Record
}): React.ReactNode => {
const {currentAccount, hasSession} = useSession()
const source = useUnstablePostSource(post.uri)
const feedFeedback = useFeedFeedback(source?.feed, hasSession)
const t = useTheme()
const pal = usePalette('default')
const {_, i18n} = useLingui()
const langPrefs = useLanguagePrefs()
const {openComposer} = useOpenComposer()
const [limitLines, setLimitLines] = React.useState(
const [limitLines, setLimitLines] = useState(
() => countLines(richText?.text) >= MAX_POST_LINES,
)
const {currentAccount} = useSession()
const shadowedPostAuthor = useProfileShadow(post.author)
const rootUri = record.reply?.root?.uri || post.uri
const postHref = React.useMemo(() => {
const postHref = useMemo(() => {
const urip = new AtUri(post.uri)
return makeProfileLink(post.author, 'post', urip.rkey)
}, [post.uri, post.author])
@@ -220,12 +225,12 @@ let PostThreadItemLoaded = ({
const authorHref = makeProfileLink(post.author)
const authorTitle = post.author.handle
const isThreadAuthor = getThreadAuthor(post, record) === currentAccount?.did
const likesHref = React.useMemo(() => {
const likesHref = useMemo(() => {
const urip = new AtUri(post.uri)
return makeProfileLink(post.author, 'post', urip.rkey, 'liked-by')
}, [post.uri, post.author])
const likesTitle = _(msg`Likes on this post`)
const repostsHref = React.useMemo(() => {
const repostsHref = useMemo(() => {
const urip = new AtUri(post.uri)
return makeProfileLink(post.author, 'post', urip.rkey, 'reposted-by')
}, [post.uri, post.author])
@@ -233,7 +238,7 @@ let PostThreadItemLoaded = ({
const threadgateHiddenReplies = useMergedThreadgateHiddenReplies({
threadgateRecord,
})
const additionalPostAlerts: AppModerationCause[] = React.useMemo(() => {
const additionalPostAlerts: AppModerationCause[] = useMemo(() => {
const isPostHiddenByThreadgate = threadgateHiddenReplies.has(post.uri)
const isControlledByViewer = new AtUri(rootUri).host === currentAccount?.did
return isControlledByViewer && isPostHiddenByThreadgate
@@ -246,7 +251,7 @@ let PostThreadItemLoaded = ({
]
: []
}, [post, currentAccount?.did, threadgateHiddenReplies, rootUri])
const quotesHref = React.useMemo(() => {
const quotesHref = useMemo(() => {
const urip = new AtUri(post.uri)
return makeProfileLink(post.author, 'post', urip.rkey, 'quotes')
}, [post.uri, post.author])
@@ -270,7 +275,15 @@ let PostThreadItemLoaded = ({
[post, langPrefs.primaryLanguage],
)
const onPressReply = React.useCallback(() => {
const onPressReply = () => {
if (source) {
feedFeedback.sendInteraction({
item: post.uri,
event: 'app.bsky.feed.defs#interactionReply',
feedContext: source.post.feedContext,
reqId: source.post.reqId,
})
}
openComposer({
replyTo: {
uri: post.uri,
@@ -282,14 +295,46 @@ let PostThreadItemLoaded = ({
},
onPost: onPostReply,
})
}, [openComposer, post, record, onPostReply, moderation])
}
const onPressShowMore = React.useCallback(() => {
const onOpenAuthor = () => {
if (source) {
feedFeedback.sendInteraction({
item: post.uri,
event: 'app.bsky.feed.defs#clickthroughAuthor',
feedContext: source.post.feedContext,
reqId: source.post.reqId,
})
}
}
const onOpenEmbed = () => {
if (source) {
feedFeedback.sendInteraction({
item: post.uri,
event: 'app.bsky.feed.defs#clickthroughEmbed',
feedContext: source.post.feedContext,
reqId: source.post.reqId,
})
}
}
const onPressShowMore = useCallback(() => {
setLimitLines(false)
}, [setLimitLines])
const {isActive: live} = useActorStatus(post.author)
const reason = source?.post.reason
const viaRepost = useMemo(() => {
if (AppBskyFeedDefs.isReasonRepost(reason) && reason.uri && reason.cid) {
return {
uri: reason.uri,
cid: reason.cid,
}
}
}, [reason])
if (!record) {
return <ErrorMessage message={_(msg`Invalid or unsupported post record`)} />
}
@@ -309,10 +354,8 @@ let PostThreadItemLoaded = ({
<View
style={[
styles.replyLine,
{
flexGrow: 1,
backgroundColor: pal.colors.replyLine,
},
a.flex_grow,
{backgroundColor: pal.colors.replyLine},
]}
/>
</View>
@@ -334,13 +377,15 @@ let PostThreadItemLoaded = ({
moderation={moderation.ui('avatar')}
type={post.author.associated?.labeler ? 'labeler' : 'user'}
live={live}
onBeforePress={onOpenAuthor}
/>
<View style={[a.flex_1]}>
<View style={[a.flex_row, a.align_center]}>
<Link
style={[a.flex_shrink]}
href={authorHref}
title={authorTitle}>
title={authorTitle}
onBeforePress={onOpenAuthor}>
<Text
emoji
style={[
@@ -413,6 +458,7 @@ let PostThreadItemLoaded = ({
embed={post.embed}
moderation={moderation}
viewContext={PostEmbedViewContext.ThreadHighlighted}
onOpen={onOpenEmbed}
/>
</View>
)}
@@ -494,16 +540,21 @@ let PostThreadItemLoaded = ({
marginLeft: -5,
},
]}>
<PostControls
big
post={post}
record={record}
richText={richText}
onPressReply={onPressReply}
onPostReply={onPostReply}
logContext="PostThreadItem"
threadgateRecord={threadgateRecord}
/>
<FeedFeedbackProvider value={feedFeedback}>
<PostControls
big
post={post}
record={record}
richText={richText}
onPressReply={onPressReply}
onPostReply={onPostReply}
logContext="PostThreadItem"
threadgateRecord={threadgateRecord}
feedContext={source?.post?.feedContext}
reqId={source?.post?.reqId}
viaRepost={viaRepost}
/>
</FeedFeedbackProvider>
</View>
</View>
</View>
@@ -779,7 +830,7 @@ function ExpandedPostDetails({
const isRootPost = !('reply' in post.record)
const langPrefs = useLanguagePrefs()
const onTranslatePress = React.useCallback(
const onTranslatePress = useCallback(
(e: GestureResponderEvent) => {
e.preventDefault()
openLink(translatorUrl, true)