[APP-1782] Migrate to new analytics APIs (#9735)

* Migrate logEvent to useAnalytics

* Migrate logger.metric to useAnalytics

* Migrate tricky spot, fix types

* Migrate remaining tricky spot

* Missed one

* Remove metric() from logger

* Migrate useGate to useAnalytics

* Remove all other StatSig mentions

* Update event payload

* Update logger tests

* Mock expo method
This commit is contained in:
Eric Bailey
2026-01-21 19:31:39 -06:00
committed by GitHub
parent 82706fbd19
commit 66af8f1fbf
149 changed files with 985 additions and 1112 deletions
+4 -3
View File
@@ -5,7 +5,6 @@ import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {PressableScale} from '#/lib/custom-animations/PressableScale'
import {logEvent} from '#/lib/statsig/statsig'
import {
useLoggedOutView,
useLoggedOutViewControls,
@@ -18,6 +17,7 @@ import {LandingScreen} from '#/screens/StarterPack/StarterPackLandingScreen'
import {atoms as a, native, tokens, useTheme} from '#/alf'
import {Button, ButtonIcon} from '#/components/Button'
import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times'
import {useAnalytics} from '#/analytics'
import {SplashScreen} from './SplashScreen'
enum ScreenState {
@@ -30,6 +30,7 @@ export {ScreenState as LoggedOutScreenState}
export function LoggedOut({onDismiss}: {onDismiss?: () => void}) {
const {_} = useLingui()
const ax = useAnalytics()
const t = useTheme()
const insets = useSafeAreaInsets()
const setMinimalShellMode = useSetMinimalShellMode()
@@ -94,11 +95,11 @@ export function LoggedOut({onDismiss}: {onDismiss?: () => void}) {
<SplashScreen
onPressSignin={() => {
setScreenState(ScreenState.S_Login)
logEvent('splash:signInPressed', {})
ax.metric('splash:signInPressed', {})
}}
onPressCreateAccount={() => {
setScreenState(ScreenState.S_CreateAccount)
logEvent('splash:createAccountPressed', {})
ax.metric('splash:createAccountPressed', {})
}}
/>
) : undefined}
+5 -3
View File
@@ -72,7 +72,6 @@ import {usePalette} from '#/lib/hooks/usePalette'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {mimeToExt} from '#/lib/media/video/util'
import {type NavigationProp} from '#/lib/routes/types'
import {logEvent} from '#/lib/statsig/statsig'
import {cleanError} from '#/lib/strings/errors'
import {colors} from '#/lib/styles'
import {logger} from '#/logger'
@@ -129,6 +128,7 @@ import {LazyQuoteEmbed} from '#/components/Post/Embed/LazyQuoteEmbed'
import * as Prompt from '#/components/Prompt'
import * as Toast from '#/components/Toast'
import {Text as NewText} from '#/components/Typography'
import {useAnalytics} from '#/analytics'
import {IS_ANDROID, IS_IOS, IS_NATIVE, IS_WEB} from '#/env'
import {BottomSheetPortalProvider} from '../../../../modules/bottom-sheet'
import {PostLanguageSelect} from './select-language/PostLanguageSelect'
@@ -178,6 +178,7 @@ export const ComposePost = ({
cancelRef?: React.RefObject<CancelRef | null>
}) => {
const {currentAccount} = useSession()
const ax = useAnalytics()
const agent = useAgent()
const queryClient = useQueryClient()
const currentDid = currentAccount!.did
@@ -520,7 +521,7 @@ export const ComposePost = ({
if (postUri) {
let index = 0
for (let post of thread.posts) {
logEvent('post:create', {
ax.metric('post:create', {
imageCount:
post.embed.media?.type === 'images'
? post.embed.media.images.length
@@ -536,7 +537,7 @@ export const ComposePost = ({
}
}
if (thread.posts.length > 1) {
logEvent('thread:create', {
ax.metric('thread:create', {
postCount: thread.posts.length,
isReply: !!replyTo,
})
@@ -594,6 +595,7 @@ export const ComposePost = ({
}, 500)
}, [
_,
ax,
agent,
thread,
canPost,
@@ -3,12 +3,12 @@ import {Keyboard} from 'react-native'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {logEvent} from '#/lib/statsig/statsig'
import {type Gif} from '#/state/queries/tenor'
import {atoms as a, useTheme} from '#/alf'
import {Button} from '#/components/Button'
import {GifSelectDialog} from '#/components/dialogs/GifSelect'
import {GifSquare_Stroke2_Corner0_Rounded as GifIcon} from '#/components/icons/Gif'
import {useAnalytics} from '#/analytics'
type Props = {
onClose?: () => void
@@ -17,15 +17,16 @@ type Props = {
}
export function SelectGifBtn({onClose, onSelectGif, disabled}: Props) {
const ax = useAnalytics()
const {_} = useLingui()
const ref = useRef<{open: () => void}>(null)
const t = useTheme()
const onPressSelectGif = useCallback(async () => {
logEvent('composer:gif:open', {})
ax.metric('composer:gif:open', {})
Keyboard.dismiss()
ref.current?.open()
}, [])
}, [ax])
return (
<>
@@ -24,6 +24,7 @@ import {Earth_Stroke2_Corner0_Rounded as EarthIcon} from '#/components/icons/Glo
import {Group3_Stroke2_Corner0_Rounded as GroupIcon} from '#/components/icons/Group'
import * as Tooltip from '#/components/Tooltip'
import {Text} from '#/components/Typography'
import {useAnalytics} from '#/analytics'
import {IS_NATIVE} from '#/env'
import {useThreadgateNudged} from '#/storage/hooks/threadgate-nudged'
@@ -42,6 +43,7 @@ export function ThreadgateBtn({
style?: StyleProp<AnimatedStyle<ViewStyle>>
}) {
const {_} = useLingui()
const ax = useAnalytics()
const control = Dialog.useDialogControl()
const [threadgateNudged, setThreadgateNudged] = useThreadgateNudged()
const [showTooltip, setShowTooltip] = useState(false)
@@ -66,7 +68,7 @@ export function ThreadgateBtn({
const [persist, setPersist] = useState(false)
const onPress = () => {
logger.metric('composer:threadgate:open', {
ax.metric('composer:threadgate:open', {
nudged: tooltipWasShown,
})
+11 -9
View File
@@ -10,7 +10,6 @@ import {
useVideoLibraryPermission,
} from '#/lib/hooks/usePermissions'
import {openCamera, openUnifiedPicker} from '#/lib/media/picker'
import {logger} from '#/logger'
import {useCurrentAccountProfile} from '#/state/queries/useCurrentAccountProfile'
import {MAX_IMAGES} from '#/view/com/composer/state/composer'
import {UserAvatar} from '#/view/com/util/UserAvatar'
@@ -21,11 +20,13 @@ import {Camera_Stroke2_Corner0_Rounded as CameraIcon} from '#/components/icons/C
import {Image_Stroke2_Corner0_Rounded as ImageIcon} from '#/components/icons/Image'
import {SubtleHover} from '#/components/SubtleHover'
import {Text} from '#/components/Typography'
import {useAnalytics} from '#/analytics'
import {IS_NATIVE} from '#/env'
export function ComposerPrompt() {
const {_} = useLingui()
const t = useTheme()
const ax = useAnalytics()
const {_} = useLingui()
const {openComposer} = useOpenComposer()
const profile = useCurrentAccountProfile()
const [hover, setHover] = useState(false)
@@ -35,12 +36,12 @@ export function ComposerPrompt() {
const sheetWrapper = useSheetWrapper()
const onPress = useCallback(() => {
logger.metric('composerPrompt:press', {})
ax.metric('composerPrompt:press', {})
openComposer({})
}, [openComposer])
}, [ax, openComposer])
const onPressImage = useCallback(async () => {
logger.metric('composerPrompt:gallery:press', {})
ax.metric('composerPrompt:gallery:press', {})
// On web, open the composer with the gallery picker auto-opening
if (!IS_NATIVE) {
@@ -87,10 +88,11 @@ export function ComposerPrompt() {
}
} catch (err: any) {
if (!String(err).toLowerCase().includes('cancel')) {
logger.warn('Error opening image picker', {error: err})
ax.logger.error('Error opening image picker', {error: err})
}
}
}, [
ax,
openComposer,
requestPhotoAccessIfNeeded,
requestVideoAccessIfNeeded,
@@ -98,7 +100,7 @@ export function ComposerPrompt() {
])
const onPressCamera = useCallback(async () => {
logger.metric('composerPrompt:camera:press', {})
ax.metric('composerPrompt:camera:press', {})
try {
if (!(await requestCameraAccessIfNeeded())) {
@@ -126,10 +128,10 @@ export function ComposerPrompt() {
})
} catch (err: any) {
if (!String(err).toLowerCase().includes('cancel')) {
logger.warn('Error opening camera', {error: err})
ax.logger.error('Error opening camera', {error: err})
}
}
}, [openComposer, requestCameraAccessIfNeeded])
}, [ax, openComposer, requestCameraAccessIfNeeded])
if (!profile) {
return null
+6 -5
View File
@@ -18,7 +18,6 @@ import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
import {ComposeIcon2} from '#/lib/icons'
import {getRootNavigation, getTabState, TabState} from '#/lib/routes/helpers'
import {type AllNavigatorParams} from '#/lib/routes/types'
import {logEvent} from '#/lib/statsig/statsig'
import {s} from '#/lib/styles'
import {listenSoftReset} from '#/state/events'
import {FeedFeedbackProvider, useFeedFeedback} from '#/state/feed-feedback'
@@ -33,6 +32,7 @@ import {truncateAndInvalidate} from '#/state/queries/util'
import {useSession} from '#/state/session'
import {useSetMinimalShellMode} from '#/state/shell'
import {useHeaderOffset} from '#/components/hooks/useHeaderOffset'
import {useAnalytics} from '#/analytics'
import {IS_NATIVE} from '#/env'
import {PostFeed} from '../posts/PostFeed'
import {FAB} from '../util/fab/FAB'
@@ -63,6 +63,7 @@ export function FeedPage({
savedFeedConfig?: AppBskyActorDefs.SavedFeed
feedInfo: FeedSourceInfo
}) {
const ax = useAnalytics()
const {hasSession} = useSession()
const {_} = useLingui()
const navigation = useNavigation<NavigationProp<AllNavigatorParams>>()
@@ -105,13 +106,13 @@ export function FeedPage({
scrollToTop()
truncateAndInvalidate(queryClient, FEED_RQKEY(feed))
setHasNew(false)
logEvent('feed:refresh', {
ax.metric('feed:refresh', {
feedType: feed.split('|')[0],
feedUrl: feed,
reason: 'soft-reset',
})
}
}, [navigation, isPageFocused, scrollToTop, queryClient, feed])
}, [ax, navigation, isPageFocused, scrollToTop, queryClient, feed])
// fires when page within screen is activated/deactivated
useEffect(() => {
@@ -129,12 +130,12 @@ export function FeedPage({
scrollToTop()
truncateAndInvalidate(queryClient, FEED_RQKEY(feed))
setHasNew(false)
logEvent('feed:refresh', {
ax.metric('feed:refresh', {
feedType: feed.split('|')[0],
feedUrl: feed,
reason: 'load-latest',
})
}, [scrollToTop, feed, queryClient])
}, [ax, scrollToTop, feed, queryClient])
const shouldPrefetch = IS_NATIVE && isPageAdjacent
const isDiscoverFeed = feedInfo.uri === DISCOVER_FEED_URI
+3 -2
View File
@@ -12,14 +12,15 @@ import {usePalette} from '#/lib/hooks/usePalette'
import {MagnifyingGlassIcon} from '#/lib/icons'
import {type NavigationProp} from '#/lib/routes/types'
import {s} from '#/lib/styles'
import {logger} from '#/logger'
import {useFeedFeedbackContext} from '#/state/feed-feedback'
import {useSession} from '#/state/session'
import {useAnalytics} from '#/analytics'
import {IS_WEB} from '#/env'
import {Button} from '../util/forms/Button'
import {Text} from '../util/text/Text'
export function CustomFeedEmptyState() {
const ax = useAnalytics()
const feedFeedback = useFeedFeedbackContext()
const {currentAccount} = useSession()
const hasLoggedDiscoverEmptyErrorRef = React.useRef(false)
@@ -33,7 +34,7 @@ export function CustomFeedEmptyState() {
!hasLoggedDiscoverEmptyErrorRef.current
) {
hasLoggedDiscoverEmptyErrorRef.current = true
logger.metric('feed:discover:emptyError', {
ax.metric('feed:discover:emptyError', {
userDid: currentAccount.did,
})
}
+24 -34
View File
@@ -31,7 +31,6 @@ import {isStatusStillActive, validateStatus} from '#/lib/actor-status'
import {DISCOVER_FEED_URI, KNOWN_SHUTDOWN_FEEDS} from '#/lib/constants'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
import {logEvent} from '#/lib/statsig/statsig'
import {isNetworkError} from '#/lib/strings/errors'
import {logger} from '#/logger'
import {usePostAuthorShadowFilter} from '#/state/cache/profile-shadow'
@@ -69,6 +68,7 @@ import {
} from '#/components/feeds/PostFeedVideoGridRow'
import {TrendingInterstitial} from '#/components/interstitials/Trending'
import {TrendingVideos as TrendingVideosInterstitial} from '#/components/interstitials/TrendingVideos'
import {useAnalytics} from '#/analytics'
import {IS_IOS, IS_NATIVE, IS_WEB} from '#/env'
import {DiscoverFeedLiveEventFeedsAndTrendingBanner} from '#/features/liveEvents/components/DiscoverFeedLiveEventFeedsAndTrendingBanner'
import {ComposerPrompt} from '../feeds/ComposerPrompt'
@@ -232,6 +232,7 @@ let PostFeed = ({
initialNumToRender?: number
isVideoFeed?: boolean
}): React.ReactNode => {
const ax = useAnalytics()
const {_} = useLingui()
const queryClient = useQueryClient()
const {currentAccount, hasSession} = useSession()
@@ -685,7 +686,7 @@ let PostFeed = ({
// =
const onRefresh = useCallback(async () => {
logEvent('feed:refresh', {
ax.metric('feed:refresh', {
feedType: feedType,
feedUrl: feed,
reason: 'pull-to-refresh',
@@ -698,12 +699,12 @@ let PostFeed = ({
logger.error('Failed to refresh posts feed', {message: err})
}
setIsPTRing(false)
}, [refetch, setIsPTRing, onHasNew, feed, feedType])
}, [ax, refetch, setIsPTRing, onHasNew, feed, feedType])
const onEndReached = useCallback(async () => {
if (isFetching || !hasNextPage || isError) return
logEvent('feed:endReached', {
ax.metric('feed:endReached', {
feedType: feedType,
feedUrl: feed,
itemCount: feedItems.length,
@@ -714,6 +715,7 @@ let PostFeed = ({
logger.error('Failed to load more posts', {message: err})
}
}, [
ax,
isFetching,
hasNextPage,
isError,
@@ -933,17 +935,13 @@ let PostFeed = ({
const position = getPostPosition('sliceItem', item.key)
logger.metric(
'post:view',
{
uri: post.uri,
authorDid: post.author.did,
logContext: 'FeedItem',
feedDescriptor: feedFeedback.feedDescriptor || feed,
position,
},
{statsig: false},
)
ax.metric('post:view', {
uri: post.uri,
authorDid: post.author.did,
logContext: 'FeedItem',
feedDescriptor: feedFeedback.feedDescriptor || feed,
position,
})
}
// Live status tracking (existing code)
@@ -955,14 +953,10 @@ let PostFeed = ({
) {
if (!seenActorWithStatusRef.current.has(actor.did)) {
seenActorWithStatusRef.current.add(actor.did)
logger.metric(
'live:view:post',
{
subject: actor.did,
feed,
},
{statsig: false},
)
ax.metric('live:view:post', {
subject: actor.did,
feed,
})
}
}
} else if (item.type === 'videoGridRow') {
@@ -976,17 +970,13 @@ let PostFeed = ({
const position = getPostPosition('videoGridRow', item.key)
logger.metric(
'post:view',
{
uri: post.uri,
authorDid: post.author.did,
logContext: 'FeedItem',
feedDescriptor: feedFeedback.feedDescriptor || feed,
position,
},
{statsig: false},
)
ax.metric('post:view', {
uri: post.uri,
authorDid: post.author.did,
logContext: 'FeedItem',
feedDescriptor: feedFeedback.feedDescriptor || feed,
position,
})
}
}
}
+5 -4
View File
@@ -18,7 +18,6 @@ import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
import {usePalette} from '#/lib/hooks/usePalette'
import {makeProfileLink} from '#/lib/routes/links'
import {countLines} from '#/lib/strings/helpers'
import {logger} from '#/logger'
import {
POST_TOMBSTONE,
type Shadow,
@@ -48,6 +47,7 @@ import {PostControls} from '#/components/PostControls'
import {DiscoverDebug} from '#/components/PostControls/DiscoverDebug'
import {RichText} from '#/components/RichText'
import {SubtleHover} from '#/components/SubtleHover'
import {useAnalytics} from '#/analytics'
import * as bsky from '#/types/bsky'
import {PostFeedReason} from './PostFeedReason'
@@ -158,6 +158,7 @@ let FeedItemInner = ({
rootPost: AppBskyFeedDefs.PostView
onShowLess?: (interaction: AppBskyFeedDefs.Interaction) => void
}): React.ReactNode => {
const ax = useAnalytics()
const queryClient = useQueryClient()
const {openComposer} = useOpenComposer()
const pal = usePalette('default')
@@ -198,7 +199,7 @@ let FeedItemInner = ({
feedContext,
reqId,
})
logger.metric('post:clickthroughAuthor', {
ax.metric('post:clickthroughAuthor', {
uri: post.uri,
authorDid: post.author.did,
logContext: 'FeedItem',
@@ -222,7 +223,7 @@ let FeedItemInner = ({
feedContext,
reqId,
})
logger.metric('post:clickthroughEmbed', {
ax.metric('post:clickthroughEmbed', {
uri: post.uri,
authorDid: post.author.did,
logContext: 'FeedItem',
@@ -237,7 +238,7 @@ let FeedItemInner = ({
feedContext,
reqId,
})
logger.metric('post:clickthroughItem', {
ax.metric('post:clickthroughItem', {
uri: post.uri,
authorDid: post.author.did,
logContext: 'FeedItem',
+12 -14
View File
@@ -12,6 +12,7 @@ import {useResolveDidQuery} from '#/state/queries/resolve-uri'
import {useSession} from '#/state/session'
import {PeopleRemove2_Stroke1_Corner0_Rounded as PeopleRemoveIcon} from '#/components/icons/PeopleRemove2'
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
import {useAnalytics} from '#/analytics'
import {List} from '../util/List'
import {ProfileCardWithFollowBtn} from './ProfileCard'
@@ -41,6 +42,7 @@ function keyExtractor(item: ActorDefs.ProfileViewBasic) {
export function ProfileFollowers({name}: {name: string}) {
const {_} = useLingui()
const ax = useAnalytics()
const navigation = useNavigation()
const initialNumToRender = useInitialNumToRender()
const {currentAccount} = useSession()
@@ -88,14 +90,14 @@ export function ProfileFollowers({name}: {name: string}) {
currentPageCount >= 3 &&
currentPageCount > paginationTrackingRef.current.page
) {
logger.metric('profile:followers:paginate', {
ax.metric('profile:followers:paginate', {
contextProfileDid: resolvedDid,
itemCount: followers.length,
page: currentPageCount,
})
}
paginationTrackingRef.current.page = currentPageCount
}, [data?.pages?.length, resolvedDid, followers.length])
}, [ax, data?.pages?.length, resolvedDid, followers.length])
const onRefresh = React.useCallback(async () => {
setIsPTRing(true)
@@ -125,12 +127,12 @@ export function ProfileFollowers({name}: {name: string}) {
// track pageview
React.useEffect(() => {
if (resolvedDid) {
logger.metric('profile:followers:view', {
ax.metric('profile:followers:view', {
contextProfileDid: resolvedDid,
isOwnProfile: isMe,
})
}
}, [resolvedDid, isMe])
}, [ax, resolvedDid, isMe])
// track seen items
const seenItemsRef = React.useRef<Set<string>>(new Set())
@@ -147,17 +149,13 @@ export function ProfileFollowers({name}: {name: string}) {
if (position === 0) {
return
}
logger.metric(
'profileCard:seen',
{
profileDid: item.did,
position,
...(resolvedDid !== undefined && {contextProfileDid: resolvedDid}),
},
{statsig: false},
)
ax.metric('profileCard:seen', {
profileDid: item.did,
position,
...(resolvedDid !== undefined && {contextProfileDid: resolvedDid}),
})
},
[followers, resolvedDid],
[ax, followers, resolvedDid],
)
if (followers.length < 1) {
+12 -14
View File
@@ -14,6 +14,7 @@ import {useSession} from '#/state/session'
import {FindContactsBannerNUX} from '#/components/contacts/FindContactsBannerNUX'
import {PeopleRemove2_Stroke1_Corner0_Rounded as PeopleRemoveIcon} from '#/components/icons/PeopleRemove2'
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
import {useAnalytics} from '#/analytics'
import {IS_WEB} from '#/env'
import {List} from '../util/List'
import {ProfileCardWithFollowBtn} from './ProfileCard'
@@ -44,6 +45,7 @@ function keyExtractor(item: ActorDefs.ProfileViewBasic) {
export function ProfileFollows({name}: {name: string}) {
const {_} = useLingui()
const ax = useAnalytics()
const initialNumToRender = useInitialNumToRender()
const {currentAccount} = useSession()
const navigation = useNavigation<NavigationProp>()
@@ -100,14 +102,14 @@ export function ProfileFollows({name}: {name: string}) {
currentPageCount >= 3 &&
currentPageCount > paginationTrackingRef.current.page
) {
logger.metric('profile:following:paginate', {
ax.metric('profile:following:paginate', {
contextProfileDid: resolvedDid,
itemCount: follows.length,
page: currentPageCount,
})
}
paginationTrackingRef.current.page = currentPageCount
}, [data?.pages?.length, resolvedDid, follows.length])
}, [ax, data?.pages?.length, resolvedDid, follows.length])
const onRefresh = React.useCallback(async () => {
setIsPTRing(true)
@@ -137,12 +139,12 @@ export function ProfileFollows({name}: {name: string}) {
// track pageview
React.useEffect(() => {
if (resolvedDid) {
logger.metric('profile:following:view', {
ax.metric('profile:following:view', {
contextProfileDid: resolvedDid,
isOwnProfile: isMe,
})
}
}, [resolvedDid, isMe])
}, [ax, resolvedDid, isMe])
// track seen items
const seenItemsRef = React.useRef<Set<string>>(new Set())
@@ -159,17 +161,13 @@ export function ProfileFollows({name}: {name: string}) {
if (position === 0) {
return
}
logger.metric(
'profileCard:seen',
{
profileDid: item.did,
position,
...(resolvedDid !== undefined && {contextProfileDid: resolvedDid}),
},
{statsig: false},
)
ax.metric('profileCard:seen', {
profileDid: item.did,
position,
...(resolvedDid !== undefined && {contextProfileDid: resolvedDid}),
})
},
[follows, resolvedDid],
[ax, follows, resolvedDid],
)
if (follows.length < 1) {
+13 -12
View File
@@ -11,7 +11,6 @@ import {makeProfileLink} from '#/lib/routes/links'
import {type NavigationProp} from '#/lib/routes/types'
import {shareText, shareUrl} from '#/lib/sharing'
import {toShareUrl} from '#/lib/strings/url-helpers'
import {logger} from '#/logger'
import {type Shadow} from '#/state/cache/types'
import {useModalControls} from '#/state/modals'
import {Nux, useNux, useSaveNux} from '#/state/queries/nuxs'
@@ -60,6 +59,7 @@ import * as Prompt from '#/components/Prompt'
import {useFullVerificationState} from '#/components/verification'
import {VerificationCreatePrompt} from '#/components/verification/VerificationCreatePrompt'
import {VerificationRemovePrompt} from '#/components/verification/VerificationRemovePrompt'
import {useAnalytics} from '#/analytics'
import {IS_WEB} from '#/env'
import {Dot} from '#/features/nuxs/components/Dot'
import {Gradient} from '#/features/nuxs/components/Gradient'
@@ -71,6 +71,7 @@ let ProfileMenu = ({
profile: Shadow<AppBskyActorDefs.ProfileViewDetailed>
}): React.ReactNode => {
const t = useTheme()
const ax = useAnalytics()
const {_} = useLingui()
const {currentAccount, hasSession} = useSession()
const {openModal} = useModalControls()
@@ -121,7 +122,7 @@ let ProfileMenu = ({
}, [queryClient, profile.did])
const onPressAddToStarterPacks = React.useCallback(() => {
logger.metric('profile:addToStarterPack', {})
ax.metric('profile:addToStarterPack', {})
addToStarterPacksDialogControl.open()
}, [addToStarterPacksDialogControl])
@@ -147,7 +148,7 @@ let ProfileMenu = ({
Toast.show(_(msg({message: 'Account unmuted', context: 'toast'})))
} catch (e: any) {
if (e?.name !== 'AbortError') {
logger.error('Failed to unmute account', {message: e})
ax.logger.error('Failed to unmute account', {message: e})
Toast.show(_(msg`There was an issue! ${e.toString()}`), 'xmark')
}
}
@@ -157,12 +158,12 @@ let ProfileMenu = ({
Toast.show(_(msg({message: 'Account muted', context: 'toast'})))
} catch (e: any) {
if (e?.name !== 'AbortError') {
logger.error('Failed to mute account', {message: e})
ax.logger.error('Failed to mute account', {message: e})
Toast.show(_(msg`There was an issue! ${e.toString()}`), 'xmark')
}
}
}
}, [profile.viewer?.muted, queueUnmute, _, queueMute])
}, [ax, profile.viewer?.muted, queueUnmute, _, queueMute])
const blockAccount = React.useCallback(async () => {
if (profile.viewer?.blocking) {
@@ -171,7 +172,7 @@ let ProfileMenu = ({
Toast.show(_(msg({message: 'Account unblocked', context: 'toast'})))
} catch (e: any) {
if (e?.name !== 'AbortError') {
logger.error('Failed to unblock account', {message: e})
ax.logger.error('Failed to unblock account', {message: e})
Toast.show(_(msg`There was an issue! ${e.toString()}`), 'xmark')
}
}
@@ -181,12 +182,12 @@ let ProfileMenu = ({
Toast.show(_(msg({message: 'Account blocked', context: 'toast'})))
} catch (e: any) {
if (e?.name !== 'AbortError') {
logger.error('Failed to block account', {message: e})
ax.logger.error('Failed to block account', {message: e})
Toast.show(_(msg`There was an issue! ${e.toString()}`), 'xmark')
}
}
}
}, [profile.viewer?.blocking, _, queueUnblock, queueBlock])
}, [ax, profile.viewer?.blocking, _, queueUnblock, queueBlock])
const onPressFollowAccount = React.useCallback(async () => {
try {
@@ -194,11 +195,11 @@ let ProfileMenu = ({
Toast.show(_(msg({message: 'Account followed', context: 'toast'})))
} catch (e: any) {
if (e?.name !== 'AbortError') {
logger.error('Failed to follow account', {message: e})
ax.logger.error('Failed to follow account', {message: e})
Toast.show(_(msg`There was an issue! ${e.toString()}`), 'xmark')
}
}
}, [_, queueFollow])
}, [_, ax, queueFollow])
const onPressUnfollowAccount = React.useCallback(async () => {
try {
@@ -206,11 +207,11 @@ let ProfileMenu = ({
Toast.show(_(msg({message: 'Account unfollowed', context: 'toast'})))
} catch (e: any) {
if (e?.name !== 'AbortError') {
logger.error('Failed to unfollow account', {message: e})
ax.logger.error('Failed to unfollow account', {message: e})
Toast.show(_(msg`There was an issue! ${e.toString()}`), 'xmark')
}
}
}, [_, queueUnfollow])
}, [_, ax, queueUnfollow])
const onPressReportAccount = React.useCallback(() => {
reportDialogControl.open()
+3 -5
View File
@@ -52,6 +52,7 @@ import {LiveStatusDialog} from '#/components/live/LiveStatusDialog'
import {MediaInsetBorder} from '#/components/MediaInsetBorder'
import * as Menu from '#/components/Menu'
import {ProfileHoverCard} from '#/components/ProfileHoverCard'
import {useAnalytics} from '#/analytics'
import {IS_ANDROID, IS_NATIVE, IS_WEB, IS_WEB_TOUCH_DEVICE} from '#/env'
import type * as bsky from '#/types/bsky'
@@ -530,6 +531,7 @@ let PreviewableUserAvatar = ({
live,
...props
}: PreviewableUserAvatarProps): React.ReactNode => {
const ax = useAnalytics()
const {_} = useLingui()
const queryClient = useQueryClient()
const status = useActorStatus(profile)
@@ -543,11 +545,7 @@ let PreviewableUserAvatar = ({
const onOpenLiveStatus = useCallback(() => {
playHaptic('Light')
logger.metric(
'live:card:open',
{subject: profile.did, from: 'post'},
{statsig: true},
)
ax.metric('live:card:open', {subject: profile.did, from: 'post'})
liveControl.open()
}, [liveControl, playHaptic, profile.did])
+5 -4
View File
@@ -11,7 +11,6 @@ import {
type HomeTabNavigatorParams,
type NativeStackScreenProps,
} from '#/lib/routes/types'
import {logEvent} from '#/lib/statsig/statsig'
import {emitSoftReset} from '#/state/events'
import {
type SavedFeedSourceInfo,
@@ -36,6 +35,7 @@ import {FollowingEmptyState} from '#/view/com/posts/FollowingEmptyState'
import {FollowingEndOfFeed} from '#/view/com/posts/FollowingEndOfFeed'
import {NoFeedsPinned} from '#/screens/Home/NoFeedsPinned'
import * as Layout from '#/components/Layout'
import {useAnalytics} from '#/analytics'
import {IS_WEB} from '#/env'
import {useDemoMode} from '#/storage/hooks/demo-mode'
@@ -105,6 +105,7 @@ function HomeScreenReady({
preferences: UsePreferencesQueryResponse
pinnedFeedInfos: SavedFeedSourceInfo[]
}) {
const ax = useAnalytics()
const allFeeds = React.useMemo(
() => pinnedFeedInfos.map(f => f.feedDescriptor),
[pinnedFeedInfos],
@@ -147,7 +148,7 @@ function HomeScreenReady({
useFocusEffect(
useNonReactiveCallback(() => {
if (maybeSelectedFeed) {
logEvent('home:feedDisplayed', {
ax.metric('home:feedDisplayed', {
index: selectedIndex,
feedType: maybeSelectedFeed.split('|')[0],
feedUrl: maybeSelectedFeed,
@@ -168,14 +169,14 @@ function HomeScreenReady({
setSelectedFeed(maybeFeed)
if (maybeFeed) {
logEvent('home:feedDisplayed', {
ax.metric('home:feedDisplayed', {
index,
feedType: maybeFeed.split('|')[0],
feedUrl: maybeFeed,
})
}
},
[setSelectedFeed, setMinimalShellMode, allFeeds],
[ax, setSelectedFeed, setMinimalShellMode, allFeeds],
)
const onPressSelected = React.useCallback(() => {
+6 -9
View File
@@ -5,7 +5,6 @@ import {useNavigation, useNavigationState} from '@react-navigation/native'
import {getCurrentRoute} from '#/lib/routes/helpers'
import {type NavigationProp} from '#/lib/routes/types'
import {logger} from '#/logger'
import {emitSoftReset} from '#/state/events'
import {
type SavedFeedSourceInfo,
@@ -19,10 +18,12 @@ import {FilterTimeline_Stroke2_Corner0_Rounded as FilterTimeline} from '#/compon
import {PlusSmall_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
import {Link} from '#/components/Link'
import {Text} from '#/components/Typography'
import {useAnalytics} from '#/analytics'
export function DesktopFeeds() {
const t = useTheme()
const {_} = useLingui()
const ax = useAnalytics()
const {data: pinnedFeedInfos, error, isLoading} = usePinnedFeedsInfos()
const selectedFeed = useSelectedFeed()
const setSelectedFeed = useSetSelectedFeed()
@@ -86,14 +87,10 @@ export function DesktopFeeds() {
feedInfo={feedInfo}
current={current}
onPress={() => {
logger.metric(
'desktopFeeds:feed:click',
{
feedUri: feedInfo.uri,
feedDescriptor: feed,
},
{statsig: false},
)
ax.metric('desktopFeeds:feed:click', {
feedUri: feedInfo.uri,
feedDescriptor: feed,
})
setSelectedFeed(feed)
navigation.navigate('Home')
if (route.name === 'Home' && feed === selectedFeed) {
@@ -2,7 +2,6 @@ import {View} from 'react-native'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {logger} from '#/logger'
import {
useTrendingSettings,
useTrendingSettingsApi,
@@ -16,6 +15,7 @@ import {Trending3_Stroke2_Corner1_Rounded as TrendingIcon} from '#/components/ic
import * as Prompt from '#/components/Prompt'
import {TrendingTopicLink} from '#/components/TrendingTopics'
import {Text} from '#/components/Typography'
import {useAnalytics} from '#/analytics'
const TRENDING_LIMIT = 5
@@ -28,13 +28,14 @@ export function SidebarTrendingTopics() {
function Inner() {
const t = useTheme()
const {_} = useLingui()
const ax = useAnalytics()
const trendingPrompt = Prompt.usePromptControl()
const {setTrendingDisabled} = useTrendingSettingsApi()
const {data: trending, error, isLoading} = useTrendingTopics()
const noTopics = !isLoading && !error && !trending?.topics?.length
const onConfirmHide = () => {
logger.metric('trendingTopics:hide', {context: 'sidebar'})
ax.metric('trendingTopics:hide', {context: 'sidebar'})
setTrendingDisabled(true)
}
@@ -90,7 +91,7 @@ function Inner() {
topic={topic}
style={[a.self_start]}
onPress={() => {
logger.metric('trendingTopic:click', {context: 'sidebar'})
ax.metric('trendingTopic:click', {context: 'sidebar'})
}}>
{({hovered}) => (
<View style={[a.flex_row, a.align_center, a.gap_xs]}>