diff --git a/package.json b/package.json
index a5be4a9be6..dd0d84d9b3 100644
--- a/package.json
+++ b/package.json
@@ -54,7 +54,7 @@
"icons:optimize": "svgo -f ./assets/icons"
},
"dependencies": {
- "@atproto/api": "^0.13.21",
+ "@atproto/api": "^0.13.27",
"@braintree/sanitize-url": "^6.0.2",
"@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet",
"@emoji-mart/react": "^1.1.1",
diff --git a/src/components/FeedInterstitials.tsx b/src/components/FeedInterstitials.tsx
index ec224eeae0..fd6eb30c68 100644
--- a/src/components/FeedInterstitials.tsx
+++ b/src/components/FeedInterstitials.tsx
@@ -196,6 +196,7 @@ export function SuggestedFollowsProfile({did}: {did: string}) {
@@ -222,10 +223,12 @@ export function ProfileGrid({
isSuggestionsLoading,
error,
profiles,
+ recId,
viewContext = 'feed',
}: {
isSuggestionsLoading: boolean
profiles: AppBskyActorDefs.ProfileViewDetailed[]
+ recId?: number
error: Error | null
viewContext: 'profile' | 'feed'
}) {
@@ -249,12 +252,20 @@ export function ProfileGrid({
))
) : error || !profiles.length ? null : (
<>
- {profiles.slice(0, maxLength).map(profile => (
+ {profiles.slice(0, maxLength).map((profile, index) => (
{
logEvent('feed:interstitial:profileCard:press', {})
+ logEvent('suggestedUser:press', {
+ logContext:
+ viewContext === 'feed'
+ ? 'InterstitialDiscover'
+ : 'InterstitialProfile',
+ recId,
+ position: index,
+ })
}}
style={[
a.flex_1,
@@ -282,6 +293,17 @@ export function ProfileGrid({
logContext="FeedInterstitial"
shape="round"
colorInverted
+ onFollow={() => {
+ logEvent('suggestedUser:follow', {
+ logContext:
+ viewContext === 'feed'
+ ? 'InterstitialDiscover'
+ : 'InterstitialProfile',
+ location: 'Card',
+ recId,
+ position: index,
+ })
+ }}
/>
diff --git a/src/components/ProfileCard.tsx b/src/components/ProfileCard.tsx
index 7bec14b9cc..78d86ab362 100644
--- a/src/components/ProfileCard.tsx
+++ b/src/components/ProfileCard.tsx
@@ -286,6 +286,7 @@ export type FollowButtonProps = {
logContext: LogEvents['profile:follow']['logContext'] &
LogEvents['profile:unfollow']['logContext']
colorInverted?: boolean
+ onFollow?: () => void
} & Partial
export function FollowButton(props: FollowButtonProps) {
@@ -299,6 +300,7 @@ export function FollowButtonInner({
moderationOpts,
logContext,
onPress: onPressProp,
+ onFollow,
colorInverted,
...rest
}: FollowButtonProps) {
@@ -325,6 +327,7 @@ export function FollowButtonInner({
),
)
onPressProp?.(e)
+ onFollow?.()
} catch (err: any) {
if (err?.name !== 'AbortError') {
Toast.show(_(msg`An issue occurred, please try again.`), 'xmark')
diff --git a/src/lib/constants.ts b/src/lib/constants.ts
index 566edf69db..1c049ea818 100644
--- a/src/lib/constants.ts
+++ b/src/lib/constants.ts
@@ -63,6 +63,9 @@ export function IS_PROD_SERVICE(url?: string) {
export const PROD_DEFAULT_FEED = (rkey: string) =>
`at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/${rkey}`
+export const STAGING_DEFAULT_FEED = (rkey: string) =>
+ `at://did:plc:yofh3kx63drvfljkibw5zuxo/app.bsky.feed.generator/${rkey}`
+
export const POST_IMG_MAX = {
width: 2000,
height: 2000,
diff --git a/src/lib/statsig/events.ts b/src/lib/statsig/events.ts
index 19bf06ba98..2888484aa9 100644
--- a/src/lib/statsig/events.ts
+++ b/src/lib/statsig/events.ts
@@ -164,6 +164,26 @@ export type LogEvents = {
| 'ProfileHeaderSuggestedFollows'
| 'PostOnboardingFindFollows'
}
+ 'suggestedUser:follow': {
+ logContext:
+ | 'Explore'
+ | 'InterstitialDiscover'
+ | 'InterstitialProfile'
+ | 'Profile'
+ location: 'Card' | 'Profile'
+ recId?: number
+ position: number
+ }
+ 'suggestedUser:press': {
+ logContext: 'Explore' | 'InterstitialDiscover' | 'InterstitialProfile'
+ recId?: number
+ position: number
+ }
+ 'suggestedUser:seen': {
+ logContext: 'Explore' | 'InterstitialDiscover' | 'InterstitialProfile'
+ recId?: number
+ position: number
+ }
'profile:unfollow': {
logContext:
| 'RecommendedFollowsItem'
diff --git a/src/state/feed-feedback.tsx b/src/state/feed-feedback.tsx
index 02f98ad145..9c7358e7af 100644
--- a/src/state/feed-feedback.tsx
+++ b/src/state/feed-feedback.tsx
@@ -3,7 +3,7 @@ import {AppState, AppStateStatus} from 'react-native'
import {AppBskyFeedDefs} from '@atproto/api'
import throttle from 'lodash.throttle'
-import {PROD_DEFAULT_FEED} from '#/lib/constants'
+import {PROD_DEFAULT_FEED, STAGING_DEFAULT_FEED} from '#/lib/constants'
import {logEvent} from '#/lib/statsig/statsig'
import {logger} from '#/logger'
import {FeedDescriptor, FeedPostSliceItem} from '#/state/queries/post-feed'
@@ -54,7 +54,7 @@ export function useFeedFeedback(feed: FeedDescriptor, hasSession: boolean) {
encoding: 'application/json',
headers: {
// TODO when we start sending to other feeds, we need to grab their DID -prf
- 'atproto-proxy': 'did:web:discover.bsky.app#bsky_fg',
+ 'atproto-proxy': 'did:web:algo.pop2.bsky.app#bsky_fg',
},
},
)
@@ -155,7 +155,10 @@ export function useFeedFeedbackContext() {
// place, we're hardcoding it to the discover feed.
// -prf
function isDiscoverFeed(feed: FeedDescriptor) {
- return feed === `feedgen|${PROD_DEFAULT_FEED('whats-hot')}`
+ return (
+ feed === `feedgen|${PROD_DEFAULT_FEED('whats-hot')}` ||
+ feed === `feedgen|${STAGING_DEFAULT_FEED('thevids')}`
+ )
}
function toString(interaction: AppBskyFeedDefs.Interaction): string {
diff --git a/src/state/queries/suggested-follows.ts b/src/state/queries/suggested-follows.ts
index 22033c0a8c..0a2343150c 100644
--- a/src/state/queries/suggested-follows.ts
+++ b/src/state/queries/suggested-follows.ts
@@ -120,7 +120,7 @@ export function useSuggestedFollowsByActorQuery({
const suggestions = res.data.isFallback
? []
: res.data.suggestions.filter(profile => !profile.viewer?.following)
- return {suggestions}
+ return {suggestions, recId: res.data.recId}
},
enabled,
})
diff --git a/src/view/com/profile/FollowButton.tsx b/src/view/com/profile/FollowButton.tsx
index c2d76316e6..ff58dc9455 100644
--- a/src/view/com/profile/FollowButton.tsx
+++ b/src/view/com/profile/FollowButton.tsx
@@ -14,12 +14,14 @@ export function FollowButton({
profile,
labelStyle,
logContext,
+ onFollow,
}: {
unfollowedType?: ButtonType
followedType?: ButtonType
profile: Shadow
labelStyle?: StyleProp
logContext: 'ProfileCard' | 'StarterPackProfilesList'
+ onFollow?: () => void
}) {
const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(
profile,
@@ -30,6 +32,7 @@ export function FollowButton({
const onPressFollow = async () => {
try {
await queueFollow()
+ onFollow?.()
} catch (e: any) {
if (e?.name !== 'AbortError') {
Toast.show(_(msg`An issue occurred, please try again.`), 'xmark')
diff --git a/src/view/com/profile/ProfileCard.tsx b/src/view/com/profile/ProfileCard.tsx
index eab8611dd4..f710d7b4e7 100644
--- a/src/view/com/profile/ProfileCard.tsx
+++ b/src/view/com/profile/ProfileCard.tsx
@@ -184,6 +184,7 @@ export function ProfileCardWithFollowBtn({
noBg,
noBorder,
onPress,
+ onFollow,
logContext = 'ProfileCard',
showKnownFollowers,
}: {
@@ -191,6 +192,7 @@ export function ProfileCardWithFollowBtn({
noBg?: boolean
noBorder?: boolean
onPress?: () => void
+ onFollow?: () => void
logContext?: 'ProfileCard' | 'StarterPackProfilesList'
showKnownFollowers?: boolean
}) {
@@ -206,7 +208,11 @@ export function ProfileCardWithFollowBtn({
isMe
? undefined
: profileShadow => (
-
+
)
}
onPress={onPress}
diff --git a/src/view/screens/Search/Explore.tsx b/src/view/screens/Search/Explore.tsx
index 378ea59a4d..324339ebf1 100644
--- a/src/view/screens/Search/Explore.tsx
+++ b/src/view/screens/Search/Explore.tsx
@@ -10,6 +10,7 @@ import {
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
+import {logEvent} from '#/lib/statsig/statsig'
import {cleanError} from '#/lib/strings/errors'
import {logger} from '#/logger'
import {isWeb} from '#/platform/detection'
@@ -253,6 +254,7 @@ type ExploreScreenItems =
type: 'profile'
key: string
profile: AppBskyActorDefs.ProfileView
+ recId?: number
}
| {
type: 'feed'
@@ -370,6 +372,7 @@ export function Explore() {
type: 'profile',
key: actor.did,
profile: actor,
+ recId: page.recId,
})
}
}
@@ -498,7 +501,7 @@ export function Explore() {
])
const renderItem = React.useCallback(
- ({item}: {item: ExploreScreenItems}) => {
+ ({item, index}: {item: ExploreScreenItems; index: number}) => {
switch (item.type) {
case 'header': {
return (
@@ -524,6 +527,21 @@ export function Explore() {
noBg
noBorder
showKnownFollowers
+ onPress={() => {
+ logEvent('suggestedUser:press', {
+ logContext: 'Explore',
+ recId: item.recId,
+ position: index,
+ })
+ }}
+ onFollow={() => {
+ logEvent('suggestedUser:follow', {
+ logContext: 'Explore',
+ location: 'Card',
+ recId: item.recId,
+ position: index,
+ })
+ }}
/>
)
diff --git a/yarn.lock b/yarn.lock
index da037040ce..c6939d7fb0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -72,15 +72,15 @@
tlds "^1.234.0"
zod "^3.23.8"
-"@atproto/api@^0.13.21":
- version "0.13.21"
- resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.13.21.tgz#8ee27a07e5a024b5bf32408d9bd623dd598ad1cc"
- integrity sha512-iOxSj2YS3Fx9IPz1NivKrSsdYPNbBgpnUH7+WhKYAMvDFDUe2PZe7taau8wsUjJAu/H3S0Mk2TDh5e/7tCRwHA==
+"@atproto/api@^0.13.27":
+ version "0.13.27"
+ resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.13.27.tgz#54d1172fbc4415d41089d2715a657052b26b60bc"
+ integrity sha512-IaZezARJSoFBxFvutGa+2hT0NiA+db6NpKkla2bRyv/SPinYViuqD5kaaPzc732yeE1uwUKQc4wwwlctYamFBQ==
dependencies:
- "@atproto/common-web" "^0.3.1"
- "@atproto/lexicon" "^0.4.4"
+ "@atproto/common-web" "^0.3.2"
+ "@atproto/lexicon" "^0.4.5"
"@atproto/syntax" "^0.3.1"
- "@atproto/xrpc" "^0.6.5"
+ "@atproto/xrpc" "^0.6.6"
await-lock "^2.2.2"
multiformats "^9.9.0"
tlds "^1.234.0"
@@ -169,6 +169,16 @@
uint8arrays "3.0.0"
zod "^3.23.8"
+"@atproto/common-web@^0.3.2":
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/@atproto/common-web/-/common-web-0.3.2.tgz#4cf78ad4d24fed801882f3d35afc39bceccdff51"
+ integrity sha512-Vx0JtL1/CssJbFAb0UOdvTrkbUautsDfHNOXNTcX2vyPIxH9xOameSqLLunM1hZnOQbJwyjmQCt6TV+bhnanDg==
+ dependencies:
+ graphemer "^1.4.0"
+ multiformats "^9.9.0"
+ uint8arrays "3.0.0"
+ zod "^3.23.8"
+
"@atproto/common@0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@atproto/common/-/common-0.1.0.tgz#4216a8fef5b985ab62ac21252a0f8ca0f4a0f210"
@@ -283,6 +293,17 @@
multiformats "^9.9.0"
zod "^3.23.8"
+"@atproto/lexicon@^0.4.5":
+ version "0.4.5"
+ resolved "https://registry.yarnpkg.com/@atproto/lexicon/-/lexicon-0.4.5.tgz#4fcf3731193c674286e9e8d677bbab5dd530b817"
+ integrity sha512-fljWqMGKn+XWtTprBcS3F1hGBREnQYh6qYHv2sjENucc7REms1gtmZXSerB9N6pVeHVNOnXiILdukeAcic5OEw==
+ dependencies:
+ "@atproto/common-web" "^0.3.2"
+ "@atproto/syntax" "^0.3.1"
+ iso-datestring-validator "^2.2.2"
+ multiformats "^9.9.0"
+ zod "^3.23.8"
+
"@atproto/oauth-provider@^0.2.10":
version "0.2.10"
resolved "https://registry.yarnpkg.com/@atproto/oauth-provider/-/oauth-provider-0.2.10.tgz#f9820d7f82c33d3b74e81a75873f50e1e654b901"
@@ -452,6 +473,14 @@
"@atproto/lexicon" "^0.4.4"
zod "^3.23.8"
+"@atproto/xrpc@^0.6.6":
+ version "0.6.6"
+ resolved "https://registry.yarnpkg.com/@atproto/xrpc/-/xrpc-0.6.6.tgz#28f58270ef4a8056f7f718bd52512e74bcd3702f"
+ integrity sha512-umXEYVMo9/pyIBoKmIAIi64RXDW9tSXY+wqztlQ6I2GZtjLfNZqmAWU+wADk3SxUe54mvjxxGyA4TtyGtDMfhA==
+ dependencies:
+ "@atproto/lexicon" "^0.4.5"
+ zod "^3.23.8"
+
"@aws-crypto/crc32@3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@aws-crypto/crc32/-/crc32-3.0.0.tgz#07300eca214409c33e3ff769cd5697b57fdd38fa"