Log additional context for post:* events (#11035)
This commit is contained in:
@@ -14,7 +14,11 @@ import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
|
|||||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||||
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
||||||
import {usePostViewTracking} from '#/lib/hooks/usePostViewTracking'
|
import {usePostViewTracking} from '#/lib/hooks/usePostViewTracking'
|
||||||
import {useFeedFeedback} from '#/state/feed-feedback'
|
import {
|
||||||
|
FeedFeedbackProvider,
|
||||||
|
type StateContext as FeedFeedbackStateContext,
|
||||||
|
useFeedFeedback,
|
||||||
|
} from '#/state/feed-feedback'
|
||||||
import {type ThreadViewOption} from '#/state/queries/preferences/useThreadPreferences'
|
import {type ThreadViewOption} from '#/state/queries/preferences/useThreadPreferences'
|
||||||
import {
|
import {
|
||||||
PostThreadContextProvider,
|
PostThreadContextProvider,
|
||||||
@@ -56,6 +60,9 @@ import {IS_NATIVE} from '#/env'
|
|||||||
|
|
||||||
const PARENT_CHUNK_SIZE = IS_NATIVE ? 5 : 20
|
const PARENT_CHUNK_SIZE = IS_NATIVE ? 5 : 20
|
||||||
const CHILDREN_CHUNK_SIZE = 50
|
const CHILDREN_CHUNK_SIZE = 50
|
||||||
|
const analyticsOnlyOnItemSeen: FeedFeedbackStateContext['onItemSeen'] = () => {}
|
||||||
|
const analyticsOnlySendInteraction: FeedFeedbackStateContext['sendInteraction'] =
|
||||||
|
() => {}
|
||||||
|
|
||||||
export function PostThread({uri}: {uri: string}) {
|
export function PostThread({uri}: {uri: string}) {
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
@@ -569,68 +576,71 @@ export function PostThread({uri}: {uri: string}) {
|
|||||||
onRetry={thread.actions.refetch}
|
onRetry={thread.actions.refetch}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<List
|
<AnalyticsOnlyFeedFeedbackProvider
|
||||||
ref={listRef}
|
feedDescriptor={feedFeedback.feedDescriptor}>
|
||||||
data={deferredSlices}
|
<List
|
||||||
renderItem={renderItem}
|
ref={listRef}
|
||||||
keyExtractor={keyExtractor}
|
data={deferredSlices}
|
||||||
onContentSizeChange={platform({
|
renderItem={renderItem}
|
||||||
web: onContentSizeChangeWebOnly,
|
keyExtractor={keyExtractor}
|
||||||
default: onContentSizeChangeNativeOnly,
|
onContentSizeChange={platform({
|
||||||
})}
|
web: onContentSizeChangeWebOnly,
|
||||||
onStartReached={onStartReached}
|
default: onContentSizeChangeNativeOnly,
|
||||||
onEndReached={onEndReached}
|
})}
|
||||||
onEndReachedThreshold={4}
|
onStartReached={onStartReached}
|
||||||
onStartReachedThreshold={1}
|
onEndReached={onEndReached}
|
||||||
onItemSeen={item => {
|
onEndReachedThreshold={4}
|
||||||
// Track post:view for parent posts and replies (non-anchor posts)
|
onStartReachedThreshold={1}
|
||||||
if (item.type === 'threadPost' && item.depth !== 0) {
|
onItemSeen={item => {
|
||||||
trackThreadItemView(item.value.post)
|
// Track post:view for parent posts and replies (non-anchor posts)
|
||||||
|
if (item.type === 'threadPost' && item.depth !== 0) {
|
||||||
|
trackThreadItemView(item.value.post)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/**
|
||||||
|
* NATIVE ONLY
|
||||||
|
* {@link https://reactnative.dev/docs/scrollview#maintainvisiblecontentposition}
|
||||||
|
*/
|
||||||
|
maintainVisibleContentPosition={{minIndexForVisible: 0}}
|
||||||
|
desktopFixedHeight
|
||||||
|
sideBorders={false}
|
||||||
|
ListFooterComponent={
|
||||||
|
<ListFooter
|
||||||
|
/*
|
||||||
|
* On native, if `deferParents` is true, we need some extra buffer to
|
||||||
|
* account for the `on*ReachedThreshold` values.
|
||||||
|
*
|
||||||
|
* Otherwise, and on web, this value needs to be the height of
|
||||||
|
* the viewport _minus_ a sensible min-post height e.g. 200, so
|
||||||
|
* that there's enough scroll remaining to get the anchor post
|
||||||
|
* back to the top of the screen when handling scroll.
|
||||||
|
*/
|
||||||
|
height={platform({
|
||||||
|
web: defaultListFooterHeight,
|
||||||
|
default: deferParents
|
||||||
|
? windowHeight * 2
|
||||||
|
: defaultListFooterHeight,
|
||||||
|
})}
|
||||||
|
style={isTombstoneView ? {borderTopWidth: 0} : undefined}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
}}
|
initialNumToRender={initialNumToRender}
|
||||||
/**
|
/**
|
||||||
* NATIVE ONLY
|
* Default: 21
|
||||||
* {@link https://reactnative.dev/docs/scrollview#maintainvisiblecontentposition}
|
*
|
||||||
*/
|
* Smaller for placeholder data so we don't waste time rendering skeletons
|
||||||
maintainVisibleContentPosition={{minIndexForVisible: 0}}
|
*/
|
||||||
desktopFixedHeight
|
windowSize={thread.state.isPlaceholderData ? 1 : 7}
|
||||||
sideBorders={false}
|
/**
|
||||||
ListFooterComponent={
|
* Default: 10
|
||||||
<ListFooter
|
*/
|
||||||
/*
|
maxToRenderPerBatch={5}
|
||||||
* On native, if `deferParents` is true, we need some extra buffer to
|
/**
|
||||||
* account for the `on*ReachedThreshold` values.
|
* Default: 50
|
||||||
*
|
*/
|
||||||
* Otherwise, and on web, this value needs to be the height of
|
updateCellsBatchingPeriod={100}
|
||||||
* the viewport _minus_ a sensible min-post height e.g. 200, so
|
/>
|
||||||
* that there's enough scroll remaining to get the anchor post
|
</AnalyticsOnlyFeedFeedbackProvider>
|
||||||
* back to the top of the screen when handling scroll.
|
|
||||||
*/
|
|
||||||
height={platform({
|
|
||||||
web: defaultListFooterHeight,
|
|
||||||
default: deferParents
|
|
||||||
? windowHeight * 2
|
|
||||||
: defaultListFooterHeight,
|
|
||||||
})}
|
|
||||||
style={isTombstoneView ? {borderTopWidth: 0} : undefined}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
initialNumToRender={initialNumToRender}
|
|
||||||
/**
|
|
||||||
* Default: 21
|
|
||||||
*
|
|
||||||
* Smaller for placeholder data so we don't waste time rendering skeletons
|
|
||||||
*/
|
|
||||||
windowSize={thread.state.isPlaceholderData ? 1 : 7}
|
|
||||||
/**
|
|
||||||
* Default: 10
|
|
||||||
*/
|
|
||||||
maxToRenderPerBatch={5}
|
|
||||||
/**
|
|
||||||
* Default: 50
|
|
||||||
*/
|
|
||||||
updateCellsBatchingPeriod={100}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!gtMobile && canReply && hasSession && (
|
{!gtMobile && canReply && hasSession && (
|
||||||
@@ -640,6 +650,36 @@ export function PostThread({uri}: {uri: string}) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function AnalyticsOnlyFeedFeedbackProvider({
|
||||||
|
children,
|
||||||
|
feedDescriptor,
|
||||||
|
}: React.PropsWithChildren<{
|
||||||
|
feedDescriptor: FeedFeedbackStateContext['feedDescriptor']
|
||||||
|
}>) {
|
||||||
|
/*
|
||||||
|
* Non-anchor posts (parents and replies) are not the post the user tapped
|
||||||
|
* from a feed, so they should not report feed interactions. But they should
|
||||||
|
* still carry the originating feedDescriptor for analytics, so events like
|
||||||
|
* post:clickQuotePost can be attributed to the feed the thread was opened
|
||||||
|
* from. This inert context value provides feedDescriptor while keeping
|
||||||
|
* interaction reporting disabled (enabled: false makes sendInteraction and
|
||||||
|
* onItemSeen no-ops). The anchor renders its own full provider that nests
|
||||||
|
* inside and overrides this for its subtree.
|
||||||
|
*/
|
||||||
|
const value = useMemo<FeedFeedbackStateContext>(
|
||||||
|
() => ({
|
||||||
|
enabled: false,
|
||||||
|
onItemSeen: analyticsOnlyOnItemSeen,
|
||||||
|
sendInteraction: analyticsOnlySendInteraction,
|
||||||
|
feedDescriptor,
|
||||||
|
feedSourceInfo: undefined,
|
||||||
|
}),
|
||||||
|
[feedDescriptor],
|
||||||
|
)
|
||||||
|
|
||||||
|
return <FeedFeedbackProvider value={value}>{children}</FeedFeedbackProvider>
|
||||||
|
}
|
||||||
|
|
||||||
function MobileComposePrompt({onPressReply}: {onPressReply: () => unknown}) {
|
function MobileComposePrompt({onPressReply}: {onPressReply: () => unknown}) {
|
||||||
const {footerHeight} = useShellLayout()
|
const {footerHeight} = useShellLayout()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user