Hook up suggestedUser:seen client events (#9468)
* Hook up suggestedUser:seen client events * Fix crash when clicking "find people to follow" * While we're at it, fix the position of the X button on the "find people to follow" modal * Add suggestedDid and category attributes to suggestedUser client events --------- Co-authored-by: Eric Bailey <git@esb.lol>
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react'
|
import React, {useCallback, useEffect, useRef} from 'react'
|
||||||
import {ScrollView, View} from 'react-native'
|
import {ScrollView, View} from 'react-native'
|
||||||
import {type AppBskyFeedDefs, AtUri} from '@atproto/api'
|
import {type AppBskyFeedDefs, AtUri} from '@atproto/api'
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
@@ -8,6 +8,7 @@ import {useNavigation} from '@react-navigation/native'
|
|||||||
import {type NavigationProp} from '#/lib/routes/types'
|
import {type NavigationProp} from '#/lib/routes/types'
|
||||||
import {logEvent} from '#/lib/statsig/statsig'
|
import {logEvent} from '#/lib/statsig/statsig'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
|
import {type MetricEvents} from '#/logger/metrics'
|
||||||
import {isIOS} from '#/platform/detection'
|
import {isIOS} from '#/platform/detection'
|
||||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||||
import {useGetPopularFeedsQuery} from '#/state/queries/feed'
|
import {useGetPopularFeedsQuery} from '#/state/queries/feed'
|
||||||
@@ -241,12 +242,14 @@ export function ProfileGrid({
|
|||||||
profiles,
|
profiles,
|
||||||
recId,
|
recId,
|
||||||
viewContext = 'feed',
|
viewContext = 'feed',
|
||||||
|
isVisible = true,
|
||||||
}: {
|
}: {
|
||||||
isSuggestionsLoading: boolean
|
isSuggestionsLoading: boolean
|
||||||
profiles: bsky.profile.AnyProfileView[]
|
profiles: bsky.profile.AnyProfileView[]
|
||||||
recId?: number
|
recId?: number
|
||||||
error: Error | null
|
error: Error | null
|
||||||
viewContext: 'profile' | 'profileHeader' | 'feed'
|
viewContext: 'profile' | 'profileHeader' | 'feed'
|
||||||
|
isVisible?: boolean
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
@@ -261,6 +264,84 @@ export function ProfileGrid({
|
|||||||
const maxLength = gtMobile ? 3 : isProfileHeaderContext ? 12 : 6
|
const maxLength = gtMobile ? 3 : isProfileHeaderContext ? 12 : 6
|
||||||
const minLength = gtMobile ? 3 : 4
|
const minLength = gtMobile ? 3 : 4
|
||||||
|
|
||||||
|
// Track seen profiles
|
||||||
|
const seenProfilesRef = useRef<Set<string>>(new Set())
|
||||||
|
const containerRef = useRef<View>(null)
|
||||||
|
const hasTrackedRef = useRef(false)
|
||||||
|
const logContext: MetricEvents['suggestedUser:seen']['logContext'] =
|
||||||
|
isFeedContext
|
||||||
|
? 'InterstitialDiscover'
|
||||||
|
: isProfileHeaderContext
|
||||||
|
? 'Profile'
|
||||||
|
: 'InterstitialProfile'
|
||||||
|
|
||||||
|
// Callback to fire seen events
|
||||||
|
const fireSeen = useCallback(() => {
|
||||||
|
if (isLoading || error || !profiles.length) return
|
||||||
|
if (hasTrackedRef.current) return
|
||||||
|
hasTrackedRef.current = true
|
||||||
|
|
||||||
|
const profilesToShow = profiles.slice(0, maxLength)
|
||||||
|
profilesToShow.forEach((profile, index) => {
|
||||||
|
if (!seenProfilesRef.current.has(profile.did)) {
|
||||||
|
seenProfilesRef.current.add(profile.did)
|
||||||
|
logger.metric(
|
||||||
|
'suggestedUser:seen',
|
||||||
|
{
|
||||||
|
logContext,
|
||||||
|
recId,
|
||||||
|
position: index,
|
||||||
|
suggestedDid: profile.did,
|
||||||
|
category: null,
|
||||||
|
},
|
||||||
|
{statsig: true},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, [isLoading, error, profiles, maxLength, logContext, recId])
|
||||||
|
|
||||||
|
// For profile header, fire when isVisible becomes true
|
||||||
|
useEffect(() => {
|
||||||
|
if (isProfileHeaderContext) {
|
||||||
|
if (!isVisible) {
|
||||||
|
hasTrackedRef.current = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fireSeen()
|
||||||
|
}
|
||||||
|
}, [isVisible, isProfileHeaderContext, fireSeen])
|
||||||
|
|
||||||
|
// For feed interstitials, use IntersectionObserver to detect actual visibility
|
||||||
|
useEffect(() => {
|
||||||
|
if (isProfileHeaderContext) return // handled above
|
||||||
|
if (isLoading || error || !profiles.length) return
|
||||||
|
|
||||||
|
const node = containerRef.current
|
||||||
|
if (!node) return
|
||||||
|
|
||||||
|
// Use IntersectionObserver on web to detect when actually visible
|
||||||
|
if (typeof IntersectionObserver !== 'undefined') {
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
entries => {
|
||||||
|
if (entries[0]?.isIntersecting) {
|
||||||
|
fireSeen()
|
||||||
|
observer.disconnect()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{threshold: 0.5},
|
||||||
|
)
|
||||||
|
// @ts-ignore - web only
|
||||||
|
observer.observe(node)
|
||||||
|
return () => observer.disconnect()
|
||||||
|
} else {
|
||||||
|
// On native, delay slightly to account for layout shifts during hydration
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
fireSeen()
|
||||||
|
}, 500)
|
||||||
|
return () => clearTimeout(timeout)
|
||||||
|
}
|
||||||
|
}, [isProfileHeaderContext, isLoading, error, profiles.length, fireSeen])
|
||||||
|
|
||||||
const content = isLoading
|
const content = isLoading
|
||||||
? Array(maxLength)
|
? Array(maxLength)
|
||||||
.fill(0)
|
.fill(0)
|
||||||
@@ -292,6 +373,8 @@ export function ProfileGrid({
|
|||||||
: 'InterstitialProfile',
|
: 'InterstitialProfile',
|
||||||
recId,
|
recId,
|
||||||
position: index,
|
position: index,
|
||||||
|
suggestedDid: profile.did,
|
||||||
|
category: null,
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
style={[
|
style={[
|
||||||
@@ -352,6 +435,8 @@ export function ProfileGrid({
|
|||||||
location: 'Card',
|
location: 'Card',
|
||||||
recId,
|
recId,
|
||||||
position: index,
|
position: index,
|
||||||
|
suggestedDid: profile.did,
|
||||||
|
category: null,
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -368,6 +453,7 @@ export function ProfileGrid({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
|
ref={containerRef}
|
||||||
style={[
|
style={[
|
||||||
!isProfileHeaderContext && a.border_t,
|
!isProfileHeaderContext && a.border_t,
|
||||||
t.atoms.border_contrast_low,
|
t.atoms.border_contrast_low,
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
import {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'
|
import {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'
|
||||||
import {TextInput, useWindowDimensions, View} from 'react-native'
|
import {
|
||||||
|
TextInput,
|
||||||
|
useWindowDimensions,
|
||||||
|
View,
|
||||||
|
type ViewToken,
|
||||||
|
} from 'react-native'
|
||||||
import {type ModerationOpts} from '@atproto/api'
|
import {type ModerationOpts} from '@atproto/api'
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
import {popularInterests, useInterestsDisplayNames} from '#/lib/interests'
|
import {popularInterests, useInterestsDisplayNames} from '#/lib/interests'
|
||||||
import {logEvent} from '#/lib/statsig/statsig'
|
import {logEvent} from '#/lib/statsig/statsig'
|
||||||
|
import {logger} from '#/logger'
|
||||||
import {isWeb} from '#/platform/detection'
|
import {isWeb} from '#/platform/detection'
|
||||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||||
import {useActorSearch} from '#/state/queries/actor-search'
|
import {useActorSearch} from '#/state/queries/actor-search'
|
||||||
@@ -243,6 +249,43 @@ function DialogInner({guide}: {guide?: Follow10ProgressGuide}) {
|
|||||||
[moderationOpts],
|
[moderationOpts],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Track seen profiles
|
||||||
|
const seenProfilesRef = useRef<Set<string>>(new Set())
|
||||||
|
const itemsRef = useRef(items)
|
||||||
|
itemsRef.current = items
|
||||||
|
const selectedInterestRef = useRef(selectedInterest)
|
||||||
|
selectedInterestRef.current = selectedInterest
|
||||||
|
|
||||||
|
const onViewableItemsChanged = useRef(
|
||||||
|
({viewableItems}: {viewableItems: ViewToken[]}) => {
|
||||||
|
for (const viewableItem of viewableItems) {
|
||||||
|
const item = viewableItem.item as Item
|
||||||
|
if (item.type === 'profile') {
|
||||||
|
if (!seenProfilesRef.current.has(item.profile.did)) {
|
||||||
|
seenProfilesRef.current.add(item.profile.did)
|
||||||
|
const position = itemsRef.current.findIndex(
|
||||||
|
i => i.type === 'profile' && i.profile.did === item.profile.did,
|
||||||
|
)
|
||||||
|
logger.metric(
|
||||||
|
'suggestedUser:seen',
|
||||||
|
{
|
||||||
|
logContext: 'ProgressGuide',
|
||||||
|
recId: undefined,
|
||||||
|
position: position !== -1 ? position : 0,
|
||||||
|
suggestedDid: item.profile.did,
|
||||||
|
category: selectedInterestRef.current,
|
||||||
|
},
|
||||||
|
{statsig: true},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
).current
|
||||||
|
const viewabilityConfig = useRef({
|
||||||
|
itemVisiblePercentThreshold: 50,
|
||||||
|
}).current
|
||||||
|
|
||||||
const onSelectTab = useCallback(
|
const onSelectTab = useCallback(
|
||||||
(interest: string) => {
|
(interest: string) => {
|
||||||
setSelectedInterest(interest)
|
setSelectedInterest(interest)
|
||||||
@@ -290,6 +333,8 @@ function DialogInner({guide}: {guide?: Follow10ProgressGuide}) {
|
|||||||
scrollIndicatorInsets={{top: headerHeight}}
|
scrollIndicatorInsets={{top: headerHeight}}
|
||||||
initialNumToRender={8}
|
initialNumToRender={8}
|
||||||
maxToRenderPerBatch={8}
|
maxToRenderPerBatch={8}
|
||||||
|
onViewableItemsChanged={onViewableItemsChanged}
|
||||||
|
viewabilityConfig={viewabilityConfig}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -400,7 +445,7 @@ function HeaderTop({guide}: {guide?: Follow10ProgressGuide}) {
|
|||||||
style={[
|
style={[
|
||||||
a.absolute,
|
a.absolute,
|
||||||
a.z_20,
|
a.z_20,
|
||||||
web({right: -4}),
|
web({right: 8}),
|
||||||
native({right: 0}),
|
native({right: 0}),
|
||||||
native({height: 32, width: 32, borderRadius: 16}),
|
native({height: 32, width: 32, borderRadius: 16}),
|
||||||
]}
|
]}
|
||||||
|
|||||||
+13
-1
@@ -334,6 +334,8 @@ export type MetricEvents = {
|
|||||||
location: 'Card' | 'Profile'
|
location: 'Card' | 'Profile'
|
||||||
recId?: number
|
recId?: number
|
||||||
position: number
|
position: number
|
||||||
|
suggestedDid: string
|
||||||
|
category: string | null
|
||||||
}
|
}
|
||||||
'suggestedUser:press': {
|
'suggestedUser:press': {
|
||||||
logContext:
|
logContext:
|
||||||
@@ -343,11 +345,21 @@ export type MetricEvents = {
|
|||||||
| 'Onboarding'
|
| 'Onboarding'
|
||||||
recId?: number
|
recId?: number
|
||||||
position: number
|
position: number
|
||||||
|
suggestedDid: string
|
||||||
|
category: string | null
|
||||||
}
|
}
|
||||||
'suggestedUser:seen': {
|
'suggestedUser:seen': {
|
||||||
logContext: 'Explore' | 'InterstitialDiscover' | 'InterstitialProfile'
|
logContext:
|
||||||
|
| 'Explore'
|
||||||
|
| 'InterstitialDiscover'
|
||||||
|
| 'InterstitialProfile'
|
||||||
|
| 'Profile'
|
||||||
|
| 'Onboarding'
|
||||||
|
| 'ProgressGuide'
|
||||||
recId?: number
|
recId?: number
|
||||||
position: number
|
position: number
|
||||||
|
suggestedDid: string
|
||||||
|
category: string | null
|
||||||
}
|
}
|
||||||
'suggestedUser:seeMore': {
|
'suggestedUser:seeMore': {
|
||||||
logContext:
|
logContext:
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
import {useContext, useMemo, useState} from 'react'
|
import {
|
||||||
|
useCallback,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {type ModerationOpts} from '@atproto/api'
|
import {type ModerationOpts} from '@atproto/api'
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
@@ -123,6 +130,28 @@ export function StepSuggestedAccounts() {
|
|||||||
|
|
||||||
const canFollowAll = followableDids.length > 0 && !isFollowingAll
|
const canFollowAll = followableDids.length > 0 && !isFollowingAll
|
||||||
|
|
||||||
|
// Track seen profiles - shared ref across all cards
|
||||||
|
const seenProfilesRef = useRef<Set<string>>(new Set())
|
||||||
|
const onProfileSeen = useCallback(
|
||||||
|
(did: string, position: number) => {
|
||||||
|
if (!seenProfilesRef.current.has(did)) {
|
||||||
|
seenProfilesRef.current.add(did)
|
||||||
|
logger.metric(
|
||||||
|
'suggestedUser:seen',
|
||||||
|
{
|
||||||
|
logContext: 'Onboarding',
|
||||||
|
recId: undefined,
|
||||||
|
position,
|
||||||
|
suggestedDid: did,
|
||||||
|
category: selectedInterest,
|
||||||
|
},
|
||||||
|
{statsig: true},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[selectedInterest],
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[a.align_start]} testID="onboardingInterests">
|
<View style={[a.align_start]} testID="onboardingInterests">
|
||||||
<Text style={[a.font_bold, a.text_3xl]}>
|
<Text style={[a.font_bold, a.text_3xl]}>
|
||||||
@@ -193,6 +222,8 @@ export function StepSuggestedAccounts() {
|
|||||||
profile={user}
|
profile={user}
|
||||||
moderationOpts={moderationOpts}
|
moderationOpts={moderationOpts}
|
||||||
position={index}
|
position={index}
|
||||||
|
category={selectedInterest}
|
||||||
|
onSeen={onProfileSeen}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
@@ -303,14 +334,52 @@ function SuggestedProfileCard({
|
|||||||
profile,
|
profile,
|
||||||
moderationOpts,
|
moderationOpts,
|
||||||
position,
|
position,
|
||||||
|
category,
|
||||||
|
onSeen,
|
||||||
}: {
|
}: {
|
||||||
profile: bsky.profile.AnyProfileView
|
profile: bsky.profile.AnyProfileView
|
||||||
moderationOpts: ModerationOpts
|
moderationOpts: ModerationOpts
|
||||||
position: number
|
position: number
|
||||||
|
category: string | null
|
||||||
|
onSeen: (did: string, position: number) => void
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
|
const cardRef = useRef<View>(null)
|
||||||
|
const hasTrackedRef = useRef(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const node = cardRef.current
|
||||||
|
if (!node || hasTrackedRef.current) return
|
||||||
|
|
||||||
|
if (isWeb && typeof IntersectionObserver !== 'undefined') {
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
entries => {
|
||||||
|
if (entries[0]?.isIntersecting && !hasTrackedRef.current) {
|
||||||
|
hasTrackedRef.current = true
|
||||||
|
onSeen(profile.did, position)
|
||||||
|
observer.disconnect()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{threshold: 0.5},
|
||||||
|
)
|
||||||
|
// @ts-ignore - web only
|
||||||
|
observer.observe(node)
|
||||||
|
return () => observer.disconnect()
|
||||||
|
} else {
|
||||||
|
// Native: use a short delay to account for initial layout
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
if (!hasTrackedRef.current) {
|
||||||
|
hasTrackedRef.current = true
|
||||||
|
onSeen(profile.did, position)
|
||||||
|
}
|
||||||
|
}, 500)
|
||||||
|
return () => clearTimeout(timeout)
|
||||||
|
}
|
||||||
|
}, [onSeen, profile.did, position])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
|
ref={cardRef}
|
||||||
style={[
|
style={[
|
||||||
a.w_full,
|
a.w_full,
|
||||||
a.py_lg,
|
a.py_lg,
|
||||||
@@ -342,6 +411,8 @@ function SuggestedProfileCard({
|
|||||||
location: 'Card',
|
location: 'Card',
|
||||||
recId: undefined,
|
recId: undefined,
|
||||||
position,
|
position,
|
||||||
|
suggestedDid: profile.did,
|
||||||
|
category,
|
||||||
},
|
},
|
||||||
{statsig: true},
|
{statsig: true},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ export function AnimatedProfileHeaderSuggestedFollows({
|
|||||||
recId={data.recId}
|
recId={data.recId}
|
||||||
error={error}
|
error={error}
|
||||||
viewContext="profileHeader"
|
viewContext="profileHeader"
|
||||||
|
isVisible={isExpanded}
|
||||||
/>
|
/>
|
||||||
</AccordionAnimation>
|
</AccordionAnimation>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1030,26 +1030,48 @@ export function Explore({
|
|||||||
|
|
||||||
// track headers and report module viewability
|
// track headers and report module viewability
|
||||||
const alreadyReportedRef = useRef<Map<string, string>>(new Map())
|
const alreadyReportedRef = useRef<Map<string, string>>(new Map())
|
||||||
const onItemSeen = useCallback((item: ExploreScreenItems) => {
|
const seenProfilesRef = useRef<Set<string>>(new Set())
|
||||||
let module: MetricEvents['explore:module:seen']['module']
|
const onItemSeen = useCallback(
|
||||||
if (item.type === 'trendingTopics' || item.type === 'trendingVideos') {
|
(item: ExploreScreenItems) => {
|
||||||
module = item.type
|
let module: MetricEvents['explore:module:seen']['module']
|
||||||
} else if (item.type === 'profile') {
|
if (item.type === 'trendingTopics' || item.type === 'trendingVideos') {
|
||||||
module = 'suggestedAccounts'
|
module = item.type
|
||||||
} else if (item.type === 'feed') {
|
} else if (item.type === 'profile') {
|
||||||
module = 'suggestedFeeds'
|
module = 'suggestedAccounts'
|
||||||
} else if (item.type === 'starterPack') {
|
// Track individual profile seen events
|
||||||
module = 'suggestedStarterPacks'
|
if (!seenProfilesRef.current.has(item.profile.did)) {
|
||||||
} else if (item.type === 'preview:sliceItem') {
|
seenProfilesRef.current.add(item.profile.did)
|
||||||
module = `feed:feedgen|${item.feed.uri}`
|
const position = suggestedFollowsModule.findIndex(
|
||||||
} else {
|
i => i.type === 'profile' && i.profile.did === item.profile.did,
|
||||||
return
|
)
|
||||||
}
|
logger.metric(
|
||||||
if (!alreadyReportedRef.current.has(module)) {
|
'suggestedUser:seen',
|
||||||
alreadyReportedRef.current.set(module, module)
|
{
|
||||||
logger.metric('explore:module:seen', {module}, {statsig: false})
|
logContext: 'Explore',
|
||||||
}
|
recId: item.recId,
|
||||||
}, [])
|
position: position !== -1 ? position - 1 : 0, // -1 to account for header
|
||||||
|
suggestedDid: item.profile.did,
|
||||||
|
category: null,
|
||||||
|
},
|
||||||
|
{statsig: true},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (item.type === 'feed') {
|
||||||
|
module = 'suggestedFeeds'
|
||||||
|
} else if (item.type === 'starterPack') {
|
||||||
|
module = 'suggestedStarterPacks'
|
||||||
|
} else if (item.type === 'preview:sliceItem') {
|
||||||
|
module = `feed:feedgen|${item.feed.uri}`
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!alreadyReportedRef.current.has(module)) {
|
||||||
|
alreadyReportedRef.current.set(module, module)
|
||||||
|
logger.metric('explore:module:seen', {module}, {statsig: false})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[suggestedFollowsModule],
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<List
|
<List
|
||||||
|
|||||||
@@ -123,6 +123,8 @@ let SuggestedProfileCard = ({
|
|||||||
logContext: 'Explore',
|
logContext: 'Explore',
|
||||||
recId,
|
recId,
|
||||||
position,
|
position,
|
||||||
|
suggestedDid: profile.did,
|
||||||
|
category: null,
|
||||||
},
|
},
|
||||||
{statsig: true},
|
{statsig: true},
|
||||||
)
|
)
|
||||||
@@ -162,6 +164,8 @@ let SuggestedProfileCard = ({
|
|||||||
location: 'Card',
|
location: 'Card',
|
||||||
recId,
|
recId,
|
||||||
position,
|
position,
|
||||||
|
suggestedDid: profile.did,
|
||||||
|
category: null,
|
||||||
},
|
},
|
||||||
{statsig: true},
|
{statsig: true},
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user