From 17e7590bcd36f9ec3433cb2714a9319fac4aeebf Mon Sep 17 00:00:00 2001 From: Ansh Date: Thu, 15 Jun 2023 14:45:14 -0700 Subject: [APP-511] metrics overhaul: frontend work (#506) * WIP * fix types and update imports * wip * tagged events that should be server side * remove server-side analytics * remove useless import * add additional profile header events * remove useless import * track follow/unfollow clicks * add missing types --- src/App.native.tsx | 2 +- src/App.web.tsx | 2 +- src/lib/analytics.tsx | 118 ---------------------- src/lib/analytics.web.tsx | 65 ------------ src/lib/analytics/analytics.tsx | 128 ++++++++++++++++++++++++ src/lib/analytics/analytics.web.tsx | 65 ++++++++++++ src/lib/analytics/types.ts | 98 ++++++++++++++++++ src/view/com/auth/LoggedOut.tsx | 2 +- src/view/com/auth/create/CreateAccount.tsx | 2 +- src/view/com/auth/login/Login.tsx | 2 +- src/view/com/composer/Composer.tsx | 2 +- src/view/com/composer/photos/OpenCameraBtn.tsx | 2 +- src/view/com/composer/photos/SelectPhotoBtn.tsx | 2 +- src/view/com/lists/ListItems.tsx | 2 +- src/view/com/lists/ListsList.tsx | 2 +- src/view/com/modals/ChangeHandle.tsx | 2 +- src/view/com/modals/CreateOrEditMuteList.tsx | 2 +- src/view/com/modals/EditProfile.tsx | 2 +- src/view/com/posts/Feed.tsx | 2 +- src/view/com/posts/FeedItem.tsx | 2 +- src/view/com/posts/MultiFeed.tsx | 2 +- src/view/com/profile/ProfileHeader.tsx | 9 +- src/view/com/search/HeaderWithInput.tsx | 2 +- src/view/com/util/ViewHeader.tsx | 2 +- src/view/screens/AppPasswords.tsx | 2 +- src/view/screens/Home.tsx | 2 +- src/view/screens/Moderation.tsx | 2 +- src/view/screens/ModerationBlockedAccounts.tsx | 2 +- src/view/screens/ModerationMutedAccounts.tsx | 2 +- src/view/screens/Notifications.tsx | 2 +- src/view/screens/Profile.tsx | 2 +- src/view/screens/SavedFeeds.tsx | 2 +- src/view/screens/Settings.tsx | 2 +- src/view/shell/Drawer.tsx | 2 +- src/view/shell/bottom-bar/BottomBar.tsx | 6 +- 35 files changed, 330 insertions(+), 215 deletions(-) delete mode 100644 src/lib/analytics.tsx delete mode 100644 src/lib/analytics.web.tsx create mode 100644 src/lib/analytics/analytics.tsx create mode 100644 src/lib/analytics/analytics.web.tsx create mode 100644 src/lib/analytics/types.ts (limited to 'src') diff --git a/src/App.native.tsx b/src/App.native.tsx index afab82368..a02ca62c8 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -13,7 +13,7 @@ import * as view from './view/index' import {RootStoreModel, setupState, RootStoreProvider} from './state' import {Shell} from './view/shell' import * as notifee from 'lib/notifee' -import * as analytics from 'lib/analytics' +import * as analytics from 'lib/analytics/analytics' import * as Toast from './view/com/util/Toast' import {handleLink} from './Navigation' diff --git a/src/App.web.tsx b/src/App.web.tsx index 7570db44e..b0f949b8b 100644 --- a/src/App.web.tsx +++ b/src/App.web.tsx @@ -3,7 +3,7 @@ import 'lib/sentry' // must be relatively on top import {SafeAreaProvider} from 'react-native-safe-area-context' import {RootSiblingParent} from 'react-native-root-siblings' import * as view from './view/index' -import * as analytics from 'lib/analytics' +import * as analytics from 'lib/analytics/analytics' import {RootStoreModel, setupState, RootStoreProvider} from './state' import {Shell} from './view/shell/index' import {ToastContainer} from './view/com/util/Toast.web' diff --git a/src/lib/analytics.tsx b/src/lib/analytics.tsx deleted file mode 100644 index d0a8f1243..000000000 --- a/src/lib/analytics.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import React from 'react' -import {AppState, AppStateStatus} from 'react-native' -import { - createClient, - AnalyticsProvider, - useAnalytics as useAnalyticsOrig, -} from '@segment/analytics-react-native' -import {RootStoreModel, AppInfo} from 'state/models/root-store' -import {useStores} from 'state/models/root-store' -import {sha256} from 'js-sha256' - -const segmentClient = createClient({ - writeKey: '8I6DsgfiSLuoONyaunGoiQM7A6y2ybdI', - trackAppLifecycleEvents: false, -}) - -export function useAnalytics() { - const store = useStores() - const methods = useAnalyticsOrig() - return React.useMemo(() => { - if (store.session.hasSession) { - return methods - } - // dont send analytics pings for anonymous users - return { - screen: () => {}, - track: () => {}, - identify: () => {}, - flush: () => {}, - group: () => {}, - alias: () => {}, - reset: () => {}, - } - }, [store, methods]) -} - -export function init(store: RootStoreModel) { - store.onSessionLoaded(() => { - const sess = store.session.currentSession - if (sess) { - if (sess.email) { - store.log.debug('Ping w/hash') - const email_hashed = sha256(sess.email) - segmentClient.identify(email_hashed, {email_hashed}) - } else { - store.log.debug('Ping w/o hash') - segmentClient.identify() - } - } - }) - - // NOTE - // this is a copy of segment's own lifecycle event tracking - // we handle it manually to ensure that it never fires while the app is backgrounded - // -prf - segmentClient.isReady.onChange(() => { - if (AppState.currentState !== 'active') { - store.log.debug('Prevented a metrics ping while the app was backgrounded') - return - } - const context = segmentClient.context.get() - if (typeof context?.app === 'undefined') { - store.log.debug('Aborted metrics ping due to unavailable context') - return - } - - const oldAppInfo = store.appInfo - const newAppInfo = context.app as AppInfo - store.setAppInfo(newAppInfo) - store.log.debug('Recording app info', {new: newAppInfo, old: oldAppInfo}) - - if (typeof oldAppInfo === 'undefined') { - if (store.session.hasSession) { - segmentClient.track('Application Installed', { - version: newAppInfo.version, - build: newAppInfo.build, - }) - } - } else if (newAppInfo.version !== oldAppInfo.version) { - if (store.session.hasSession) { - segmentClient.track('Application Updated', { - version: newAppInfo.version, - build: newAppInfo.build, - previous_version: oldAppInfo.version, - previous_build: oldAppInfo.build, - }) - } - } - if (store.session.hasSession) { - segmentClient.track('Application Opened', { - from_background: false, - version: newAppInfo.version, - build: newAppInfo.build, - }) - } - }) - - let lastState: AppStateStatus = AppState.currentState - AppState.addEventListener('change', (state: AppStateStatus) => { - if (state === 'active' && lastState !== 'active') { - const context = segmentClient.context.get() - segmentClient.track('Application Opened', { - from_background: true, - version: context?.app?.version, - build: context?.app?.build, - }) - } else if (state !== 'active' && lastState === 'active') { - segmentClient.track('Application Backgrounded') - } - lastState = state - }) -} - -export function Provider({children}: React.PropsWithChildren<{}>) { - return ( - {children} - ) -} diff --git a/src/lib/analytics.web.tsx b/src/lib/analytics.web.tsx deleted file mode 100644 index 467ae278b..000000000 --- a/src/lib/analytics.web.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React from 'react' -import { - createClient, - AnalyticsProvider, - useAnalytics as useAnalyticsOrig, -} from '@segment/analytics-react' -import {RootStoreModel} from 'state/models/root-store' -import {useStores} from 'state/models/root-store' -import {sha256} from 'js-sha256' - -const segmentClient = createClient( - { - writeKey: '8I6DsgfiSLuoONyaunGoiQM7A6y2ybdI', - }, - { - integrations: { - 'Segment.io': { - apiHost: 'api.evt.bsky.app/v1', - }, - }, - }, -) -export const track = segmentClient?.track?.bind?.(segmentClient) - -export function useAnalytics() { - const store = useStores() - const methods = useAnalyticsOrig() - return React.useMemo(() => { - if (store.session.hasSession) { - return methods - } - // dont send analytics pings for anonymous users - return { - screen: () => {}, - track: () => {}, - identify: () => {}, - flush: () => {}, - group: () => {}, - alias: () => {}, - reset: () => {}, - } - }, [store, methods]) -} - -export function init(store: RootStoreModel) { - store.onSessionLoaded(() => { - const sess = store.session.currentSession - if (sess) { - if (sess.email) { - store.log.debug('Ping w/hash') - const email_hashed = sha256(sess.email) - segmentClient.identify(email_hashed, {email_hashed}) - } else { - store.log.debug('Ping w/o hash') - segmentClient.identify() - } - } - }) -} - -export function Provider({children}: React.PropsWithChildren<{}>) { - return ( - {children} - ) -} diff --git a/src/lib/analytics/analytics.tsx b/src/lib/analytics/analytics.tsx new file mode 100644 index 000000000..d9d53e6a9 --- /dev/null +++ b/src/lib/analytics/analytics.tsx @@ -0,0 +1,128 @@ +import React from 'react' +import {AppState, AppStateStatus} from 'react-native' +import { + createClient, + AnalyticsProvider, + useAnalytics as useAnalyticsOrig, + ClientMethods, +} from '@segment/analytics-react-native' +import {RootStoreModel, AppInfo} from 'state/models/root-store' +import {useStores} from 'state/models/root-store' +import {sha256} from 'js-sha256' +import {ScreenEvent, TrackEvent} from './types' + +const segmentClient = createClient({ + writeKey: '8I6DsgfiSLuoONyaunGoiQM7A6y2ybdI', + trackAppLifecycleEvents: false, +}) + +export function useAnalytics() { + const store = useStores() + const methods: ClientMethods = useAnalyticsOrig() + return React.useMemo(() => { + if (store.session.hasSession) { + return { + screen: methods.screen as ScreenEvent, // ScreenEvents defines all the possible screen names + track: methods.track as TrackEvent, // TrackEvents defines all the possible track events and their properties + identify: methods.identify, + flush: methods.flush, + group: methods.group, + alias: methods.alias, + reset: methods.reset, + } + } + // dont send analytics pings for anonymous users + return { + screen: () => Promise, + track: () => Promise, + identify: () => Promise, + flush: () => Promise, + group: () => Promise, + alias: () => Promise, + reset: () => Promise, + } + }, [store, methods]) +} + +export function init(store: RootStoreModel) { + store.onSessionLoaded(() => { + const sess = store.session.currentSession + if (sess) { + if (sess.email) { + store.log.debug('Ping w/hash') + const email_hashed = sha256(sess.email) + segmentClient.identify(email_hashed, {email_hashed}) + } else { + store.log.debug('Ping w/o hash') + segmentClient.identify() + } + } + }) + + // NOTE + // this is a copy of segment's own lifecycle event tracking + // we handle it manually to ensure that it never fires while the app is backgrounded + // -prf + segmentClient.isReady.onChange(() => { + if (AppState.currentState !== 'active') { + store.log.debug('Prevented a metrics ping while the app was backgrounded') + return + } + const context = segmentClient.context.get() + if (typeof context?.app === 'undefined') { + store.log.debug('Aborted metrics ping due to unavailable context') + return + } + + const oldAppInfo = store.appInfo + const newAppInfo = context.app as AppInfo + store.setAppInfo(newAppInfo) + store.log.debug('Recording app info', {new: newAppInfo, old: oldAppInfo}) + + if (typeof oldAppInfo === 'undefined') { + if (store.session.hasSession) { + segmentClient.track('Application Installed', { + version: newAppInfo.version, + build: newAppInfo.build, + }) + } + } else if (newAppInfo.version !== oldAppInfo.version) { + if (store.session.hasSession) { + segmentClient.track('Application Updated', { + version: newAppInfo.version, + build: newAppInfo.build, + previous_version: oldAppInfo.version, + previous_build: oldAppInfo.build, + }) + } + } + if (store.session.hasSession) { + segmentClient.track('Application Opened', { + from_background: false, + version: newAppInfo.version, + build: newAppInfo.build, + }) + } + }) + + let lastState: AppStateStatus = AppState.currentState + AppState.addEventListener('change', (state: AppStateStatus) => { + if (state === 'active' && lastState !== 'active') { + const context = segmentClient.context.get() + segmentClient.track('Application Opened', { + from_background: true, + version: context?.app?.version, + build: context?.app?.build, + }) + } else if (state !== 'active' && lastState === 'active') { + segmentClient.track('Application Backgrounded') + } + lastState = state + }) +} + +export function Provider({children}: React.PropsWithChildren<{}>) { + return ( + {children} + ) +} diff --git a/src/lib/analytics/analytics.web.tsx b/src/lib/analytics/analytics.web.tsx new file mode 100644 index 000000000..467ae278b --- /dev/null +++ b/src/lib/analytics/analytics.web.tsx @@ -0,0 +1,65 @@ +import React from 'react' +import { + createClient, + AnalyticsProvider, + useAnalytics as useAnalyticsOrig, +} from '@segment/analytics-react' +import {RootStoreModel} from 'state/models/root-store' +import {useStores} from 'state/models/root-store' +import {sha256} from 'js-sha256' + +const segmentClient = createClient( + { + writeKey: '8I6DsgfiSLuoONyaunGoiQM7A6y2ybdI', + }, + { + integrations: { + 'Segment.io': { + apiHost: 'api.evt.bsky.app/v1', + }, + }, + }, +) +export const track = segmentClient?.track?.bind?.(segmentClient) + +export function useAnalytics() { + const store = useStores() + const methods = useAnalyticsOrig() + return React.useMemo(() => { + if (store.session.hasSession) { + return methods + } + // dont send analytics pings for anonymous users + return { + screen: () => {}, + track: () => {}, + identify: () => {}, + flush: () => {}, + group: () => {}, + alias: () => {}, + reset: () => {}, + } + }, [store, methods]) +} + +export function init(store: RootStoreModel) { + store.onSessionLoaded(() => { + const sess = store.session.currentSession + if (sess) { + if (sess.email) { + store.log.debug('Ping w/hash') + const email_hashed = sha256(sess.email) + segmentClient.identify(email_hashed, {email_hashed}) + } else { + store.log.debug('Ping w/o hash') + segmentClient.identify() + } + } + }) +} + +export function Provider({children}: React.PropsWithChildren<{}>) { + return ( + {children} + ) +} diff --git a/src/lib/analytics/types.ts b/src/lib/analytics/types.ts new file mode 100644 index 000000000..0638c6b77 --- /dev/null +++ b/src/lib/analytics/types.ts @@ -0,0 +1,98 @@ +export type TrackEvent = ( + event: keyof TrackPropertiesMap, + properties?: TrackPropertiesMap[keyof TrackPropertiesMap], +) => Promise + +export type ScreenEvent = ( + name: keyof ScreenPropertiesMap, + properties?: ScreenPropertiesMap[keyof ScreenPropertiesMap], +) => Promise +interface TrackPropertiesMap { + // LOGIN / SIGN UP events + 'Sign In': {resumedSession: boolean} // CAN BE SERVER + 'Create Account': {} // CAN BE SERVER + 'Signin:PressedForgotPassword': {} + 'Signin:PressedSelectService': {} + // COMPOSER / CREATE POST events + 'Create Post': {imageCount: string} // CAN BE SERVER + 'Composer:PastedPhotos': {} + 'Composer:CameraOpened': {} + 'Composer:GalleryOpened': {} + 'HomeScreen:PressCompose': {} + 'ProfileScreen:PressCompose': {} + // EDIT PROFILE events + 'EditHandle:ViewCustomForm': {} + 'EditHandle:ViewProvidedForm': {} + 'EditHandle:SetNewHandle': {} + 'EditProfile:AvatarSelected': {} + 'EditProfile:BannerSelected': {} + 'EditProfile:Save': {} // CAN BE SERVER + // FEED events + 'Feed:onRefresh': {} + 'Feed:onEndReached': {} + // FEED ITEM events + 'FeedItem:PostReply': {} // CAN BE SERVER + 'FeedItem:PostRepost': {} // CAN BE SERVER + 'FeedItem:PostLike': {} // CAN BE SERVER + 'FeedItem:PostDelete': {} // CAN BE SERVER + 'FeedItem:ThreadMute': {} // CAN BE SERVER + // PROFILE HEADER events + 'ProfileHeader:EditProfileButtonClicked': {} + 'ProfileHeader:FollowersButtonClicked': {} + 'ProfileHeader:FollowsButtonClicked': {} + 'ProfileHeader:ShareButtonClicked': {} + 'ProfileHeader:MuteAccountButtonClicked': {} + 'ProfileHeader:UnmuteAccountButtonClicked': {} + 'ProfileHeader:ReportAccountButtonClicked': {} + 'ProfileHeader:AddToListsButtonClicked': {} + 'ProfileHeader:BlockAccountButtonClicked': {} + 'ProfileHeader:UnblockAccountButtonClicked': {} + 'ProfileHeader:FollowButtonClicked': {} + 'ProfileHeader:UnfollowButtonClicked': {} + 'ViewHeader:MenuButtonClicked': {} + // SETTINGS events + 'Settings:SwitchAccountButtonClicked': {} + 'Settings:AddAccountButtonClicked': {} + 'Settings:ChangeHandleButtonClicked': {} + 'Settings:InvitecodesButtonClicked': {} + 'Settings:ContentfilteringButtonClicked': {} + 'Settings:SignOutButtonClicked': {} + 'Settings:ContentlanguagesButtonClicked': {} + // MENU events + 'Menu:ItemClicked': {url: string} + 'Menu:FeedbackClicked': {} + // MOBILE SHELL events + 'MobileShell:MyProfileButtonPressed': {} + 'MobileShell:HomeButtonPressed': {} + 'MobileShell:SearchButtonPressed': {} + 'MobileShell:NotificationsButtonPressed': {} + 'MobileShell:FeedsButtonPressed': {} + // LISTS events + 'Lists:onRefresh': {} + 'Lists:onEndReached': {} + 'CreateMuteList:AvatarSelected': {} + 'CreateMuteList:Save': {} // CAN BE SERVER + // CUSTOM FEED events + 'MultiFeed:onEndReached': {} + 'MultiFeed:onRefresh': {} + // MODERATION events + 'Moderation:ContentfilteringButtonClicked': {} +} + +interface ScreenPropertiesMap { + Login: {} + CreateAccount: {} + 'Choose Account': {} + 'Signin:ForgotPassword': {} + 'Signin:SetNewPasswordForm': {} + 'Signin:PasswordUpdatedForm': {} + Feed: {} + Notifications: {} + Profile: {} + Settings: {} + AppPasswords: {} + Moderation: {} + BlockedAccounts: {} + MutedAccounts: {} + SavedFeeds: {} +} diff --git a/src/view/com/auth/LoggedOut.tsx b/src/view/com/auth/LoggedOut.tsx index 5d4b9451f..e35706466 100644 --- a/src/view/com/auth/LoggedOut.tsx +++ b/src/view/com/auth/LoggedOut.tsx @@ -7,7 +7,7 @@ import {ErrorBoundary} from 'view/com/util/ErrorBoundary' import {s} from 'lib/styles' import {usePalette} from 'lib/hooks/usePalette' import {useStores} from 'state/index' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {SplashScreen} from './SplashScreen' import {CenteredView} from '../util/Views' diff --git a/src/view/com/auth/create/CreateAccount.tsx b/src/view/com/auth/create/CreateAccount.tsx index ed2c379b4..97200709b 100644 --- a/src/view/com/auth/create/CreateAccount.tsx +++ b/src/view/com/auth/create/CreateAccount.tsx @@ -8,7 +8,7 @@ import { View, } from 'react-native' import {observer} from 'mobx-react-lite' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {Text} from '../../util/text/Text' import {s} from 'lib/styles' import {useStores} from 'state/index' diff --git a/src/view/com/auth/login/Login.tsx b/src/view/com/auth/login/Login.tsx index c5ccd7c1b..af4f01874 100644 --- a/src/view/com/auth/login/Login.tsx +++ b/src/view/com/auth/login/Login.tsx @@ -14,7 +14,7 @@ import { } from '@fortawesome/react-native-fontawesome' import * as EmailValidator from 'email-validator' import {BskyAgent} from '@atproto/api' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {Text} from '../../util/text/Text' import {UserAvatar} from '../../util/UserAvatar' import {s, colors} from 'lib/styles' diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index d7a4a42d5..37569fbec 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -13,7 +13,7 @@ import {useSafeAreaInsets} from 'react-native-safe-area-context' import LinearGradient from 'react-native-linear-gradient' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {RichText} from '@atproto/api' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {UserAutocompleteModel} from 'state/models/discovery/user-autocomplete' import {ExternalEmbed} from './ExternalEmbed' import {Text} from '../util/text/Text' diff --git a/src/view/com/composer/photos/OpenCameraBtn.tsx b/src/view/com/composer/photos/OpenCameraBtn.tsx index bfcfa6b78..0f955984d 100644 --- a/src/view/com/composer/photos/OpenCameraBtn.tsx +++ b/src/view/com/composer/photos/OpenCameraBtn.tsx @@ -5,7 +5,7 @@ import { FontAwesomeIconStyle, } from '@fortawesome/react-native-fontawesome' import {usePalette} from 'lib/hooks/usePalette' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {useStores} from 'state/index' import {isDesktopWeb} from 'platform/detection' import {openCamera} from 'lib/media/picker' diff --git a/src/view/com/composer/photos/SelectPhotoBtn.tsx b/src/view/com/composer/photos/SelectPhotoBtn.tsx index 0b8046a4b..aaf0477c7 100644 --- a/src/view/com/composer/photos/SelectPhotoBtn.tsx +++ b/src/view/com/composer/photos/SelectPhotoBtn.tsx @@ -5,7 +5,7 @@ import { FontAwesomeIconStyle, } from '@fortawesome/react-native-fontawesome' import {usePalette} from 'lib/hooks/usePalette' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {isDesktopWeb} from 'platform/detection' import {usePhotoLibraryPermission} from 'lib/hooks/usePermissions' import {GalleryModel} from 'state/models/media/gallery' diff --git a/src/view/com/lists/ListItems.tsx b/src/view/com/lists/ListItems.tsx index 914f446a1..42965981b 100644 --- a/src/view/com/lists/ListItems.tsx +++ b/src/view/com/lists/ListItems.tsx @@ -20,7 +20,7 @@ import {RichText as RichTextCom} from '../util/text/RichText' import {UserAvatar} from '../util/UserAvatar' import {TextLink} from '../util/Link' import {ListModel} from 'state/models/content/list' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {usePalette} from 'lib/hooks/usePalette' import {useStores} from 'state/index' import {s} from 'lib/styles' diff --git a/src/view/com/lists/ListsList.tsx b/src/view/com/lists/ListsList.tsx index 88b71acc0..09e3a501c 100644 --- a/src/view/com/lists/ListsList.tsx +++ b/src/view/com/lists/ListsList.tsx @@ -21,7 +21,7 @@ import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn' import {Button} from '../util/forms/Button' import {Text} from '../util/text/Text' import {ListsListModel} from 'state/models/lists/lists-list' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {usePalette} from 'lib/hooks/usePalette' import {s} from 'lib/styles' diff --git a/src/view/com/modals/ChangeHandle.tsx b/src/view/com/modals/ChangeHandle.tsx index 57f922f89..961efc08c 100644 --- a/src/view/com/modals/ChangeHandle.tsx +++ b/src/view/com/modals/ChangeHandle.tsx @@ -18,7 +18,7 @@ import {s} from 'lib/styles' import {makeValidHandle, createFullHandle} from 'lib/strings/handles' import {usePalette} from 'lib/hooks/usePalette' import {useTheme} from 'lib/ThemeContext' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {cleanError} from 'lib/strings/errors' export const snapPoints = ['100%'] diff --git a/src/view/com/modals/CreateOrEditMuteList.tsx b/src/view/com/modals/CreateOrEditMuteList.tsx index 7984ea64c..09048b5db 100644 --- a/src/view/com/modals/CreateOrEditMuteList.tsx +++ b/src/view/com/modals/CreateOrEditMuteList.tsx @@ -21,7 +21,7 @@ import {compressIfNeeded} from 'lib/media/manip' import {UserAvatar} from '../util/UserAvatar' import {usePalette} from 'lib/hooks/usePalette' import {useTheme} from 'lib/ThemeContext' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {cleanError, isNetworkError} from 'lib/strings/errors' import {isDesktopWeb} from 'platform/detection' diff --git a/src/view/com/modals/EditProfile.tsx b/src/view/com/modals/EditProfile.tsx index f37a0f71a..3db8d82d8 100644 --- a/src/view/com/modals/EditProfile.tsx +++ b/src/view/com/modals/EditProfile.tsx @@ -23,7 +23,7 @@ import {UserBanner} from '../util/UserBanner' import {UserAvatar} from '../util/UserAvatar' import {usePalette} from 'lib/hooks/usePalette' import {useTheme} from 'lib/ThemeContext' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {cleanError, isNetworkError} from 'lib/strings/errors' export const snapPoints = ['fullscreen'] diff --git a/src/view/com/posts/Feed.tsx b/src/view/com/posts/Feed.tsx index 8206ca509..921f23190 100644 --- a/src/view/com/posts/Feed.tsx +++ b/src/view/com/posts/Feed.tsx @@ -16,7 +16,7 @@ import {FeedSlice} from './FeedSlice' import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn' import {OnScrollCb} from 'lib/hooks/useOnMainScroll' import {s} from 'lib/styles' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {usePalette} from 'lib/hooks/usePalette' import {useTheme} from 'lib/ThemeContext' diff --git a/src/view/com/posts/FeedItem.tsx b/src/view/com/posts/FeedItem.tsx index 7854035f8..18c32b899 100644 --- a/src/view/com/posts/FeedItem.tsx +++ b/src/view/com/posts/FeedItem.tsx @@ -25,7 +25,7 @@ import {UserAvatar} from '../util/UserAvatar' import {s} from 'lib/styles' import {useStores} from 'state/index' import {usePalette} from 'lib/hooks/usePalette' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {sanitizeDisplayName} from 'lib/strings/display-names' export const FeedItem = observer(function ({ diff --git a/src/view/com/posts/MultiFeed.tsx b/src/view/com/posts/MultiFeed.tsx index db353909c..dc28d2d8a 100644 --- a/src/view/com/posts/MultiFeed.tsx +++ b/src/view/com/posts/MultiFeed.tsx @@ -19,7 +19,7 @@ import {Link} from '../util/Link' import {UserAvatar} from '../util/UserAvatar' import {OnScrollCb} from 'lib/hooks/useOnMainScroll' import {s} from 'lib/styles' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {usePalette} from 'lib/hooks/usePalette' import {useTheme} from 'lib/ThemeContext' import {isDesktopWeb} from 'platform/detection' diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx index 46a6bb235..0ad6b2eb7 100644 --- a/src/view/com/profile/ProfileHeader.tsx +++ b/src/view/com/profile/ProfileHeader.tsx @@ -29,7 +29,7 @@ import {UserAvatar} from '../util/UserAvatar' import {UserBanner} from '../util/UserBanner' import {ProfileHeaderWarnings} from '../util/moderation/ProfileHeaderWarnings' import {usePalette} from 'lib/hooks/usePalette' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {NavigationProp} from 'lib/routes/types' import {listUriToHref} from 'lib/strings/url-helpers' import {isDesktopWeb, isNative} from 'platform/detection' @@ -117,6 +117,11 @@ const ProfileHeaderLoaded = observer( }, [store, view]) const onPressToggleFollow = React.useCallback(() => { + track( + view.viewer.following + ? 'ProfileHeader:FollowButtonClicked' + : 'ProfileHeader:UnfollowButtonClicked', + ) view?.toggleFollowing().then( () => { Toast.show( @@ -127,7 +132,7 @@ const ProfileHeaderLoaded = observer( }, err => store.log.error('Failed to toggle follow', err), ) - }, [view, store]) + }, [track, view, store.log]) const onPressEditProfile = React.useCallback(() => { track('ProfileHeader:EditProfileButtonClicked') diff --git a/src/view/com/search/HeaderWithInput.tsx b/src/view/com/search/HeaderWithInput.tsx index d44673717..c51d4f709 100644 --- a/src/view/com/search/HeaderWithInput.tsx +++ b/src/view/com/search/HeaderWithInput.tsx @@ -9,7 +9,7 @@ import {MagnifyingGlassIcon} from 'lib/icons' import {useTheme} from 'lib/ThemeContext' import {usePalette} from 'lib/hooks/usePalette' import {useStores} from 'state/index' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' const MENU_HITSLOP = {left: 10, top: 10, right: 30, bottom: 10} diff --git a/src/view/com/util/ViewHeader.tsx b/src/view/com/util/ViewHeader.tsx index 732a46d06..f5a921ac0 100644 --- a/src/view/com/util/ViewHeader.tsx +++ b/src/view/com/util/ViewHeader.tsx @@ -8,7 +8,7 @@ import {Text} from './text/Text' import {useStores} from 'state/index' import {usePalette} from 'lib/hooks/usePalette' import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {NavigationProp} from 'lib/routes/types' import {isDesktopWeb} from 'platform/detection' diff --git a/src/view/screens/AppPasswords.tsx b/src/view/screens/AppPasswords.tsx index ca60787d5..582bc0f9b 100644 --- a/src/view/screens/AppPasswords.tsx +++ b/src/view/screens/AppPasswords.tsx @@ -12,7 +12,7 @@ import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {observer} from 'mobx-react-lite' import {NativeStackScreenProps} from '@react-navigation/native-stack' import {CommonNavigatorParams} from 'lib/routes/types' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {useFocusEffect} from '@react-navigation/native' import {ViewHeader} from '../com/util/ViewHeader' import {CenteredView} from 'view/com/util/Views' diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx index 4b90c2147..95fb69400 100644 --- a/src/view/screens/Home.tsx +++ b/src/view/screens/Home.tsx @@ -19,7 +19,7 @@ import {FAB} from '../com/util/fab/FAB' import {useStores} from 'state/index' import {s} from 'lib/styles' import {useOnMainScroll} from 'lib/hooks/useOnMainScroll' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {ComposeIcon2} from 'lib/icons' import {isDesktopWeb, isMobileWebMediaQuery, isWeb} from 'platform/detection' diff --git a/src/view/screens/Moderation.tsx b/src/view/screens/Moderation.tsx index 4c52301cb..41df1244e 100644 --- a/src/view/screens/Moderation.tsx +++ b/src/view/screens/Moderation.tsx @@ -15,7 +15,7 @@ import {ViewHeader} from '../com/util/ViewHeader' import {Link} from '../com/util/Link' import {Text} from '../com/util/text/Text' import {usePalette} from 'lib/hooks/usePalette' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {isDesktopWeb} from 'platform/detection' type Props = NativeStackScreenProps diff --git a/src/view/screens/ModerationBlockedAccounts.tsx b/src/view/screens/ModerationBlockedAccounts.tsx index cd506d630..794195e58 100644 --- a/src/view/screens/ModerationBlockedAccounts.tsx +++ b/src/view/screens/ModerationBlockedAccounts.tsx @@ -16,7 +16,7 @@ import {observer} from 'mobx-react-lite' import {NativeStackScreenProps} from '@react-navigation/native-stack' import {CommonNavigatorParams} from 'lib/routes/types' import {BlockedAccountsModel} from 'state/models/lists/blocked-accounts' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {useFocusEffect} from '@react-navigation/native' import {ViewHeader} from '../com/util/ViewHeader' import {CenteredView} from 'view/com/util/Views' diff --git a/src/view/screens/ModerationMutedAccounts.tsx b/src/view/screens/ModerationMutedAccounts.tsx index 22b8c0d33..995223c15 100644 --- a/src/view/screens/ModerationMutedAccounts.tsx +++ b/src/view/screens/ModerationMutedAccounts.tsx @@ -16,7 +16,7 @@ import {observer} from 'mobx-react-lite' import {NativeStackScreenProps} from '@react-navigation/native-stack' import {CommonNavigatorParams} from 'lib/routes/types' import {MutedAccountsModel} from 'state/models/lists/muted-accounts' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {useFocusEffect} from '@react-navigation/native' import {ViewHeader} from '../com/util/ViewHeader' import {CenteredView} from 'view/com/util/Views' diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx index 4db1d14ae..15bbf4fd0 100644 --- a/src/view/screens/Notifications.tsx +++ b/src/view/screens/Notifications.tsx @@ -15,7 +15,7 @@ import {useStores} from 'state/index' import {useOnMainScroll} from 'lib/hooks/useOnMainScroll' import {useTabFocusEffect} from 'lib/hooks/useTabFocusEffect' import {s} from 'lib/styles' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {isWeb} from 'platform/detection' type Props = NativeStackScreenProps< diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx index c5ad286c7..f50a2c45d 100644 --- a/src/view/screens/Profile.tsx +++ b/src/view/screens/Profile.tsx @@ -23,7 +23,7 @@ import {EmptyState} from '../com/util/EmptyState' import {Text} from '../com/util/text/Text' import {FAB} from '../com/util/fab/FAB' import {s, colors} from 'lib/styles' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {ComposeIcon2} from 'lib/icons' import {CustomFeed} from 'view/com/feeds/CustomFeed' import {CustomFeedModel} from 'state/models/feeds/custom-feed' diff --git a/src/view/screens/SavedFeeds.tsx b/src/view/screens/SavedFeeds.tsx index 103b18c70..ac38a760b 100644 --- a/src/view/screens/SavedFeeds.tsx +++ b/src/view/screens/SavedFeeds.tsx @@ -9,7 +9,7 @@ import { } from 'react-native' import {useFocusEffect} from '@react-navigation/native' import {NativeStackScreenProps} from '@react-navigation/native-stack' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {usePalette} from 'lib/hooks/usePalette' import {CommonNavigatorParams} from 'lib/routes/types' import {observer} from 'mobx-react-lite' diff --git a/src/view/screens/Settings.tsx b/src/view/screens/Settings.tsx index ac072c479..3d057451a 100644 --- a/src/view/screens/Settings.tsx +++ b/src/view/screens/Settings.tsx @@ -35,7 +35,7 @@ import {ToggleButton} from 'view/com/util/forms/ToggleButton' import {usePalette} from 'lib/hooks/usePalette' import {useCustomPalette} from 'lib/hooks/useCustomPalette' import {AccountData} from 'state/models/session' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {NavigationProp} from 'lib/routes/types' import {isDesktopWeb} from 'platform/detection' import {pluralize} from 'lib/strings/helpers' diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx index cf8639338..c02478f98 100644 --- a/src/view/shell/Drawer.tsx +++ b/src/view/shell/Drawer.tsx @@ -36,7 +36,7 @@ import {UserAvatar} from 'view/com/util/UserAvatar' import {Text} from 'view/com/util/text/Text' import {useTheme} from 'lib/ThemeContext' import {usePalette} from 'lib/hooks/usePalette' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {pluralize} from 'lib/strings/helpers' import {getTabState, TabState} from 'lib/routes/helpers' import {NavigationProp} from 'lib/routes/types' diff --git a/src/view/shell/bottom-bar/BottomBar.tsx b/src/view/shell/bottom-bar/BottomBar.tsx index e8cba9047..09b6f9f65 100644 --- a/src/view/shell/bottom-bar/BottomBar.tsx +++ b/src/view/shell/bottom-bar/BottomBar.tsx @@ -11,7 +11,7 @@ import {useSafeAreaInsets} from 'react-native-safe-area-context' import {observer} from 'mobx-react-lite' import {Text} from 'view/com/util/text/Text' import {useStores} from 'state/index' -import {useAnalytics} from 'lib/analytics' +import {useAnalytics} from 'lib/analytics/analytics' import {clamp} from 'lib/numbers' import { HomeIcon, @@ -30,6 +30,8 @@ import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode' import {useNavigationTabState} from 'lib/hooks/useNavigationTabState' import {UserAvatar} from 'view/com/util/UserAvatar' +type TabOptions = 'Home' | 'Search' | 'Notifications' | 'MyProfile' | 'Feeds' + export const BottomBar = observer(({navigation}: BottomTabBarProps) => { const store = useStores() const pal = usePalette('default') @@ -42,7 +44,7 @@ export const BottomBar = observer(({navigation}: BottomTabBarProps) => { const {notifications} = store.me const onPressTab = React.useCallback( - (tab: string) => { + (tab: TabOptions) => { track(`MobileShell:${tab}ButtonPressed`) const state = navigation.getState() const tabState = getTabState(state, tab) -- cgit 1.4.1