Pass correct log context to suggested user metrics (#10074)

(cherry picked from commit d44060a34e)
This commit is contained in:
DS Boyce
2026-03-25 15:13:23 -07:00
committed by Eric Bailey
parent fc402f1b7e
commit 222954b397
3 changed files with 98 additions and 76 deletions
+17 -12
View File
@@ -480,10 +480,12 @@ export type Events = {
'suggestedUser:follow': {
logContext:
| 'Explore'
| 'InterstitialDiscover'
| 'InterstitialProfile'
| 'Profile'
| 'DiscoverInterstitial'
| 'ProfileInterstitial'
| 'ProfileHeader'
| 'Onboarding'
| 'SeeMoreSuggestedUsers'
| 'ProgressGuide'
location: 'Card' | 'Profile' | 'FollowAll'
recId?: number | string
position: number
@@ -493,9 +495,11 @@ export type Events = {
'suggestedUser:press': {
logContext:
| 'Explore'
| 'InterstitialDiscover'
| 'InterstitialProfile'
| 'DiscoverInterstitial'
| 'ProfileInterstitial'
| 'ProfileHeader'
| 'Onboarding'
| 'SeeMoreSuggestedUsers'
recId?: number | string
position: number
suggestedDid: string
@@ -504,10 +508,11 @@ export type Events = {
'suggestedUser:seen': {
logContext:
| 'Explore'
| 'InterstitialDiscover'
| 'InterstitialProfile'
| 'Profile'
| 'DiscoverInterstitial'
| 'ProfileInterstitial'
| 'ProfileHeader'
| 'Onboarding'
| 'SeeMoreSuggestedUsers'
| 'ProgressGuide'
recId?: number | string
position: number
@@ -517,14 +522,14 @@ export type Events = {
'suggestedUser:seeMore': {
logContext:
| 'Explore'
| 'InterstitialDiscover'
| 'InterstitialProfile'
| 'Profile'
| 'DiscoverInterstitial'
| 'ProfileInterstitial'
| 'ProfileHeader'
| 'Onboarding'
recId?: number | string
}
'suggestedUser:dismiss': {
logContext: 'InterstitialDiscover' | 'InterstitialProfile'
logContext: 'DiscoverInterstitial' | 'ProfileInterstitial' | 'ProfileHeader'
recId?: number | string
position: number
suggestedDid: string
+19 -29
View File
@@ -8,9 +8,7 @@ import Animated, {
LinearTransition,
} from 'react-native-reanimated'
import {type AppBskyFeedDefs, AtUri} from '@atproto/api'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
import {Trans, useLingui} from '@lingui/react/macro'
import {useNavigation} from '@react-navigation/native'
import {type NavigationProp} from '#/lib/routes/types'
@@ -295,7 +293,7 @@ export function ProfileGrid({
}) {
const t = useTheme()
const ax = useAnalytics()
const {_} = useLingui()
const {t: l} = useLingui()
const moderationOpts = useModerationOpts()
const {gtMobile} = useBreakpoints()
const followDialogControl = useDialogControl()
@@ -312,10 +310,10 @@ export function ProfileGrid({
const containerRef = useRef<View>(null)
const hasTrackedRef = useRef(false)
const logContext: Metrics['suggestedUser:seen']['logContext'] = isFeedContext
? 'InterstitialDiscover'
? 'DiscoverInterstitial'
: isProfileHeaderContext
? 'Profile'
: 'InterstitialProfile'
? 'ProfileHeader'
: 'ProfileInterstitial'
// Callback to fire seen events
const fireSeen = useCallback(() => {
@@ -336,7 +334,7 @@ export function ProfileGrid({
})
}
})
}, [ax, isLoading, error, profiles, maxLength, logContext])
}, [isLoading, error, profiles, maxLength, ax, logContext])
// For profile header, fire when isVisible becomes true
useEffect(() => {
@@ -424,9 +422,7 @@ export function ProfileGrid({
profile={profile.actor}
onPress={() => {
ax.metric('suggestedUser:press', {
logContext: isFeedContext
? 'InterstitialDiscover'
: 'InterstitialProfile',
logContext,
recId: profile.recId,
position: index,
suggestedDid: profile.actor.did,
@@ -442,14 +438,12 @@ export function ProfileGrid({
<ProfileCard.Outer>
{onDismiss && (
<Button
label={_(msg`Dismiss this suggestion`)}
label={l`Dismiss this suggestion`}
onPress={e => {
e.preventDefault()
onDismiss(profile.actor.did)
ax.metric('suggestedUser:dismiss', {
logContext: isFeedContext
? 'InterstitialDiscover'
: 'InterstitialProfile',
logContext,
position: index,
suggestedDid: profile.actor.did,
recId: profile.recId,
@@ -515,10 +509,8 @@ export function ProfileGrid({
style={[a.rounded_sm]}
onFollow={() => {
ax.metric('suggestedUser:follow', {
logContext: isFeedContext
? 'InterstitialDiscover'
: 'InterstitialProfile',
location: 'Card',
logContext,
location: 'Profile',
recId: profile.recId,
position: index,
suggestedDid: profile.actor.did,
@@ -570,11 +562,11 @@ export function ProfileGrid({
</Text>
{!isProfileHeaderContext && (
<Button
label={_(msg`See more suggested profiles`)}
label={l`See more suggested profiles`}
onPress={() => {
followDialogControl.open()
ax.metric('suggestedUser:seeMore', {
logContext: isFeedContext ? 'Explore' : 'Profile',
logContext,
recId,
})
}}>
@@ -595,9 +587,7 @@ export function ProfileGrid({
</Button>
)}
</View>
<FollowDialogWithoutGuide control={followDialogControl} />
<LayoutAnimationConfig skipExiting skipEntering>
{gtMobile ? (
<View style={[a.p_lg, a.pt_md]}>
@@ -620,7 +610,7 @@ export function ProfileGrid({
onPress={() => {
followDialogControl.open()
ax.metric('suggestedUser:seeMore', {
logContext: 'Explore',
logContext,
})
}}
/>
@@ -634,11 +624,11 @@ export function ProfileGrid({
}
function SeeMoreSuggestedProfilesCard({onPress}: {onPress: () => void}) {
const {_} = useLingui()
const {t: l} = useLingui()
return (
<Button
label={_(msg`Browse more accounts`)}
label={l`Browse more accounts`}
onPress={onPress}
style={[
a.flex_col,
@@ -662,7 +652,7 @@ const numFeedsToDisplay = 3
export function SuggestedFeeds() {
const t = useTheme()
const ax = useAnalytics()
const {_} = useLingui()
const {t: l} = useLingui()
const {data, isLoading, error} = useGetPopularFeedsQuery({
limit: numFeedsToDisplay,
})
@@ -749,7 +739,7 @@ export function SuggestedFeeds() {
a.gap_md,
]}>
<InlineLinkText
label={_(msg`Browse more suggestions`)}
label={l`Browse more suggestions`}
to="/search"
style={[t.atoms.text_contrast_medium]}>
<Trans>Browse more suggestions</Trans>
@@ -768,7 +758,7 @@ export function SuggestedFeeds() {
{content}
<Button
label={_(msg`Browse more feeds on the Explore page`)}
label={l`Browse more feeds on the Explore page`}
onPress={() => {
navigation.navigate('SearchTab')
}}
+62 -35
View File
@@ -1,9 +1,7 @@
import {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'
import {TextInput, View, type ViewToken} from 'react-native'
import {type ModerationOpts} from '@atproto/api'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
import {Trans, useLingui} from '@lingui/react/macro'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
import {popularInterests, useInterestsDisplayNames} from '#/lib/interests'
@@ -64,14 +62,14 @@ export function FollowDialog({
showArrow?: boolean
}) {
const ax = useAnalytics()
const {_} = useLingui()
const {t: l} = useLingui()
const control = Dialog.useDialogControl()
const {gtPhone} = useBreakpoints()
return (
<>
<Button
label={_(msg`Find people to follow`)}
label={l`Find people to follow`}
onPress={() => {
control.open()
ax.metric('progressGuide:followDialog:open', {})
@@ -112,7 +110,7 @@ let lastSelectedInterest = ''
let lastSearchText = ''
function DialogInner({guide}: {guide?: Follow10ProgressGuide}) {
const {_} = useLingui()
const {t: l} = useLingui()
const ax = useAnalytics()
const interestsDisplayNames = useInterestsDisplayNames()
const {data: preferences} = usePreferencesQuery()
@@ -182,7 +180,7 @@ function DialogInner({guide}: {guide?: Follow10ProgressGuide}) {
_items.push({
type: 'empty',
key: 'empty',
message: _(msg`We're having network issues, try again`),
message: l`We're having network issues, try again`,
})
} else {
const seen = new Set<string>()
@@ -208,12 +206,12 @@ function DialogInner({guide}: {guide?: Follow10ProgressGuide}) {
!_items.length &&
!isSearchResultsError
) {
_items.push({type: 'empty', key: 'empty', message: _(msg`No results`)})
_items.push({type: 'empty', key: 'empty', message: l`No results`})
}
return _items
}, [
_,
l,
suggestions,
suggestionsError,
isFetchingSuggestions,
@@ -226,6 +224,9 @@ function DialogInner({guide}: {guide?: Follow10ProgressGuide}) {
isSearchResultsError,
])
const isGuide = Boolean(guide)
const recIdForLogging = hasSearchText ? undefined : suggestions?.recId
const renderItems = useCallback(
({item, index}: {item: Item; index: number}) => {
switch (item.type) {
@@ -235,6 +236,9 @@ function DialogInner({guide}: {guide?: Follow10ProgressGuide}) {
profile={item.profile}
moderationOpts={moderationOpts!}
noBorder={index === 0}
position={index}
recId={recIdForLogging}
isGuide={isGuide}
/>
)
}
@@ -248,7 +252,7 @@ function DialogInner({guide}: {guide?: Follow10ProgressGuide}) {
return null
}
},
[moderationOpts],
[moderationOpts, recIdForLogging, isGuide],
)
// Track seen profiles
@@ -269,8 +273,8 @@ function DialogInner({guide}: {guide?: Follow10ProgressGuide}) {
i => i.type === 'profile' && i.profile.did === item.profile.did,
)
ax.metric('suggestedUser:seen', {
logContext: 'ProgressGuide',
recId: hasSearchText ? undefined : suggestions?.recId,
logContext: isGuide ? 'ProgressGuide' : 'SeeMoreSuggestedUsers',
recId: recIdForLogging,
position: position !== -1 ? position : 0,
suggestedDid: item.profile.did,
category: selectedInterestRef.current,
@@ -404,7 +408,7 @@ let Header = ({
Header = memo(Header)
function HeaderTop({guide}: {guide?: Follow10ProgressGuide}) {
const {_} = useLingui()
const {t: l} = useLingui()
const t = useTheme()
const control = Dialog.useDialogContext()
return (
@@ -438,7 +442,7 @@ function HeaderTop({guide}: {guide?: Follow10ProgressGuide}) {
)}
{IS_WEB ? (
<Button
label={_(msg`Close`)}
label={l`Close`}
size="small"
shape="round"
variant={IS_WEB ? 'ghost' : 'solid'}
@@ -474,22 +478,18 @@ let Tab = ({
onLayout: (index: number, x: number, width: number) => void
}): React.ReactNode => {
const t = useTheme()
const {_} = useLingui()
const {t: l} = useLingui()
const label = active
? _(
msg({
message: `Search for "${interestsDisplayName}" (active)`,
comment:
'Accessibility label for a tab that searches for accounts in a category (e.g. Art, Video Games, Sports, etc.) that are suggested for the user to follow. The tab is currently selected.',
}),
)
: _(
msg({
message: `Search for "${interestsDisplayName}"`,
comment:
'Accessibility label for a tab that searches for accounts in a category (e.g. Art, Video Games, Sports, etc.) that are suggested for the user to follow. The tab is not currently active and can be selected.',
}),
)
? l({
message: `Search for "${interestsDisplayName}" (active)`,
comment:
'Accessibility label for a tab that searches for accounts in a category (e.g. Art, Video Games, Sports, etc.) that are suggested for the user to follow. The tab is currently selected.',
})
: l({
message: `Search for "${interestsDisplayName}"`,
comment:
'Accessibility label for a tab that searches for accounts in a category (e.g. Art, Video Games, Sports, etc.) that are suggested for the user to follow. The tab is not currently active and can be selected.',
})
return (
<View
key={interest}
@@ -532,16 +532,25 @@ let FollowProfileCard = ({
profile,
moderationOpts,
noBorder,
position,
recId,
isGuide,
}: {
profile: bsky.profile.AnyProfileView
moderationOpts: ModerationOpts
noBorder?: boolean
position: number
recId?: string
isGuide: boolean
}): React.ReactNode => {
return (
<FollowProfileCardInner
profile={profile}
moderationOpts={moderationOpts}
noBorder={noBorder}
position={position}
recId={recId}
isGuide={isGuide}
/>
)
}
@@ -552,14 +561,21 @@ function FollowProfileCardInner({
moderationOpts,
onFollow,
noBorder,
position,
recId,
isGuide,
}: {
profile: bsky.profile.AnyProfileView
moderationOpts: ModerationOpts
onFollow?: () => void
noBorder?: boolean
position: number
recId?: string
isGuide: boolean
}) {
const control = Dialog.useDialogContext()
const t = useTheme()
const ax = useAnalytics()
return (
<ProfileCard.Link
profile={profile}
@@ -588,7 +604,19 @@ function FollowProfileCardInner({
moderationOpts={moderationOpts}
logContext="PostOnboardingFindFollows"
shape="round"
onPress={onFollow}
onPress={() => {
ax.metric('suggestedUser:follow', {
logContext: isGuide
? 'ProgressGuide'
: 'SeeMoreSuggestedUsers',
location: 'Card',
recId,
position,
suggestedDid: profile.did,
category: null,
})
onFollow?.()
}}
colorInverted
/>
</ProfileCard.Header>
@@ -632,7 +660,7 @@ function SearchInput({
defaultValue: string
}) {
const t = useTheme()
const {_} = useLingui()
const {t: l} = useLingui()
const {
state: hovered,
onIn: onMouseEnter,
@@ -652,10 +680,9 @@ function SearchInput({
size="md"
fill={interacted ? t.palette.primary_500 : t.palette.contrast_300}
/>
<TextInput
ref={inputRef}
placeholder={_(msg`Search by name or interest`)}
placeholder={l`Search by name or interest`}
defaultValue={defaultValue}
onChangeText={onChangeText}
onFocus={onFocus}
@@ -674,8 +701,8 @@ function SearchInput({
autoCorrect={false}
autoComplete="off"
autoCapitalize="none"
accessibilityLabel={_(msg`Search profiles`)}
accessibilityHint={_(msg`Searches for profiles`)}
accessibilityLabel={l`Search profiles`}
accessibilityHint={l`Searches for profiles`}
/>
</View>
)