add metrics

This commit is contained in:
Hailey
2025-05-29 11:21:04 -07:00
parent bc42d26bc5
commit 91660cd394
5 changed files with 58 additions and 15 deletions
+2
View File
@@ -72,11 +72,13 @@ let PostControls = ({
const [queueLike, queueUnlike] = usePostLikeMutationQueue( const [queueLike, queueUnlike] = usePostLikeMutationQueue(
post, post,
viaRepost, viaRepost,
feedContext,
logContext, logContext,
) )
const [queueRepost, queueUnrepost] = usePostRepostMutationQueue( const [queueRepost, queueUnrepost] = usePostRepostMutationQueue(
post, post,
viaRepost, viaRepost,
feedContext,
logContext, logContext,
) )
const requireAuth = useRequireAuth() const requireAuth = useRequireAuth()
+25
View File
@@ -130,6 +130,27 @@ export type MetricEvents = {
feedType: string feedType: string
reason: 'pull-to-refresh' | 'soft-reset' | 'load-latest' reason: 'pull-to-refresh' | 'soft-reset' | 'load-latest'
} }
'feed:save': {
feedUrl: string
}
'feed:unsave': {
feedUrl: string
}
'feed:pin': {
feedUrl: string
}
'feed:unpin': {
feedUrl: string
}
'feed:like': {
feedUrl: string
}
'feed:unlike': {
feedUrl: string
}
'feed:share': {
feedUrl: string
}
'discover:showMore': { 'discover:showMore': {
feedContext: string feedContext: string
} }
@@ -175,15 +196,19 @@ export type MetricEvents = {
likerClout: number | undefined likerClout: number | undefined
postClout: number | undefined postClout: number | undefined
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo' logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
feedContext?: string
} }
'post:repost': { 'post:repost': {
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo' logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
feedContext?: string
} }
'post:unlike': { 'post:unlike': {
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo' logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
feedContext?: string
} }
'post:unrepost': { 'post:unrepost': {
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo' logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
feedContext?: string
} }
'post:mute': {} 'post:mute': {}
'post:unmute': {} 'post:unmute': {}
@@ -113,13 +113,14 @@ export function ProfileFeedHeader({info}: {info: FeedSourceFeedInfo}) {
const isSaved = Boolean(savedFeedConfig) const isSaved = Boolean(savedFeedConfig)
const isPinned = Boolean(savedFeedConfig?.pinned) const isPinned = Boolean(savedFeedConfig?.pinned)
const onToggleSaved = React.useCallback(async () => { const onToggleSaved = async () => {
try { try {
playHaptic() playHaptic()
if (savedFeedConfig) { if (savedFeedConfig) {
await removeFeed(savedFeedConfig) await removeFeed(savedFeedConfig)
Toast.show(_(msg`Removed from your feeds`)) Toast.show(_(msg`Removed from your feeds`))
logger.metric('feed:unsave', {feedUrl: info.uri})
} else { } else {
await addSavedFeeds([ await addSavedFeeds([
{ {
@@ -129,6 +130,7 @@ export function ProfileFeedHeader({info}: {info: FeedSourceFeedInfo}) {
}, },
]) ])
Toast.show(_(msg`Saved to your feeds`)) Toast.show(_(msg`Saved to your feeds`))
logger.metric('feed:save', {feedUrl: info.uri})
} }
} catch (err) { } catch (err) {
Toast.show( Toast.show(
@@ -139,9 +141,9 @@ export function ProfileFeedHeader({info}: {info: FeedSourceFeedInfo}) {
) )
logger.error('Failed to update feeds', {message: err}) logger.error('Failed to update feeds', {message: err})
} }
}, [_, playHaptic, info, removeFeed, addSavedFeeds, savedFeedConfig]) }
const onTogglePinned = React.useCallback(async () => { const onTogglePinned = async () => {
try { try {
playHaptic() playHaptic()
@@ -156,8 +158,10 @@ export function ProfileFeedHeader({info}: {info: FeedSourceFeedInfo}) {
if (pinned) { if (pinned) {
Toast.show(_(msg`Pinned ${info.displayName} to Home`)) Toast.show(_(msg`Pinned ${info.displayName} to Home`))
logger.metric('feed:pin', {feedUrl: info.uri})
} else { } else {
Toast.show(_(msg`Unpinned ${info.displayName} from Home`)) Toast.show(_(msg`Unpinned ${info.displayName} from Home`))
logger.metric('feed:unpin', {feedUrl: info.uri})
} }
} else { } else {
await addSavedFeeds([ await addSavedFeeds([
@@ -168,12 +172,13 @@ export function ProfileFeedHeader({info}: {info: FeedSourceFeedInfo}) {
}, },
]) ])
Toast.show(_(msg`Pinned ${info.displayName} to Home`)) Toast.show(_(msg`Pinned ${info.displayName} to Home`))
logger.metric('feed:pin', {feedUrl: info.uri})
} }
} catch (e) { } catch (e) {
Toast.show(_(msg`There was an issue contacting the server`), 'xmark') Toast.show(_(msg`There was an issue contacting the server`), 'xmark')
logger.error('Failed to toggle pinned feed', {message: e}) logger.error('Failed to toggle pinned feed', {message: e})
} }
}, [playHaptic, info, _, savedFeedConfig, updateSavedFeeds, addSavedFeeds]) }
return ( return (
<> <>
@@ -394,16 +399,18 @@ function DialogInner({
const isLiked = !!likeUri const isLiked = !!likeUri
const feedRkey = React.useMemo(() => new AtUri(info.uri).rkey, [info.uri]) const feedRkey = React.useMemo(() => new AtUri(info.uri).rkey, [info.uri])
const onToggleLiked = React.useCallback(async () => { const onToggleLiked = async () => {
try { try {
playHaptic() playHaptic()
if (isLiked && likeUri) { if (isLiked && likeUri) {
await unlikeFeed({uri: likeUri}) await unlikeFeed({uri: likeUri})
setLikeUri('') setLikeUri('')
logger.metric('feed:unlike', {feedUrl: info.uri})
} else { } else {
const res = await likeFeed({uri: info.uri, cid: info.cid}) const res = await likeFeed({uri: info.uri, cid: info.cid})
setLikeUri(res.uri) setLikeUri(res.uri)
logger.metric('feed:like', {feedUrl: info.uri})
} }
} catch (err) { } catch (err) {
Toast.show( Toast.show(
@@ -414,12 +421,13 @@ function DialogInner({
) )
logger.error('Failed to toggle like', {message: err}) logger.error('Failed to toggle like', {message: err})
} }
}, [playHaptic, isLiked, likeUri, unlikeFeed, setLikeUri, likeFeed, info, _]) }
const onPressShare = React.useCallback(() => { const onPressShare = React.useCallback(() => {
playHaptic() playHaptic()
const url = toShareUrl(info.route.href) const url = toShareUrl(info.route.href)
shareUrl(url) shareUrl(url)
logger.metric('feed:share', {feedUrl: info.uri})
}, [info, playHaptic]) }, [info, playHaptic])
const onPressReport = React.useCallback(() => { const onPressReport = React.useCallback(() => {
@@ -134,7 +134,7 @@ export function ExploreTrendingVideos() {
label={_(msg`Pin`)} label={_(msg`Pin`)}
size="small" size="small"
variant="outline" variant="outline"
color="secondary" color="secondary
onPress={pinFeed}> onPress={pinFeed}>
<ButtonText>{_(msg`Pin`)}</ButtonText> <ButtonText>{_(msg`Pin`)}</ButtonText>
<ButtonIcon icon={Pin} position="right" /> <ButtonIcon icon={Pin} position="right" />
+16 -8
View File
@@ -4,6 +4,7 @@ import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
import {useToggleMutationQueue} from '#/lib/hooks/useToggleMutationQueue' import {useToggleMutationQueue} from '#/lib/hooks/useToggleMutationQueue'
import {logEvent, type LogEvents, toClout} from '#/lib/statsig/statsig' import {logEvent, type LogEvents, toClout} from '#/lib/statsig/statsig'
import {logger} from '#/logger'
import {updatePostShadow} from '#/state/cache/post-shadow' import {updatePostShadow} from '#/state/cache/post-shadow'
import {type Shadow} from '#/state/cache/types' import {type Shadow} from '#/state/cache/types'
import {useAgent, useSession} from '#/state/session' import {useAgent, useSession} from '#/state/session'
@@ -99,6 +100,7 @@ export function useGetPosts() {
export function usePostLikeMutationQueue( export function usePostLikeMutationQueue(
post: Shadow<AppBskyFeedDefs.PostView>, post: Shadow<AppBskyFeedDefs.PostView>,
viaRepost: {uri: string; cid: string} | undefined, viaRepost: {uri: string; cid: string} | undefined,
feedContext: string | undefined,
logContext: LogEvents['post:like']['logContext'] & logContext: LogEvents['post:like']['logContext'] &
LogEvents['post:unlike']['logContext'], LogEvents['post:unlike']['logContext'],
) { ) {
@@ -106,8 +108,8 @@ export function usePostLikeMutationQueue(
const postUri = post.uri const postUri = post.uri
const postCid = post.cid const postCid = post.cid
const initialLikeUri = post.viewer?.like const initialLikeUri = post.viewer?.like
const likeMutation = usePostLikeMutation(logContext, post) const likeMutation = usePostLikeMutation(feedContext, logContext, post)
const unlikeMutation = usePostUnlikeMutation(logContext) const unlikeMutation = usePostUnlikeMutation(feedContext, logContext)
const queueToggle = useToggleMutationQueue({ const queueToggle = useToggleMutationQueue({
initialState: initialLikeUri, initialState: initialLikeUri,
@@ -159,6 +161,7 @@ export function usePostLikeMutationQueue(
} }
function usePostLikeMutation( function usePostLikeMutation(
feedContext: string | undefined,
logContext: LogEvents['post:like']['logContext'], logContext: LogEvents['post:like']['logContext'],
post: Shadow<AppBskyFeedDefs.PostView>, post: Shadow<AppBskyFeedDefs.PostView>,
) { ) {
@@ -176,7 +179,7 @@ function usePostLikeMutation(
if (currentAccount) { if (currentAccount) {
ownProfile = findProfileQueryData(queryClient, currentAccount.did) ownProfile = findProfileQueryData(queryClient, currentAccount.did)
} }
logEvent('post:like', { logger.metric('post:like', {
logContext, logContext,
doesPosterFollowLiker: postAuthor.viewer doesPosterFollowLiker: postAuthor.viewer
? Boolean(postAuthor.viewer.followedBy) ? Boolean(postAuthor.viewer.followedBy)
@@ -191,6 +194,7 @@ function usePostLikeMutation(
post.replyCount != null post.replyCount != null
? toClout(post.likeCount + post.repostCount + post.replyCount) ? toClout(post.likeCount + post.repostCount + post.replyCount)
: undefined, : undefined,
feedContext: feedContext,
}) })
return agent.like(uri, cid, via) return agent.like(uri, cid, via)
}, },
@@ -198,12 +202,13 @@ function usePostLikeMutation(
} }
function usePostUnlikeMutation( function usePostUnlikeMutation(
feedContext: string | undefined,
logContext: LogEvents['post:unlike']['logContext'], logContext: LogEvents['post:unlike']['logContext'],
) { ) {
const agent = useAgent() const agent = useAgent()
return useMutation<void, Error, {postUri: string; likeUri: string}>({ return useMutation<void, Error, {postUri: string; likeUri: string}>({
mutationFn: ({likeUri}) => { mutationFn: ({likeUri}) => {
logEvent('post:unlike', {logContext}) logger.metric('post:unlike', {logContext, feedContext: feedContext})
return agent.deleteLike(likeUri) return agent.deleteLike(likeUri)
}, },
}) })
@@ -212,6 +217,7 @@ function usePostUnlikeMutation(
export function usePostRepostMutationQueue( export function usePostRepostMutationQueue(
post: Shadow<AppBskyFeedDefs.PostView>, post: Shadow<AppBskyFeedDefs.PostView>,
viaRepost: {uri: string; cid: string} | undefined, viaRepost: {uri: string; cid: string} | undefined,
feedContext: string | undefined,
logContext: LogEvents['post:repost']['logContext'] & logContext: LogEvents['post:repost']['logContext'] &
LogEvents['post:unrepost']['logContext'], LogEvents['post:unrepost']['logContext'],
) { ) {
@@ -219,8 +225,8 @@ export function usePostRepostMutationQueue(
const postUri = post.uri const postUri = post.uri
const postCid = post.cid const postCid = post.cid
const initialRepostUri = post.viewer?.repost const initialRepostUri = post.viewer?.repost
const repostMutation = usePostRepostMutation(logContext) const repostMutation = usePostRepostMutation(feedContext, logContext)
const unrepostMutation = usePostUnrepostMutation(logContext) const unrepostMutation = usePostUnrepostMutation(feedContext, logContext)
const queueToggle = useToggleMutationQueue({ const queueToggle = useToggleMutationQueue({
initialState: initialRepostUri, initialState: initialRepostUri,
@@ -270,6 +276,7 @@ export function usePostRepostMutationQueue(
} }
function usePostRepostMutation( function usePostRepostMutation(
feedContext: string | undefined,
logContext: LogEvents['post:repost']['logContext'], logContext: LogEvents['post:repost']['logContext'],
) { ) {
const agent = useAgent() const agent = useAgent()
@@ -279,19 +286,20 @@ function usePostRepostMutation(
{uri: string; cid: string; via?: {uri: string; cid: string}} // the post's uri and cid, and the repost uri/cid if present {uri: string; cid: string; via?: {uri: string; cid: string}} // the post's uri and cid, and the repost uri/cid if present
>({ >({
mutationFn: ({uri, cid, via}) => { mutationFn: ({uri, cid, via}) => {
logEvent('post:repost', {logContext}) logEvent('post:repost', {logContext, feedContext: feedContext})
return agent.repost(uri, cid, via) return agent.repost(uri, cid, via)
}, },
}) })
} }
function usePostUnrepostMutation( function usePostUnrepostMutation(
feedContext: string | undefined,
logContext: LogEvents['post:unrepost']['logContext'], logContext: LogEvents['post:unrepost']['logContext'],
) { ) {
const agent = useAgent() const agent = useAgent()
return useMutation<void, Error, {postUri: string; repostUri: string}>({ return useMutation<void, Error, {postUri: string; repostUri: string}>({
mutationFn: ({repostUri}) => { mutationFn: ({repostUri}) => {
logEvent('post:unrepost', {logContext}) logEvent('post:unrepost', {logContext, feedContext: feedContext})
return agent.deleteRepost(repostUri) return agent.deleteRepost(repostUri)
}, },
}) })