Implement FeedFeedback API (#3498)
* Implement onViewableItemsChanged on List.web.tsx * Introduce onItemSeen to List API * Add FeedFeedback tracker * Add clickthrough interaction tracking * Add engagement interaction tracking * Reduce duplicate sends, introduce a flushAndReset to be triggered on refreshes, and modify the api design a bit * Wire up SDK types and feedContext * Avoid needless function allocations * Fix schema usage * Add show more / show less buttons * Fix minor rendering issue on mobile menu * Wire up sendInteractions() * Fix logic error * Fix: it's item not uri * Update 'seen' to mean 3 seconds on-screen with some significant portion visible * Fix non-reactive debounce * Move methods out * Use a WeakSet for deduping * Reset timeout * 3 -> 2 seconds * Oopsie * Throttle instead * Fix divider * Remove explicit flush calls * Rm unused --------- Co-authored-by: dan <dan.abramov@gmail.com>
This commit is contained in:
parent
e264dfbb87
commit
4fad18b2fa
22 changed files with 516 additions and 64 deletions
|
@ -16,6 +16,7 @@ import {useLingui} from '@lingui/react'
|
|||
import {useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {POST_TOMBSTONE, Shadow, usePostShadow} from '#/state/cache/post-shadow'
|
||||
import {useFeedFeedbackContext} from '#/state/feed-feedback'
|
||||
import {useComposerControls} from '#/state/shell/composer'
|
||||
import {isReasonFeedSource, ReasonFeedSource} from 'lib/api/feed/types'
|
||||
import {MAX_POST_LINES} from 'lib/constants'
|
||||
|
@ -45,6 +46,7 @@ export function FeedItem({
|
|||
post,
|
||||
record,
|
||||
reason,
|
||||
feedContext,
|
||||
moderation,
|
||||
isThreadChild,
|
||||
isThreadLastChild,
|
||||
|
@ -53,6 +55,7 @@ export function FeedItem({
|
|||
post: AppBskyFeedDefs.PostView
|
||||
record: AppBskyFeedPost.Record
|
||||
reason: AppBskyFeedDefs.ReasonRepost | ReasonFeedSource | undefined
|
||||
feedContext: string | undefined
|
||||
moderation: ModerationDecision
|
||||
isThreadChild?: boolean
|
||||
isThreadLastChild?: boolean
|
||||
|
@ -78,6 +81,7 @@ export function FeedItem({
|
|||
post={postShadowed}
|
||||
record={record}
|
||||
reason={reason}
|
||||
feedContext={feedContext}
|
||||
richText={richText}
|
||||
moderation={moderation}
|
||||
isThreadChild={isThreadChild}
|
||||
|
@ -93,6 +97,7 @@ let FeedItemInner = ({
|
|||
post,
|
||||
record,
|
||||
reason,
|
||||
feedContext,
|
||||
richText,
|
||||
moderation,
|
||||
isThreadChild,
|
||||
|
@ -102,6 +107,7 @@ let FeedItemInner = ({
|
|||
post: Shadow<AppBskyFeedDefs.PostView>
|
||||
record: AppBskyFeedPost.Record
|
||||
reason: AppBskyFeedDefs.ReasonRepost | ReasonFeedSource | undefined
|
||||
feedContext: string | undefined
|
||||
richText: RichTextAPI
|
||||
moderation: ModerationDecision
|
||||
isThreadChild?: boolean
|
||||
|
@ -116,6 +122,7 @@ let FeedItemInner = ({
|
|||
const urip = new AtUri(post.uri)
|
||||
return makeProfileLink(post.author, 'post', urip.rkey)
|
||||
}, [post.uri, post.author])
|
||||
const {sendInteraction} = useFeedFeedbackContext()
|
||||
|
||||
const replyAuthorDid = useMemo(() => {
|
||||
if (!record?.reply) {
|
||||
|
@ -126,6 +133,11 @@ let FeedItemInner = ({
|
|||
}, [record?.reply])
|
||||
|
||||
const onPressReply = React.useCallback(() => {
|
||||
sendInteraction({
|
||||
item: post.uri,
|
||||
event: 'app.bsky.feed.defs#interactionReply',
|
||||
feedContext,
|
||||
})
|
||||
openComposer({
|
||||
replyTo: {
|
||||
uri: post.uri,
|
||||
|
@ -136,11 +148,40 @@ let FeedItemInner = ({
|
|||
moderation,
|
||||
},
|
||||
})
|
||||
}, [post, record, openComposer, moderation])
|
||||
}, [post, record, openComposer, moderation, sendInteraction, feedContext])
|
||||
|
||||
const onOpenAuthor = React.useCallback(() => {
|
||||
sendInteraction({
|
||||
item: post.uri,
|
||||
event: 'app.bsky.feed.defs#clickthroughAuthor',
|
||||
feedContext,
|
||||
})
|
||||
}, [sendInteraction, post, feedContext])
|
||||
|
||||
const onOpenReposter = React.useCallback(() => {
|
||||
sendInteraction({
|
||||
item: post.uri,
|
||||
event: 'app.bsky.feed.defs#clickthroughReposter',
|
||||
feedContext,
|
||||
})
|
||||
}, [sendInteraction, post, feedContext])
|
||||
|
||||
const onOpenEmbed = React.useCallback(() => {
|
||||
sendInteraction({
|
||||
item: post.uri,
|
||||
event: 'app.bsky.feed.defs#clickthroughEmbed',
|
||||
feedContext,
|
||||
})
|
||||
}, [sendInteraction, post, feedContext])
|
||||
|
||||
const onBeforePress = React.useCallback(() => {
|
||||
sendInteraction({
|
||||
item: post.uri,
|
||||
event: 'app.bsky.feed.defs#clickthroughItem',
|
||||
feedContext,
|
||||
})
|
||||
precacheProfile(queryClient, post.author)
|
||||
}, [queryClient, post.author])
|
||||
}, [queryClient, post, sendInteraction, feedContext])
|
||||
|
||||
const outerStyles = [
|
||||
styles.outer,
|
||||
|
@ -207,7 +248,8 @@ let FeedItemInner = ({
|
|||
msg`Reposted by ${sanitizeDisplayName(
|
||||
reason.by.displayName || reason.by.handle,
|
||||
)}`,
|
||||
)}>
|
||||
)}
|
||||
onBeforePress={onOpenReposter}>
|
||||
<FontAwesomeIcon
|
||||
icon="retweet"
|
||||
style={{
|
||||
|
@ -235,6 +277,7 @@ let FeedItemInner = ({
|
|||
moderation.ui('displayName'),
|
||||
)}
|
||||
href={makeProfileLink(reason.by)}
|
||||
onBeforePress={onOpenReposter}
|
||||
/>
|
||||
</ProfileHoverCard>
|
||||
</Trans>
|
||||
|
@ -251,6 +294,7 @@ let FeedItemInner = ({
|
|||
profile={post.author}
|
||||
moderation={moderation.ui('avatar')}
|
||||
type={post.author.associated?.labeler ? 'labeler' : 'user'}
|
||||
onBeforePress={onOpenAuthor}
|
||||
/>
|
||||
{isThreadParent && (
|
||||
<View
|
||||
|
@ -272,6 +316,7 @@ let FeedItemInner = ({
|
|||
authorHasWarning={!!post.author.labels?.length}
|
||||
timestamp={post.indexedAt}
|
||||
postHref={href}
|
||||
onOpenAuthor={onOpenAuthor}
|
||||
/>
|
||||
{!isThreadChild && replyAuthorDid !== '' && (
|
||||
<View style={[s.flexRow, s.mb2, s.alignCenter]}>
|
||||
|
@ -308,6 +353,7 @@ let FeedItemInner = ({
|
|||
richText={richText}
|
||||
postEmbed={post.embed}
|
||||
postAuthor={post.author}
|
||||
onOpenEmbed={onOpenEmbed}
|
||||
/>
|
||||
<PostCtrls
|
||||
post={post}
|
||||
|
@ -315,6 +361,7 @@ let FeedItemInner = ({
|
|||
richText={richText}
|
||||
onPressReply={onPressReply}
|
||||
logContext="FeedItem"
|
||||
feedContext={feedContext}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
@ -328,11 +375,13 @@ let PostContent = ({
|
|||
richText,
|
||||
postEmbed,
|
||||
postAuthor,
|
||||
onOpenEmbed,
|
||||
}: {
|
||||
moderation: ModerationDecision
|
||||
richText: RichTextAPI
|
||||
postEmbed: AppBskyFeedDefs.PostView['embed']
|
||||
postAuthor: AppBskyFeedDefs.PostView['author']
|
||||
onOpenEmbed: () => void
|
||||
}): React.ReactNode => {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
|
@ -373,7 +422,11 @@ let PostContent = ({
|
|||
) : undefined}
|
||||
{postEmbed ? (
|
||||
<View style={[a.pb_sm]}>
|
||||
<PostEmbeds embed={postEmbed} moderation={moderation} />
|
||||
<PostEmbeds
|
||||
embed={postEmbed}
|
||||
moderation={moderation}
|
||||
onOpen={onOpenEmbed}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
</ContentHider>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue