Integrate post-source

(cherry picked from commit fe053e9b38395a4fcb30a4367bc800f64ea84fe9)
This commit is contained in:
Eric Bailey
2025-06-11 14:15:03 -05:00
parent e16aa197e7
commit ffddc16f5a
2 changed files with 108 additions and 16 deletions
@@ -1,7 +1,7 @@
import {memo, useCallback, useMemo} from 'react' import {memo, useCallback, useMemo} from 'react'
import {type GestureResponderEvent, Text as RNText, View} from 'react-native' import {type GestureResponderEvent, Text as RNText, View} from 'react-native'
import { import {
type AppBskyFeedDefs, AppBskyFeedDefs,
AppBskyFeedPost, AppBskyFeedPost,
type AppBskyFeedThreadgate, type AppBskyFeedThreadgate,
AtUri, AtUri,
@@ -26,11 +26,13 @@ import {
usePostShadow, usePostShadow,
} from '#/state/cache/post-shadow' } from '#/state/cache/post-shadow'
import {useProfileShadow} from '#/state/cache/profile-shadow' import {useProfileShadow} from '#/state/cache/profile-shadow'
import {FeedFeedbackProvider, useFeedFeedback} from '#/state/feed-feedback'
import {useLanguagePrefs} from '#/state/preferences' import {useLanguagePrefs} from '#/state/preferences'
import {type ThreadItem} from '#/state/queries/usePostThread/types' import {type ThreadItem} from '#/state/queries/usePostThread/types'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import {type OnPostSuccessData} from '#/state/shell/composer' import {type OnPostSuccessData} from '#/state/shell/composer'
import {useMergedThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies' import {useMergedThreadgateHiddenReplies} from '#/state/threadgate-hidden-replies'
import {type PostSource} from '#/state/unstable-post-source'
import {PostThreadFollowBtn} from '#/view/com/post-thread/PostThreadFollowBtn' import {PostThreadFollowBtn} from '#/view/com/post-thread/PostThreadFollowBtn'
import {Link} from '#/view/com/util/Link' import {Link} from '#/view/com/util/Link'
import {formatCount} from '#/view/com/util/numeric/format' import {formatCount} from '#/view/com/util/numeric/format'
@@ -64,10 +66,12 @@ export function ThreadItemAnchor({
item, item,
onPostSuccess, onPostSuccess,
threadgateRecord, threadgateRecord,
postSource,
}: { }: {
item: Extract<ThreadItem, {type: 'threadPost'}> item: Extract<ThreadItem, {type: 'threadPost'}>
onPostSuccess?: (data: OnPostSuccessData) => void onPostSuccess?: (data: OnPostSuccessData) => void
threadgateRecord?: AppBskyFeedThreadgate.Record threadgateRecord?: AppBskyFeedThreadgate.Record
postSource?: PostSource
}) { }) {
const postShadow = usePostShadow(item.value.post) const postShadow = usePostShadow(item.value.post)
const threadRootUri = item.value.post.record.reply?.root?.uri || item.uri const threadRootUri = item.value.post.record.reply?.root?.uri || item.uri
@@ -86,6 +90,7 @@ export function ThreadItemAnchor({
postShadow={postShadow} postShadow={postShadow}
onPostSuccess={onPostSuccess} onPostSuccess={onPostSuccess}
threadgateRecord={threadgateRecord} threadgateRecord={threadgateRecord}
postSource={postSource}
/> />
) )
} }
@@ -161,17 +166,20 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({
postShadow, postShadow,
onPostSuccess, onPostSuccess,
threadgateRecord, threadgateRecord,
postSource,
}: { }: {
item: Extract<ThreadItem, {type: 'threadPost'}> item: Extract<ThreadItem, {type: 'threadPost'}>
isRoot: boolean isRoot: boolean
postShadow: Shadow<AppBskyFeedDefs.PostView> postShadow: Shadow<AppBskyFeedDefs.PostView>
onPostSuccess?: (data: OnPostSuccessData) => void onPostSuccess?: (data: OnPostSuccessData) => void
threadgateRecord?: AppBskyFeedThreadgate.Record threadgateRecord?: AppBskyFeedThreadgate.Record
postSource?: PostSource
}) { }) {
const t = useTheme() const t = useTheme()
const {_, i18n} = useLingui() const {_, i18n} = useLingui()
const {openComposer} = useOpenComposer() const {openComposer} = useOpenComposer()
const {currentAccount} = useSession() const {currentAccount, hasSession} = useSession()
const feedFeedback = useFeedFeedback(postSource?.feed, hasSession)
const post = item.value.post const post = item.value.post
const record = item.value.post.record const record = item.value.post.record
@@ -228,6 +236,17 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({
const showFollowButton = const showFollowButton =
currentAccount?.did !== post.author.did && !onlyFollowersCanReply currentAccount?.did !== post.author.did && !onlyFollowersCanReply
const viaRepost = useMemo(() => {
const reason = postSource?.post.reason
if (AppBskyFeedDefs.isReasonRepost(reason) && reason.uri && reason.cid) {
return {
uri: reason.uri,
cid: reason.cid,
}
}
}, [postSource])
const onPressReply = useCallback(() => { const onPressReply = useCallback(() => {
openComposer({ openComposer({
replyTo: { replyTo: {
@@ -240,7 +259,46 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({
}, },
onPostSuccess: onPostSuccess, onPostSuccess: onPostSuccess,
}) })
}, [openComposer, post, record, onPostSuccess, moderation])
if (postSource) {
feedFeedback.sendInteraction({
item: post.uri,
event: 'app.bsky.feed.defs#interactionReply',
feedContext: postSource.post.feedContext,
reqId: postSource.post.reqId,
})
}
}, [
openComposer,
post,
record,
onPostSuccess,
moderation,
postSource,
feedFeedback,
])
const onOpenAuthor = () => {
if (postSource) {
feedFeedback.sendInteraction({
item: post.uri,
event: 'app.bsky.feed.defs#clickthroughAuthor',
feedContext: postSource.post.feedContext,
reqId: postSource.post.reqId,
})
}
}
const onOpenEmbed = () => {
if (postSource) {
feedFeedback.sendInteraction({
item: post.uri,
event: 'app.bsky.feed.defs#clickthroughEmbed',
feedContext: postSource.post.feedContext,
reqId: postSource.post.reqId,
})
}
}
return ( return (
<> <>
@@ -261,13 +319,15 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({
moderation={moderation.ui('avatar')} moderation={moderation.ui('avatar')}
type={post.author.associated?.labeler ? 'labeler' : 'user'} type={post.author.associated?.labeler ? 'labeler' : 'user'}
live={live} live={live}
onBeforePress={onOpenAuthor}
/> />
<View style={[a.flex_1]}> <View style={[a.flex_1]}>
<View style={[a.flex_row, a.align_center]}> <View style={[a.flex_row, a.align_center]}>
<Link <Link
style={[a.flex_shrink]} style={[a.flex_shrink]}
href={authorHref} href={authorHref}
title={authorTitle}> title={authorTitle}
onBeforePress={onOpenAuthor}>
<Text <Text
emoji emoji
style={[a.text_lg, a.font_bold, a.leading_snug, a.self_start]} style={[a.text_lg, a.font_bold, a.leading_snug, a.self_start]}
@@ -332,6 +392,7 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({
embed={post.embed} embed={post.embed}
moderation={moderation} moderation={moderation}
viewContext={PostEmbedViewContext.ThreadHighlighted} viewContext={PostEmbedViewContext.ThreadHighlighted}
onOpen={onOpenEmbed}
/> />
</View> </View>
)} )}
@@ -411,15 +472,20 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({
marginLeft: -5, marginLeft: -5,
}, },
]}> ]}>
<PostControls <FeedFeedbackProvider value={feedFeedback}>
big <PostControls
post={postShadow} big
record={record} post={postShadow}
richText={richText} record={record}
onPressReply={onPressReply} richText={richText}
logContext="PostThreadItem" onPressReply={onPressReply}
threadgateRecord={threadgateRecord} logContext="PostThreadItem"
/> threadgateRecord={threadgateRecord}
feedContext={postSource?.post?.feedContext}
reqId={postSource?.post?.reqId}
viaRepost={viaRepost}
/>
</FeedFeedbackProvider>
</View> </View>
</View> </View>
</View> </View>
+29 -3
View File
@@ -5,11 +5,13 @@ import {Trans} from '@lingui/macro'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
import {useFeedFeedback} from '#/state/feed-feedback'
import {type ThreadViewOption} from '#/state/queries/preferences/useThreadPreferences' import {type ThreadViewOption} from '#/state/queries/preferences/useThreadPreferences'
import {type ThreadItem, usePostThread} from '#/state/queries/usePostThread' import {type ThreadItem, usePostThread} from '#/state/queries/usePostThread'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import {type OnPostSuccessData} from '#/state/shell/composer' import {type OnPostSuccessData} from '#/state/shell/composer'
import {useShellLayout} from '#/state/shell/shell-layout' import {useShellLayout} from '#/state/shell/shell-layout'
import {useUnstablePostSource} from '#/state/unstable-post-source'
import {PostThreadComposePrompt} from '#/view/com/post-thread/PostThreadComposePrompt' import {PostThreadComposePrompt} from '#/view/com/post-thread/PostThreadComposePrompt'
import {List, type ListMethods} from '#/view/com/util/List' import {List, type ListMethods} from '#/view/com/util/List'
import {HeaderDropdown} from '#/screens/PostThread/components/HeaderDropdown' import {HeaderDropdown} from '#/screens/PostThread/components/HeaderDropdown'
@@ -40,11 +42,13 @@ import {ListFooter} from '#/components/Lists'
const PARENT_CHUNK_SIZE = 5 const PARENT_CHUNK_SIZE = 5
const CHILDREN_CHUNK_SIZE = 50 const CHILDREN_CHUNK_SIZE = 50
export function PostThread({uri}: {uri: string | undefined}) { export function PostThread({uri}: {uri: string}) {
const {gtMobile} = useBreakpoints() const {gtMobile} = useBreakpoints()
const {hasSession} = useSession() const {hasSession} = useSession()
const initialNumToRender = useInitialNumToRender() // TODO const initialNumToRender = useInitialNumToRender() // TODO
const {height: windowHeight} = useWindowDimensions() const {height: windowHeight} = useWindowDimensions()
const anchorPostSource = useUnstablePostSource(uri)
const feedFeedback = useFeedFeedback(anchorPostSource?.feed, hasSession)
/* /*
* One query to rule them all * One query to rule them all
@@ -87,7 +91,22 @@ export function PostThread({uri}: {uri: string | undefined}) {
}, },
onPostSuccess: optimisticOnPostReply, onPostSuccess: optimisticOnPostReply,
}) })
}, [anchor, openComposer, optimisticOnPostReply])
if (anchorPostSource) {
feedFeedback.sendInteraction({
item: post.uri,
event: 'app.bsky.feed.defs#interactionReply',
feedContext: anchorPostSource.post.feedContext,
reqId: anchorPostSource.post.reqId,
})
}
}, [
anchor,
openComposer,
optimisticOnPostReply,
anchorPostSource,
feedFeedback,
])
const isRoot = !!anchor && anchor.value.post.record.reply === undefined const isRoot = !!anchor && anchor.value.post.record.reply === undefined
const canReply = !anchor?.value.post?.viewer?.replyDisabled const canReply = !anchor?.value.post?.viewer?.replyDisabled
@@ -380,6 +399,7 @@ export function PostThread({uri}: {uri: string | undefined}) {
item={item} item={item}
threadgateRecord={thread.data.threadgate?.record ?? undefined} threadgateRecord={thread.data.threadgate?.record ?? undefined}
onPostSuccess={optimisticOnPostReply} onPostSuccess={optimisticOnPostReply}
postSource={anchorPostSource}
/> />
</View> </View>
) )
@@ -452,7 +472,13 @@ export function PostThread({uri}: {uri: string | undefined}) {
} }
return null return null
}, },
[thread, optimisticOnPostReply, onReplyToAnchor, gtMobile], [
thread,
optimisticOnPostReply,
onReplyToAnchor,
gtMobile,
anchorPostSource,
],
) )
return ( return (