Bluesky app fork with some witchin' additions 💫

add metrics (#8426)

authored by hailey.at and committed by GitHub ce7b9dc4 2a453cd9

Changed files
+62 -17
src
components
PostControls
logger
screens
Profile
VideoFeed
state
queries
+3
src/components/PostControls/index.tsx
··· 69 69 const {_, i18n} = useLingui() 70 70 const {gtMobile} = useBreakpoints() 71 71 const {openComposer} = useOpenComposer() 72 + const {feedDescriptor} = useFeedFeedbackContext() 72 73 const [queueLike, queueUnlike] = usePostLikeMutationQueue( 73 74 post, 74 75 viaRepost, 76 + feedDescriptor, 75 77 logContext, 76 78 ) 77 79 const [queueRepost, queueUnrepost] = usePostRepostMutationQueue( 78 80 post, 79 81 viaRepost, 82 + feedDescriptor, 80 83 logContext, 81 84 ) 82 85 const requireAuth = useRequireAuth()
+25
src/logger/metrics.ts
··· 130 130 feedType: string 131 131 reason: 'pull-to-refresh' | 'soft-reset' | 'load-latest' 132 132 } 133 + 'feed:save': { 134 + feedUrl: string 135 + } 136 + 'feed:unsave': { 137 + feedUrl: string 138 + } 139 + 'feed:pin': { 140 + feedUrl: string 141 + } 142 + 'feed:unpin': { 143 + feedUrl: string 144 + } 145 + 'feed:like': { 146 + feedUrl: string 147 + } 148 + 'feed:unlike': { 149 + feedUrl: string 150 + } 151 + 'feed:share': { 152 + feedUrl: string 153 + } 133 154 'discover:showMore': { 134 155 feedContext: string 135 156 } ··· 175 196 likerClout: number | undefined 176 197 postClout: number | undefined 177 198 logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo' 199 + feedDescriptor?: string 178 200 } 179 201 'post:repost': { 180 202 logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo' 203 + feedDescriptor?: string 181 204 } 182 205 'post:unlike': { 183 206 logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo' 207 + feedDescriptor?: string 184 208 } 185 209 'post:unrepost': { 186 210 logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo' 211 + feedDescriptor?: string 187 212 } 188 213 'post:mute': {} 189 214 'post:unmute': {}
+14 -6
src/screens/Profile/components/ProfileFeedHeader.tsx
··· 113 113 const isSaved = Boolean(savedFeedConfig) 114 114 const isPinned = Boolean(savedFeedConfig?.pinned) 115 115 116 - const onToggleSaved = React.useCallback(async () => { 116 + const onToggleSaved = async () => { 117 117 try { 118 118 playHaptic() 119 119 120 120 if (savedFeedConfig) { 121 121 await removeFeed(savedFeedConfig) 122 122 Toast.show(_(msg`Removed from your feeds`)) 123 + logger.metric('feed:unsave', {feedUrl: info.uri}) 123 124 } else { 124 125 await addSavedFeeds([ 125 126 { ··· 129 130 }, 130 131 ]) 131 132 Toast.show(_(msg`Saved to your feeds`)) 133 + logger.metric('feed:save', {feedUrl: info.uri}) 132 134 } 133 135 } catch (err) { 134 136 Toast.show( ··· 139 141 ) 140 142 logger.error('Failed to update feeds', {message: err}) 141 143 } 142 - }, [_, playHaptic, info, removeFeed, addSavedFeeds, savedFeedConfig]) 144 + } 143 145 144 - const onTogglePinned = React.useCallback(async () => { 146 + const onTogglePinned = async () => { 145 147 try { 146 148 playHaptic() 147 149 ··· 156 158 157 159 if (pinned) { 158 160 Toast.show(_(msg`Pinned ${info.displayName} to Home`)) 161 + logger.metric('feed:pin', {feedUrl: info.uri}) 159 162 } else { 160 163 Toast.show(_(msg`Unpinned ${info.displayName} from Home`)) 164 + logger.metric('feed:unpin', {feedUrl: info.uri}) 161 165 } 162 166 } else { 163 167 await addSavedFeeds([ ··· 168 172 }, 169 173 ]) 170 174 Toast.show(_(msg`Pinned ${info.displayName} to Home`)) 175 + logger.metric('feed:pin', {feedUrl: info.uri}) 171 176 } 172 177 } catch (e) { 173 178 Toast.show(_(msg`There was an issue contacting the server`), 'xmark') 174 179 logger.error('Failed to toggle pinned feed', {message: e}) 175 180 } 176 - }, [playHaptic, info, _, savedFeedConfig, updateSavedFeeds, addSavedFeeds]) 181 + } 177 182 178 183 return ( 179 184 <> ··· 394 399 const isLiked = !!likeUri 395 400 const feedRkey = React.useMemo(() => new AtUri(info.uri).rkey, [info.uri]) 396 401 397 - const onToggleLiked = React.useCallback(async () => { 402 + const onToggleLiked = async () => { 398 403 try { 399 404 playHaptic() 400 405 401 406 if (isLiked && likeUri) { 402 407 await unlikeFeed({uri: likeUri}) 403 408 setLikeUri('') 409 + logger.metric('feed:unlike', {feedUrl: info.uri}) 404 410 } else { 405 411 const res = await likeFeed({uri: info.uri, cid: info.cid}) 406 412 setLikeUri(res.uri) 413 + logger.metric('feed:like', {feedUrl: info.uri}) 407 414 } 408 415 } catch (err) { 409 416 Toast.show( ··· 414 421 ) 415 422 logger.error('Failed to toggle like', {message: err}) 416 423 } 417 - }, [playHaptic, isLiked, likeUri, unlikeFeed, setLikeUri, likeFeed, info, _]) 424 + } 418 425 419 426 const onPressShare = React.useCallback(() => { 420 427 playHaptic() 421 428 const url = toShareUrl(info.route.href) 422 429 shareUrl(url) 430 + logger.metric('feed:share', {feedUrl: info.uri}) 423 431 }, [info, playHaptic]) 424 432 425 433 const onPressReport = React.useCallback(() => {
+1
src/screens/VideoFeed/index.tsx
··· 1027 1027 const [queueLike] = usePostLikeMutationQueue( 1028 1028 post, 1029 1029 undefined, 1030 + undefined, 1030 1031 'ImmersiveVideo', 1031 1032 ) 1032 1033 const {sendInteraction} = useFeedFeedbackContext()
+19 -11
src/state/queries/post.ts
··· 3 3 import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query' 4 4 5 5 import {useToggleMutationQueue} from '#/lib/hooks/useToggleMutationQueue' 6 - import {logEvent, type LogEvents, toClout} from '#/lib/statsig/statsig' 6 + import {type LogEvents, toClout} from '#/lib/statsig/statsig' 7 + import {logger} from '#/logger' 7 8 import {updatePostShadow} from '#/state/cache/post-shadow' 8 9 import {type Shadow} from '#/state/cache/types' 9 10 import {useAgent, useSession} from '#/state/session' ··· 99 100 export function usePostLikeMutationQueue( 100 101 post: Shadow<AppBskyFeedDefs.PostView>, 101 102 viaRepost: {uri: string; cid: string} | undefined, 103 + feedDescriptor: string | undefined, 102 104 logContext: LogEvents['post:like']['logContext'] & 103 105 LogEvents['post:unlike']['logContext'], 104 106 ) { ··· 106 108 const postUri = post.uri 107 109 const postCid = post.cid 108 110 const initialLikeUri = post.viewer?.like 109 - const likeMutation = usePostLikeMutation(logContext, post) 110 - const unlikeMutation = usePostUnlikeMutation(logContext) 111 + const likeMutation = usePostLikeMutation(feedDescriptor, logContext, post) 112 + const unlikeMutation = usePostUnlikeMutation(feedDescriptor, logContext) 111 113 112 114 const queueToggle = useToggleMutationQueue({ 113 115 initialState: initialLikeUri, ··· 159 161 } 160 162 161 163 function usePostLikeMutation( 164 + feedDescriptor: string | undefined, 162 165 logContext: LogEvents['post:like']['logContext'], 163 166 post: Shadow<AppBskyFeedDefs.PostView>, 164 167 ) { ··· 176 179 if (currentAccount) { 177 180 ownProfile = findProfileQueryData(queryClient, currentAccount.did) 178 181 } 179 - logEvent('post:like', { 182 + logger.metric('post:like', { 180 183 logContext, 181 184 doesPosterFollowLiker: postAuthor.viewer 182 185 ? Boolean(postAuthor.viewer.followedBy) ··· 191 194 post.replyCount != null 192 195 ? toClout(post.likeCount + post.repostCount + post.replyCount) 193 196 : undefined, 197 + feedDescriptor: feedDescriptor, 194 198 }) 195 199 return agent.like(uri, cid, via) 196 200 }, ··· 198 202 } 199 203 200 204 function usePostUnlikeMutation( 205 + feedDescriptor: string | undefined, 201 206 logContext: LogEvents['post:unlike']['logContext'], 202 207 ) { 203 208 const agent = useAgent() 204 209 return useMutation<void, Error, {postUri: string; likeUri: string}>({ 205 210 mutationFn: ({likeUri}) => { 206 - logEvent('post:unlike', {logContext}) 211 + logger.metric('post:unlike', {logContext, feedDescriptor}) 207 212 return agent.deleteLike(likeUri) 208 213 }, 209 214 }) ··· 212 217 export function usePostRepostMutationQueue( 213 218 post: Shadow<AppBskyFeedDefs.PostView>, 214 219 viaRepost: {uri: string; cid: string} | undefined, 220 + feedDescriptor: string | undefined, 215 221 logContext: LogEvents['post:repost']['logContext'] & 216 222 LogEvents['post:unrepost']['logContext'], 217 223 ) { ··· 219 225 const postUri = post.uri 220 226 const postCid = post.cid 221 227 const initialRepostUri = post.viewer?.repost 222 - const repostMutation = usePostRepostMutation(logContext) 223 - const unrepostMutation = usePostUnrepostMutation(logContext) 228 + const repostMutation = usePostRepostMutation(feedDescriptor, logContext) 229 + const unrepostMutation = usePostUnrepostMutation(feedDescriptor, logContext) 224 230 225 231 const queueToggle = useToggleMutationQueue({ 226 232 initialState: initialRepostUri, ··· 270 276 } 271 277 272 278 function usePostRepostMutation( 279 + feedDescriptor: string | undefined, 273 280 logContext: LogEvents['post:repost']['logContext'], 274 281 ) { 275 282 const agent = useAgent() ··· 279 286 {uri: string; cid: string; via?: {uri: string; cid: string}} // the post's uri and cid, and the repost uri/cid if present 280 287 >({ 281 288 mutationFn: ({uri, cid, via}) => { 282 - logEvent('post:repost', {logContext}) 289 + logger.metric('post:repost', {logContext, feedDescriptor}) 283 290 return agent.repost(uri, cid, via) 284 291 }, 285 292 }) 286 293 } 287 294 288 295 function usePostUnrepostMutation( 296 + feedDescriptor: string | undefined, 289 297 logContext: LogEvents['post:unrepost']['logContext'], 290 298 ) { 291 299 const agent = useAgent() 292 300 return useMutation<void, Error, {postUri: string; repostUri: string}>({ 293 301 mutationFn: ({repostUri}) => { 294 - logEvent('post:unrepost', {logContext}) 302 + logger.metric('post:unrepost', {logContext, feedDescriptor}) 295 303 return agent.deleteRepost(repostUri) 296 304 }, 297 305 }) ··· 363 371 {uri: string} // the root post's uri 364 372 >({ 365 373 mutationFn: ({uri}) => { 366 - logEvent('post:mute', {}) 374 + logger.metric('post:mute', {}) 367 375 return agent.api.app.bsky.graph.muteThread({root: uri}) 368 376 }, 369 377 }) ··· 373 381 const agent = useAgent() 374 382 return useMutation<{}, Error, {uri: string}>({ 375 383 mutationFn: ({uri}) => { 376 - logEvent('post:unmute', {}) 384 + logger.metric('post:unmute', {}) 377 385 return agent.api.app.bsky.graph.unmuteThread({root: uri}) 378 386 }, 379 387 })