diff options
author | Samuel Newman <mozzius@protonmail.com> | 2025-05-28 22:09:28 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-05-28 22:09:28 +0300 |
commit | cf63c2ca07c9a77bb92449ea4f3d78b8dd54fb8f (patch) | |
tree | 6136c729a77ef8daf3cbece566f4221c1b6a8d47 /src/view/com/post-thread/PostThreadItem.tsx | |
parent | 665a0430a3c04a3ad689954c5f930b4434daef79 (diff) | |
download | voidsky-cf63c2ca07c9a77bb92449ea4f3d78b8dd54fb8f.tar.zst |
Send FeedFeedback interactions in thread view (#8414)
Diffstat (limited to 'src/view/com/post-thread/PostThreadItem.tsx')
-rw-r--r-- | src/view/com/post-thread/PostThreadItem.tsx | 107 |
1 files changed, 79 insertions, 28 deletions
diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index 82852aa62..77adebac9 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -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) |