about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authordan <dan.abramov@gmail.com>2024-03-13 22:31:51 +0000
committerGitHub <noreply@github.com>2024-03-13 22:31:51 +0000
commit7eaa573b57c6fb3a37abe105d4a8de10e9b9f893 (patch)
treefebe1e8a6bdad2d071d46af58de32b0b515092e9 /src
parentdb79c918b2e929c1317cbb91918eb00252fdc136 (diff)
downloadvoidsky-7eaa573b57c6fb3a37abe105d4a8de10e9b9f893.tar.zst
[Statsig] Track likes, reposts, follows (#3195)
* [Statsig] Track likes

* Move tracking to intent

* Track repost/unrepost

* Track profile follows/unfollows

* Less copy paste

* Reorder
Diffstat (limited to 'src')
-rw-r--r--src/lib/statsig/events.ts32
-rw-r--r--src/lib/statsig/statsig.tsx8
-rw-r--r--src/state/queries/post.ts47
-rw-r--r--src/state/queries/profile.ts17
-rw-r--r--src/view/com/auth/onboarding/RecommendedFollowsItem.tsx7
-rw-r--r--src/view/com/post-thread/PostThreadFollowBtn.tsx5
-rw-r--r--src/view/com/post-thread/PostThreadItem.tsx2
-rw-r--r--src/view/com/post/Post.tsx1
-rw-r--r--src/view/com/posts/FeedItem.tsx1
-rw-r--r--src/view/com/profile/FollowButton.tsx7
-rw-r--r--src/view/com/profile/ProfileCard.tsx4
-rw-r--r--src/view/com/profile/ProfileHeader.tsx5
-rw-r--r--src/view/com/profile/ProfileHeaderSuggestedFollows.tsx5
-rw-r--r--src/view/com/profile/ProfileMenu.tsx5
-rw-r--r--src/view/com/util/post-ctrls/PostCtrls.tsx9
15 files changed, 125 insertions, 30 deletions
diff --git a/src/lib/statsig/events.ts b/src/lib/statsig/events.ts
index bc647710c..321a0f681 100644
--- a/src/lib/statsig/events.ts
+++ b/src/lib/statsig/events.ts
@@ -1,5 +1,35 @@
-export type Events = {
+export type LogEvents = {
   init: {
     initMs: number
   }
+  'post:like': {
+    logContext: 'FeedItem' | 'PostThreadItem' | 'Post'
+  }
+  'post:repost': {
+    logContext: 'FeedItem' | 'PostThreadItem' | 'Post'
+  }
+  'post:unlike': {
+    logContext: 'FeedItem' | 'PostThreadItem' | 'Post'
+  }
+  'post:unrepost': {
+    logContext: 'FeedItem' | 'PostThreadItem' | 'Post'
+  }
+  'profile:follow': {
+    logContext:
+      | 'RecommendedFollowsItem'
+      | 'PostThreadItem'
+      | 'ProfileCard'
+      | 'ProfileHeader'
+      | 'ProfileHeaderSuggestedFollows'
+      | 'ProfileMenu'
+  }
+  'profile:unfollow': {
+    logContext:
+      | 'RecommendedFollowsItem'
+      | 'PostThreadItem'
+      | 'ProfileCard'
+      | 'ProfileHeader'
+      | 'ProfileHeaderSuggestedFollows'
+      | 'ProfileMenu'
+  }
 }
diff --git a/src/lib/statsig/statsig.tsx b/src/lib/statsig/statsig.tsx
index a46cef4da..5745d204a 100644
--- a/src/lib/statsig/statsig.tsx
+++ b/src/lib/statsig/statsig.tsx
@@ -6,7 +6,9 @@ import {
 } from 'statsig-react-native-expo'
 import {useSession} from '../../state/session'
 import {sha256} from 'js-sha256'
-import {Events} from './events'
+import {LogEvents} from './events'
+
+export type {LogEvents}
 
 const statsigOptions = {
   environment: {
@@ -31,9 +33,9 @@ export function attachRouteToLogEvents(
   getCurrentRouteName = getRouteName
 }
 
-export function logEvent<E extends keyof Events>(
+export function logEvent<E extends keyof LogEvents>(
   eventName: E & string,
-  rawMetadata?: Events[E] & FlatJSONRecord,
+  rawMetadata: LogEvents[E] & FlatJSONRecord,
 ) {
   const fullMetadata = {
     ...rawMetadata,
diff --git a/src/state/queries/post.ts b/src/state/queries/post.ts
index eb59f7da4..e3682e304 100644
--- a/src/state/queries/post.ts
+++ b/src/state/queries/post.ts
@@ -5,6 +5,7 @@ import {Shadow} from '#/state/cache/types'
 import {getAgent} from '#/state/session'
 import {updatePostShadow} from '#/state/cache/post-shadow'
 import {track} from '#/lib/analytics/analytics'
+import {logEvent, LogEvents} from '#/lib/statsig/statsig'
 import {useToggleMutationQueue} from '#/lib/hooks/useToggleMutationQueue'
 
 export const RQKEY = (postUri: string) => ['post', postUri]
@@ -58,12 +59,14 @@ export function useGetPost() {
 
 export function usePostLikeMutationQueue(
   post: Shadow<AppBskyFeedDefs.PostView>,
+  logContext: LogEvents['post:like']['logContext'] &
+    LogEvents['post:unlike']['logContext'],
 ) {
   const postUri = post.uri
   const postCid = post.cid
   const initialLikeUri = post.viewer?.like
-  const likeMutation = usePostLikeMutation()
-  const unlikeMutation = usePostUnlikeMutation()
+  const likeMutation = usePostLikeMutation(logContext)
+  const unlikeMutation = usePostUnlikeMutation(logContext)
 
   const queueToggle = useToggleMutationQueue({
     initialState: initialLikeUri,
@@ -111,22 +114,30 @@ export function usePostLikeMutationQueue(
   return [queueLike, queueUnlike]
 }
 
-function usePostLikeMutation() {
+function usePostLikeMutation(logContext: LogEvents['post:like']['logContext']) {
   return useMutation<
     {uri: string}, // responds with the uri of the like
     Error,
     {uri: string; cid: string} // the post's uri and cid
   >({
-    mutationFn: post => getAgent().like(post.uri, post.cid),
+    mutationFn: post => {
+      logEvent('post:like', {logContext})
+      return getAgent().like(post.uri, post.cid)
+    },
     onSuccess() {
       track('Post:Like')
     },
   })
 }
 
-function usePostUnlikeMutation() {
+function usePostUnlikeMutation(
+  logContext: LogEvents['post:unlike']['logContext'],
+) {
   return useMutation<void, Error, {postUri: string; likeUri: string}>({
-    mutationFn: ({likeUri}) => getAgent().deleteLike(likeUri),
+    mutationFn: ({likeUri}) => {
+      logEvent('post:unlike', {logContext})
+      return getAgent().deleteLike(likeUri)
+    },
     onSuccess() {
       track('Post:Unlike')
     },
@@ -135,12 +146,14 @@ function usePostUnlikeMutation() {
 
 export function usePostRepostMutationQueue(
   post: Shadow<AppBskyFeedDefs.PostView>,
+  logContext: LogEvents['post:repost']['logContext'] &
+    LogEvents['post:unrepost']['logContext'],
 ) {
   const postUri = post.uri
   const postCid = post.cid
   const initialRepostUri = post.viewer?.repost
-  const repostMutation = usePostRepostMutation()
-  const unrepostMutation = usePostUnrepostMutation()
+  const repostMutation = usePostRepostMutation(logContext)
+  const unrepostMutation = usePostUnrepostMutation(logContext)
 
   const queueToggle = useToggleMutationQueue({
     initialState: initialRepostUri,
@@ -188,22 +201,32 @@ export function usePostRepostMutationQueue(
   return [queueRepost, queueUnrepost]
 }
 
-function usePostRepostMutation() {
+function usePostRepostMutation(
+  logContext: LogEvents['post:repost']['logContext'],
+) {
   return useMutation<
     {uri: string}, // responds with the uri of the repost
     Error,
     {uri: string; cid: string} // the post's uri and cid
   >({
-    mutationFn: post => getAgent().repost(post.uri, post.cid),
+    mutationFn: post => {
+      logEvent('post:repost', {logContext})
+      return getAgent().repost(post.uri, post.cid)
+    },
     onSuccess() {
       track('Post:Repost')
     },
   })
 }
 
-function usePostUnrepostMutation() {
+function usePostUnrepostMutation(
+  logContext: LogEvents['post:unrepost']['logContext'],
+) {
   return useMutation<void, Error, {postUri: string; repostUri: string}>({
-    mutationFn: ({repostUri}) => getAgent().deleteRepost(repostUri),
+    mutationFn: ({repostUri}) => {
+      logEvent('post:unrepost', {logContext})
+      return getAgent().deleteRepost(repostUri)
+    },
     onSuccess() {
       track('Post:Unrepost')
     },
diff --git a/src/state/queries/profile.ts b/src/state/queries/profile.ts
index e81ea0f3f..3c9e3e41c 100644
--- a/src/state/queries/profile.ts
+++ b/src/state/queries/profile.ts
@@ -26,6 +26,7 @@ import {RQKEY as RQKEY_MY_MUTED} from './my-muted-accounts'
 import {RQKEY as RQKEY_MY_BLOCKED} from './my-blocked-accounts'
 import {STALE} from '#/state/queries'
 import {track} from '#/lib/analytics/analytics'
+import {logEvent, LogEvents} from '#/lib/statsig/statsig'
 import {ThreadNode} from './post-thread'
 
 export const RQKEY = (did: string) => ['profile', did]
@@ -186,11 +187,13 @@ export function useProfileUpdateMutation() {
 
 export function useProfileFollowMutationQueue(
   profile: Shadow<AppBskyActorDefs.ProfileViewDetailed>,
+  logContext: LogEvents['profile:follow']['logContext'] &
+    LogEvents['profile:unfollow']['logContext'],
 ) {
   const did = profile.did
   const initialFollowingUri = profile.viewer?.following
-  const followMutation = useProfileFollowMutation()
-  const unfollowMutation = useProfileUnfollowMutation()
+  const followMutation = useProfileFollowMutation(logContext)
+  const unfollowMutation = useProfileUnfollowMutation(logContext)
 
   const queueToggle = useToggleMutationQueue({
     initialState: initialFollowingUri,
@@ -237,9 +240,12 @@ export function useProfileFollowMutationQueue(
   return [queueFollow, queueUnfollow]
 }
 
-function useProfileFollowMutation() {
+function useProfileFollowMutation(
+  logContext: LogEvents['profile:follow']['logContext'],
+) {
   return useMutation<{uri: string; cid: string}, Error, {did: string}>({
     mutationFn: async ({did}) => {
+      logEvent('profile:follow', {logContext})
       return await getAgent().follow(did)
     },
     onSuccess(data, variables) {
@@ -248,9 +254,12 @@ function useProfileFollowMutation() {
   })
 }
 
-function useProfileUnfollowMutation() {
+function useProfileUnfollowMutation(
+  logContext: LogEvents['profile:unfollow']['logContext'],
+) {
   return useMutation<void, Error, {did: string; followUri: string}>({
     mutationFn: async ({followUri}) => {
+      logEvent('profile:unfollow', {logContext})
       track('Profile:Unfollow', {username: followUri})
       return await getAgent().deleteFollow(followUri)
     },
diff --git a/src/view/com/auth/onboarding/RecommendedFollowsItem.tsx b/src/view/com/auth/onboarding/RecommendedFollowsItem.tsx
index 07001068c..5f81a4d65 100644
--- a/src/view/com/auth/onboarding/RecommendedFollowsItem.tsx
+++ b/src/view/com/auth/onboarding/RecommendedFollowsItem.tsx
@@ -56,7 +56,7 @@ export function RecommendedFollowsItem({
   )
 }
 
-export function ProfileCard({
+function ProfileCard({
   profile,
   onFollowStateChange,
   moderation,
@@ -72,7 +72,10 @@ export function ProfileCard({
   const pal = usePalette('default')
   const [addingMoreSuggestions, setAddingMoreSuggestions] =
     React.useState(false)
-  const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(profile)
+  const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(
+    profile,
+    'RecommendedFollowsItem',
+  )
 
   const onToggleFollow = React.useCallback(async () => {
     try {
diff --git a/src/view/com/post-thread/PostThreadFollowBtn.tsx b/src/view/com/post-thread/PostThreadFollowBtn.tsx
index e5b747cc9..45c3771f5 100644
--- a/src/view/com/post-thread/PostThreadFollowBtn.tsx
+++ b/src/view/com/post-thread/PostThreadFollowBtn.tsx
@@ -42,7 +42,10 @@ function PostThreadFollowBtnLoaded({
   const {isTabletOrDesktop} = useWebMediaQueries()
   const profile: Shadow<AppBskyActorDefs.ProfileViewBasic> =
     useProfileShadow(profileUnshadowed)
-  const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(profile)
+  const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(
+    profile,
+    'PostThreadItem',
+  )
   const requireAuth = useRequireAuth()
 
   const isFollowing = !!profile.viewer?.following
diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx
index 9522ea6a0..7efd535f2 100644
--- a/src/view/com/post-thread/PostThreadItem.tsx
+++ b/src/view/com/post-thread/PostThreadItem.tsx
@@ -407,6 +407,7 @@ let PostThreadItemLoaded = ({
                 record={record}
                 richText={richText}
                 onPressReply={onPressReply}
+                logContext="PostThreadItem"
               />
             </View>
           </View>
@@ -560,6 +561,7 @@ let PostThreadItemLoaded = ({
                   record={record}
                   richText={richText}
                   onPressReply={onPressReply}
+                  logContext="PostThreadItem"
                 />
               </View>
             </View>
diff --git a/src/view/com/post/Post.tsx b/src/view/com/post/Post.tsx
index 5fa4da84e..0fe3420ba 100644
--- a/src/view/com/post/Post.tsx
+++ b/src/view/com/post/Post.tsx
@@ -220,6 +220,7 @@ function PostInner({
             record={record}
             richText={richText}
             onPressReply={onPressReply}
+            logContext="Post"
           />
         </View>
       </View>
diff --git a/src/view/com/posts/FeedItem.tsx b/src/view/com/posts/FeedItem.tsx
index 7d29703e2..4b78dce7a 100644
--- a/src/view/com/posts/FeedItem.tsx
+++ b/src/view/com/posts/FeedItem.tsx
@@ -310,6 +310,7 @@ let FeedItemInner = ({
             showAppealLabelItem={
               post.author.did === currentAccount?.did && isModeratedPost
             }
+            logContext="FeedItem"
           />
         </View>
       </View>
diff --git a/src/view/com/profile/FollowButton.tsx b/src/view/com/profile/FollowButton.tsx
index 9cc635b66..7b090ffeb 100644
--- a/src/view/com/profile/FollowButton.tsx
+++ b/src/view/com/profile/FollowButton.tsx
@@ -13,13 +13,18 @@ export function FollowButton({
   followedType = 'default',
   profile,
   labelStyle,
+  logContext,
 }: {
   unfollowedType?: ButtonType
   followedType?: ButtonType
   profile: Shadow<AppBskyActorDefs.ProfileViewBasic>
   labelStyle?: StyleProp<TextStyle>
+  logContext: 'ProfileCard'
 }) {
-  const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(profile)
+  const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(
+    profile,
+    logContext,
+  )
   const {_} = useLingui()
 
   const onPressFollow = async () => {
diff --git a/src/view/com/profile/ProfileCard.tsx b/src/view/com/profile/ProfileCard.tsx
index 266adc51d..019e6c10e 100644
--- a/src/view/com/profile/ProfileCard.tsx
+++ b/src/view/com/profile/ProfileCard.tsx
@@ -230,7 +230,9 @@ export function ProfileCardWithFollowBtn({
       renderButton={
         isMe
           ? undefined
-          : profileShadow => <FollowButton profile={profileShadow} />
+          : profileShadow => (
+              <FollowButton profile={profileShadow} logContext="ProfileCard" />
+            )
       }
     />
   )
diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx
index 75e06eb9b..17dc5ce1b 100644
--- a/src/view/com/profile/ProfileHeader.tsx
+++ b/src/view/com/profile/ProfileHeader.tsx
@@ -103,7 +103,10 @@ let ProfileHeader = ({
   const invalidHandle = isInvalidHandle(profile.handle)
   const {isDesktop} = useWebMediaQueries()
   const [showSuggestedFollows, setShowSuggestedFollows] = React.useState(false)
-  const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(profile)
+  const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(
+    profile,
+    'ProfileHeader',
+  )
   const [__, queueUnblock] = useProfileBlockMutationQueue(profile)
   const unblockPromptControl = Prompt.usePromptControl()
   const moderation = useMemo(
diff --git a/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx b/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx
index 6edc61fcf..8f2c89499 100644
--- a/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx
+++ b/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx
@@ -170,7 +170,10 @@ function SuggestedFollow({
   const pal = usePalette('default')
   const moderationOpts = useModerationOpts()
   const profile = useProfileShadow(profileUnshadowed)
-  const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(profile)
+  const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(
+    profile,
+    'ProfileHeaderSuggestedFollows',
+  )
 
   const onPressFollow = React.useCallback(async () => {
     try {
diff --git a/src/view/com/profile/ProfileMenu.tsx b/src/view/com/profile/ProfileMenu.tsx
index 4153b819e..7c6db1ed8 100644
--- a/src/view/com/profile/ProfileMenu.tsx
+++ b/src/view/com/profile/ProfileMenu.tsx
@@ -52,7 +52,10 @@ let ProfileMenu = ({
 
   const [queueMute, queueUnmute] = useProfileMuteMutationQueue(profile)
   const [queueBlock, queueUnblock] = useProfileBlockMutationQueue(profile)
-  const [, queueUnfollow] = useProfileFollowMutationQueue(profile)
+  const [, queueUnfollow] = useProfileFollowMutationQueue(
+    profile,
+    'ProfileMenu',
+  )
 
   const blockPromptControl = Prompt.usePromptControl()
 
diff --git a/src/view/com/util/post-ctrls/PostCtrls.tsx b/src/view/com/util/post-ctrls/PostCtrls.tsx
index 1e26eecce..c96954a11 100644
--- a/src/view/com/util/post-ctrls/PostCtrls.tsx
+++ b/src/view/com/util/post-ctrls/PostCtrls.tsx
@@ -44,6 +44,7 @@ let PostCtrls = ({
   showAppealLabelItem,
   style,
   onPressReply,
+  logContext,
 }: {
   big?: boolean
   post: Shadow<AppBskyFeedDefs.PostView>
@@ -52,13 +53,17 @@ let PostCtrls = ({
   showAppealLabelItem?: boolean
   style?: StyleProp<ViewStyle>
   onPressReply: () => void
+  logContext: 'FeedItem' | 'PostThreadItem' | 'Post'
 }): React.ReactNode => {
   const theme = useTheme()
   const {_} = useLingui()
   const {openComposer} = useComposerControls()
   const {closeModal} = useModalControls()
-  const [queueLike, queueUnlike] = usePostLikeMutationQueue(post)
-  const [queueRepost, queueUnrepost] = usePostRepostMutationQueue(post)
+  const [queueLike, queueUnlike] = usePostLikeMutationQueue(post, logContext)
+  const [queueRepost, queueUnrepost] = usePostRepostMutationQueue(
+    post,
+    logContext,
+  )
   const requireAuth = useRequireAuth()
 
   const defaultCtrlColor = React.useMemo(