Profile follow client events (#9385)
* Add parameters to profile:follow Track who was followed, whose profile generated the follow, and the position of the person who was followed in the list * Add profileCard:seen event * Don't send "profileCard:seen" event to Statsig * Clean up * prevent overzealous clearing --------- Co-authored-by: Samuel Newman <mozzius@protonmail.com>
This commit is contained in:
@@ -9,10 +9,14 @@ export function ProfileCardWithFollowBtn({
|
||||
profile,
|
||||
noBorder,
|
||||
logContext = 'ProfileCard',
|
||||
position,
|
||||
contextProfileDid,
|
||||
}: {
|
||||
profile: AppBskyActorDefs.ProfileView
|
||||
noBorder?: boolean
|
||||
logContext?: 'ProfileCard' | 'StarterPackProfilesList'
|
||||
position?: number
|
||||
contextProfileDid?: string
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const moderationOpts = useModerationOpts()
|
||||
@@ -30,6 +34,8 @@ export function ProfileCardWithFollowBtn({
|
||||
profile={profile}
|
||||
moderationOpts={moderationOpts}
|
||||
logContext={logContext}
|
||||
position={position}
|
||||
contextProfileDid={contextProfileDid}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
|
||||
@@ -16,15 +16,19 @@ import {ProfileCardWithFollowBtn} from './ProfileCard'
|
||||
function renderItem({
|
||||
item,
|
||||
index,
|
||||
contextProfileDid,
|
||||
}: {
|
||||
item: ActorDefs.ProfileView
|
||||
index: number
|
||||
contextProfileDid: string | undefined
|
||||
}) {
|
||||
return (
|
||||
<ProfileCardWithFollowBtn
|
||||
key={item.did}
|
||||
profile={item}
|
||||
noBorder={index === 0}
|
||||
position={index + 1}
|
||||
contextProfileDid={contextProfileDid}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -83,6 +87,40 @@ export function ProfileFollowers({name}: {name: string}) {
|
||||
}
|
||||
}, [isFetchingNextPage, hasNextPage, error, fetchNextPage])
|
||||
|
||||
const renderItemWithContext = React.useCallback(
|
||||
({item, index}: {item: ActorDefs.ProfileView; index: number}) =>
|
||||
renderItem({item, index, contextProfileDid: resolvedDid}),
|
||||
[resolvedDid],
|
||||
)
|
||||
|
||||
// track seen items
|
||||
const seenItemsRef = React.useRef<Set<string>>(new Set())
|
||||
React.useEffect(() => {
|
||||
seenItemsRef.current.clear()
|
||||
}, [resolvedDid])
|
||||
const onItemSeen = React.useCallback(
|
||||
(item: ActorDefs.ProfileView) => {
|
||||
if (seenItemsRef.current.has(item.did)) {
|
||||
return
|
||||
}
|
||||
seenItemsRef.current.add(item.did)
|
||||
const position = followers.findIndex(p => p.did === item.did) + 1
|
||||
if (position === 0) {
|
||||
return
|
||||
}
|
||||
logger.metric(
|
||||
'profileCard:seen',
|
||||
{
|
||||
profileDid: item.did,
|
||||
position,
|
||||
...(resolvedDid !== undefined && {contextProfileDid: resolvedDid}),
|
||||
},
|
||||
{statsig: false},
|
||||
)
|
||||
},
|
||||
[followers, resolvedDid],
|
||||
)
|
||||
|
||||
if (followers.length < 1) {
|
||||
return (
|
||||
<ListMaybePlaceholder
|
||||
@@ -104,12 +142,13 @@ export function ProfileFollowers({name}: {name: string}) {
|
||||
return (
|
||||
<List
|
||||
data={followers}
|
||||
renderItem={renderItem}
|
||||
renderItem={renderItemWithContext}
|
||||
keyExtractor={keyExtractor}
|
||||
refreshing={isPTRing}
|
||||
onRefresh={onRefresh}
|
||||
onEndReached={onEndReached}
|
||||
onEndReachedThreshold={4}
|
||||
onItemSeen={onItemSeen}
|
||||
ListFooterComponent={
|
||||
<ListFooter
|
||||
isFetchingNextPage={isFetchingNextPage}
|
||||
|
||||
@@ -16,15 +16,19 @@ import {ProfileCardWithFollowBtn} from './ProfileCard'
|
||||
function renderItem({
|
||||
item,
|
||||
index,
|
||||
contextProfileDid,
|
||||
}: {
|
||||
item: ActorDefs.ProfileView
|
||||
index: number
|
||||
contextProfileDid: string | undefined
|
||||
}) {
|
||||
return (
|
||||
<ProfileCardWithFollowBtn
|
||||
key={item.did}
|
||||
profile={item}
|
||||
noBorder={index === 0}
|
||||
position={index + 1}
|
||||
contextProfileDid={contextProfileDid}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -83,6 +87,40 @@ export function ProfileFollows({name}: {name: string}) {
|
||||
}
|
||||
}, [error, fetchNextPage, hasNextPage, isFetchingNextPage])
|
||||
|
||||
const renderItemWithContext = React.useCallback(
|
||||
({item, index}: {item: ActorDefs.ProfileView; index: number}) =>
|
||||
renderItem({item, index, contextProfileDid: resolvedDid}),
|
||||
[resolvedDid],
|
||||
)
|
||||
|
||||
// track seen items
|
||||
const seenItemsRef = React.useRef<Set<string>>(new Set())
|
||||
React.useEffect(() => {
|
||||
seenItemsRef.current.clear()
|
||||
}, [resolvedDid])
|
||||
const onItemSeen = React.useCallback(
|
||||
(item: ActorDefs.ProfileView) => {
|
||||
if (seenItemsRef.current.has(item.did)) {
|
||||
return
|
||||
}
|
||||
seenItemsRef.current.add(item.did)
|
||||
const position = follows.findIndex(p => p.did === item.did) + 1
|
||||
if (position === 0) {
|
||||
return
|
||||
}
|
||||
logger.metric(
|
||||
'profileCard:seen',
|
||||
{
|
||||
profileDid: item.did,
|
||||
position,
|
||||
...(resolvedDid !== undefined && {contextProfileDid: resolvedDid}),
|
||||
},
|
||||
{statsig: false},
|
||||
)
|
||||
},
|
||||
[follows, resolvedDid],
|
||||
)
|
||||
|
||||
if (follows.length < 1) {
|
||||
return (
|
||||
<ListMaybePlaceholder
|
||||
@@ -104,12 +142,13 @@ export function ProfileFollows({name}: {name: string}) {
|
||||
return (
|
||||
<List
|
||||
data={follows}
|
||||
renderItem={renderItem}
|
||||
renderItem={renderItemWithContext}
|
||||
keyExtractor={keyExtractor}
|
||||
refreshing={isPTRing}
|
||||
onRefresh={onRefresh}
|
||||
onEndReached={onEndReached}
|
||||
onEndReachedThreshold={4}
|
||||
onItemSeen={onItemSeen}
|
||||
ListFooterComponent={
|
||||
<ListFooter
|
||||
isFetchingNextPage={isFetchingNextPage}
|
||||
|
||||
Reference in New Issue
Block a user