mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

[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

authored by

Ansh and committed by
GitHub
17e7590b 1695ae34

+156 -41
+1 -1
src/App.native.tsx
··· 13 13 import {RootStoreModel, setupState, RootStoreProvider} from './state' 14 14 import {Shell} from './view/shell' 15 15 import * as notifee from 'lib/notifee' 16 - import * as analytics from 'lib/analytics' 16 + import * as analytics from 'lib/analytics/analytics' 17 17 import * as Toast from './view/com/util/Toast' 18 18 import {handleLink} from './Navigation' 19 19
+1 -1
src/App.web.tsx
··· 3 3 import {SafeAreaProvider} from 'react-native-safe-area-context' 4 4 import {RootSiblingParent} from 'react-native-root-siblings' 5 5 import * as view from './view/index' 6 - import * as analytics from 'lib/analytics' 6 + import * as analytics from 'lib/analytics/analytics' 7 7 import {RootStoreModel, setupState, RootStoreProvider} from './state' 8 8 import {Shell} from './view/shell/index' 9 9 import {ToastContainer} from './view/com/util/Toast.web'
+19 -9
src/lib/analytics.tsx src/lib/analytics/analytics.tsx
··· 4 4 createClient, 5 5 AnalyticsProvider, 6 6 useAnalytics as useAnalyticsOrig, 7 + ClientMethods, 7 8 } from '@segment/analytics-react-native' 8 9 import {RootStoreModel, AppInfo} from 'state/models/root-store' 9 10 import {useStores} from 'state/models/root-store' 10 11 import {sha256} from 'js-sha256' 12 + import {ScreenEvent, TrackEvent} from './types' 11 13 12 14 const segmentClient = createClient({ 13 15 writeKey: '8I6DsgfiSLuoONyaunGoiQM7A6y2ybdI', ··· 16 18 17 19 export function useAnalytics() { 18 20 const store = useStores() 19 - const methods = useAnalyticsOrig() 21 + const methods: ClientMethods = useAnalyticsOrig() 20 22 return React.useMemo(() => { 21 23 if (store.session.hasSession) { 22 - return methods 24 + return { 25 + screen: methods.screen as ScreenEvent, // ScreenEvents defines all the possible screen names 26 + track: methods.track as TrackEvent, // TrackEvents defines all the possible track events and their properties 27 + identify: methods.identify, 28 + flush: methods.flush, 29 + group: methods.group, 30 + alias: methods.alias, 31 + reset: methods.reset, 32 + } 23 33 } 24 34 // dont send analytics pings for anonymous users 25 35 return { 26 - screen: () => {}, 27 - track: () => {}, 28 - identify: () => {}, 29 - flush: () => {}, 30 - group: () => {}, 31 - alias: () => {}, 32 - reset: () => {}, 36 + screen: () => Promise<void>, 37 + track: () => Promise<void>, 38 + identify: () => Promise<void>, 39 + flush: () => Promise<void>, 40 + group: () => Promise<void>, 41 + alias: () => Promise<void>, 42 + reset: () => Promise<void>, 33 43 } 34 44 }, [store, methods]) 35 45 }
src/lib/analytics.web.tsx src/lib/analytics/analytics.web.tsx
+98
src/lib/analytics/types.ts
··· 1 + export type TrackEvent = ( 2 + event: keyof TrackPropertiesMap, 3 + properties?: TrackPropertiesMap[keyof TrackPropertiesMap], 4 + ) => Promise<void> 5 + 6 + export type ScreenEvent = ( 7 + name: keyof ScreenPropertiesMap, 8 + properties?: ScreenPropertiesMap[keyof ScreenPropertiesMap], 9 + ) => Promise<void> 10 + interface TrackPropertiesMap { 11 + // LOGIN / SIGN UP events 12 + 'Sign In': {resumedSession: boolean} // CAN BE SERVER 13 + 'Create Account': {} // CAN BE SERVER 14 + 'Signin:PressedForgotPassword': {} 15 + 'Signin:PressedSelectService': {} 16 + // COMPOSER / CREATE POST events 17 + 'Create Post': {imageCount: string} // CAN BE SERVER 18 + 'Composer:PastedPhotos': {} 19 + 'Composer:CameraOpened': {} 20 + 'Composer:GalleryOpened': {} 21 + 'HomeScreen:PressCompose': {} 22 + 'ProfileScreen:PressCompose': {} 23 + // EDIT PROFILE events 24 + 'EditHandle:ViewCustomForm': {} 25 + 'EditHandle:ViewProvidedForm': {} 26 + 'EditHandle:SetNewHandle': {} 27 + 'EditProfile:AvatarSelected': {} 28 + 'EditProfile:BannerSelected': {} 29 + 'EditProfile:Save': {} // CAN BE SERVER 30 + // FEED events 31 + 'Feed:onRefresh': {} 32 + 'Feed:onEndReached': {} 33 + // FEED ITEM events 34 + 'FeedItem:PostReply': {} // CAN BE SERVER 35 + 'FeedItem:PostRepost': {} // CAN BE SERVER 36 + 'FeedItem:PostLike': {} // CAN BE SERVER 37 + 'FeedItem:PostDelete': {} // CAN BE SERVER 38 + 'FeedItem:ThreadMute': {} // CAN BE SERVER 39 + // PROFILE HEADER events 40 + 'ProfileHeader:EditProfileButtonClicked': {} 41 + 'ProfileHeader:FollowersButtonClicked': {} 42 + 'ProfileHeader:FollowsButtonClicked': {} 43 + 'ProfileHeader:ShareButtonClicked': {} 44 + 'ProfileHeader:MuteAccountButtonClicked': {} 45 + 'ProfileHeader:UnmuteAccountButtonClicked': {} 46 + 'ProfileHeader:ReportAccountButtonClicked': {} 47 + 'ProfileHeader:AddToListsButtonClicked': {} 48 + 'ProfileHeader:BlockAccountButtonClicked': {} 49 + 'ProfileHeader:UnblockAccountButtonClicked': {} 50 + 'ProfileHeader:FollowButtonClicked': {} 51 + 'ProfileHeader:UnfollowButtonClicked': {} 52 + 'ViewHeader:MenuButtonClicked': {} 53 + // SETTINGS events 54 + 'Settings:SwitchAccountButtonClicked': {} 55 + 'Settings:AddAccountButtonClicked': {} 56 + 'Settings:ChangeHandleButtonClicked': {} 57 + 'Settings:InvitecodesButtonClicked': {} 58 + 'Settings:ContentfilteringButtonClicked': {} 59 + 'Settings:SignOutButtonClicked': {} 60 + 'Settings:ContentlanguagesButtonClicked': {} 61 + // MENU events 62 + 'Menu:ItemClicked': {url: string} 63 + 'Menu:FeedbackClicked': {} 64 + // MOBILE SHELL events 65 + 'MobileShell:MyProfileButtonPressed': {} 66 + 'MobileShell:HomeButtonPressed': {} 67 + 'MobileShell:SearchButtonPressed': {} 68 + 'MobileShell:NotificationsButtonPressed': {} 69 + 'MobileShell:FeedsButtonPressed': {} 70 + // LISTS events 71 + 'Lists:onRefresh': {} 72 + 'Lists:onEndReached': {} 73 + 'CreateMuteList:AvatarSelected': {} 74 + 'CreateMuteList:Save': {} // CAN BE SERVER 75 + // CUSTOM FEED events 76 + 'MultiFeed:onEndReached': {} 77 + 'MultiFeed:onRefresh': {} 78 + // MODERATION events 79 + 'Moderation:ContentfilteringButtonClicked': {} 80 + } 81 + 82 + interface ScreenPropertiesMap { 83 + Login: {} 84 + CreateAccount: {} 85 + 'Choose Account': {} 86 + 'Signin:ForgotPassword': {} 87 + 'Signin:SetNewPasswordForm': {} 88 + 'Signin:PasswordUpdatedForm': {} 89 + Feed: {} 90 + Notifications: {} 91 + Profile: {} 92 + Settings: {} 93 + AppPasswords: {} 94 + Moderation: {} 95 + BlockedAccounts: {} 96 + MutedAccounts: {} 97 + SavedFeeds: {} 98 + }
+1 -1
src/view/com/auth/LoggedOut.tsx
··· 7 7 import {s} from 'lib/styles' 8 8 import {usePalette} from 'lib/hooks/usePalette' 9 9 import {useStores} from 'state/index' 10 - import {useAnalytics} from 'lib/analytics' 10 + import {useAnalytics} from 'lib/analytics/analytics' 11 11 import {SplashScreen} from './SplashScreen' 12 12 import {CenteredView} from '../util/Views' 13 13
+1 -1
src/view/com/auth/create/CreateAccount.tsx
··· 8 8 View, 9 9 } from 'react-native' 10 10 import {observer} from 'mobx-react-lite' 11 - import {useAnalytics} from 'lib/analytics' 11 + import {useAnalytics} from 'lib/analytics/analytics' 12 12 import {Text} from '../../util/text/Text' 13 13 import {s} from 'lib/styles' 14 14 import {useStores} from 'state/index'
+1 -1
src/view/com/auth/login/Login.tsx
··· 14 14 } from '@fortawesome/react-native-fontawesome' 15 15 import * as EmailValidator from 'email-validator' 16 16 import {BskyAgent} from '@atproto/api' 17 - import {useAnalytics} from 'lib/analytics' 17 + import {useAnalytics} from 'lib/analytics/analytics' 18 18 import {Text} from '../../util/text/Text' 19 19 import {UserAvatar} from '../../util/UserAvatar' 20 20 import {s, colors} from 'lib/styles'
+1 -1
src/view/com/composer/Composer.tsx
··· 13 13 import LinearGradient from 'react-native-linear-gradient' 14 14 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 15 15 import {RichText} from '@atproto/api' 16 - import {useAnalytics} from 'lib/analytics' 16 + import {useAnalytics} from 'lib/analytics/analytics' 17 17 import {UserAutocompleteModel} from 'state/models/discovery/user-autocomplete' 18 18 import {ExternalEmbed} from './ExternalEmbed' 19 19 import {Text} from '../util/text/Text'
+1 -1
src/view/com/composer/photos/OpenCameraBtn.tsx
··· 5 5 FontAwesomeIconStyle, 6 6 } from '@fortawesome/react-native-fontawesome' 7 7 import {usePalette} from 'lib/hooks/usePalette' 8 - import {useAnalytics} from 'lib/analytics' 8 + import {useAnalytics} from 'lib/analytics/analytics' 9 9 import {useStores} from 'state/index' 10 10 import {isDesktopWeb} from 'platform/detection' 11 11 import {openCamera} from 'lib/media/picker'
+1 -1
src/view/com/composer/photos/SelectPhotoBtn.tsx
··· 5 5 FontAwesomeIconStyle, 6 6 } from '@fortawesome/react-native-fontawesome' 7 7 import {usePalette} from 'lib/hooks/usePalette' 8 - import {useAnalytics} from 'lib/analytics' 8 + import {useAnalytics} from 'lib/analytics/analytics' 9 9 import {isDesktopWeb} from 'platform/detection' 10 10 import {usePhotoLibraryPermission} from 'lib/hooks/usePermissions' 11 11 import {GalleryModel} from 'state/models/media/gallery'
+1 -1
src/view/com/lists/ListItems.tsx
··· 20 20 import {UserAvatar} from '../util/UserAvatar' 21 21 import {TextLink} from '../util/Link' 22 22 import {ListModel} from 'state/models/content/list' 23 - import {useAnalytics} from 'lib/analytics' 23 + import {useAnalytics} from 'lib/analytics/analytics' 24 24 import {usePalette} from 'lib/hooks/usePalette' 25 25 import {useStores} from 'state/index' 26 26 import {s} from 'lib/styles'
+1 -1
src/view/com/lists/ListsList.tsx
··· 21 21 import {Button} from '../util/forms/Button' 22 22 import {Text} from '../util/text/Text' 23 23 import {ListsListModel} from 'state/models/lists/lists-list' 24 - import {useAnalytics} from 'lib/analytics' 24 + import {useAnalytics} from 'lib/analytics/analytics' 25 25 import {usePalette} from 'lib/hooks/usePalette' 26 26 import {s} from 'lib/styles' 27 27
+1 -1
src/view/com/modals/ChangeHandle.tsx
··· 18 18 import {makeValidHandle, createFullHandle} from 'lib/strings/handles' 19 19 import {usePalette} from 'lib/hooks/usePalette' 20 20 import {useTheme} from 'lib/ThemeContext' 21 - import {useAnalytics} from 'lib/analytics' 21 + import {useAnalytics} from 'lib/analytics/analytics' 22 22 import {cleanError} from 'lib/strings/errors' 23 23 24 24 export const snapPoints = ['100%']
+1 -1
src/view/com/modals/CreateOrEditMuteList.tsx
··· 21 21 import {UserAvatar} from '../util/UserAvatar' 22 22 import {usePalette} from 'lib/hooks/usePalette' 23 23 import {useTheme} from 'lib/ThemeContext' 24 - import {useAnalytics} from 'lib/analytics' 24 + import {useAnalytics} from 'lib/analytics/analytics' 25 25 import {cleanError, isNetworkError} from 'lib/strings/errors' 26 26 import {isDesktopWeb} from 'platform/detection' 27 27
+1 -1
src/view/com/modals/EditProfile.tsx
··· 23 23 import {UserAvatar} from '../util/UserAvatar' 24 24 import {usePalette} from 'lib/hooks/usePalette' 25 25 import {useTheme} from 'lib/ThemeContext' 26 - import {useAnalytics} from 'lib/analytics' 26 + import {useAnalytics} from 'lib/analytics/analytics' 27 27 import {cleanError, isNetworkError} from 'lib/strings/errors' 28 28 29 29 export const snapPoints = ['fullscreen']
+1 -1
src/view/com/posts/Feed.tsx
··· 16 16 import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn' 17 17 import {OnScrollCb} from 'lib/hooks/useOnMainScroll' 18 18 import {s} from 'lib/styles' 19 - import {useAnalytics} from 'lib/analytics' 19 + import {useAnalytics} from 'lib/analytics/analytics' 20 20 import {usePalette} from 'lib/hooks/usePalette' 21 21 import {useTheme} from 'lib/ThemeContext' 22 22
+1 -1
src/view/com/posts/FeedItem.tsx
··· 25 25 import {s} from 'lib/styles' 26 26 import {useStores} from 'state/index' 27 27 import {usePalette} from 'lib/hooks/usePalette' 28 - import {useAnalytics} from 'lib/analytics' 28 + import {useAnalytics} from 'lib/analytics/analytics' 29 29 import {sanitizeDisplayName} from 'lib/strings/display-names' 30 30 31 31 export const FeedItem = observer(function ({
+1 -1
src/view/com/posts/MultiFeed.tsx
··· 19 19 import {UserAvatar} from '../util/UserAvatar' 20 20 import {OnScrollCb} from 'lib/hooks/useOnMainScroll' 21 21 import {s} from 'lib/styles' 22 - import {useAnalytics} from 'lib/analytics' 22 + import {useAnalytics} from 'lib/analytics/analytics' 23 23 import {usePalette} from 'lib/hooks/usePalette' 24 24 import {useTheme} from 'lib/ThemeContext' 25 25 import {isDesktopWeb} from 'platform/detection'
+7 -2
src/view/com/profile/ProfileHeader.tsx
··· 29 29 import {UserBanner} from '../util/UserBanner' 30 30 import {ProfileHeaderWarnings} from '../util/moderation/ProfileHeaderWarnings' 31 31 import {usePalette} from 'lib/hooks/usePalette' 32 - import {useAnalytics} from 'lib/analytics' 32 + import {useAnalytics} from 'lib/analytics/analytics' 33 33 import {NavigationProp} from 'lib/routes/types' 34 34 import {listUriToHref} from 'lib/strings/url-helpers' 35 35 import {isDesktopWeb, isNative} from 'platform/detection' ··· 117 117 }, [store, view]) 118 118 119 119 const onPressToggleFollow = React.useCallback(() => { 120 + track( 121 + view.viewer.following 122 + ? 'ProfileHeader:FollowButtonClicked' 123 + : 'ProfileHeader:UnfollowButtonClicked', 124 + ) 120 125 view?.toggleFollowing().then( 121 126 () => { 122 127 Toast.show( ··· 127 132 }, 128 133 err => store.log.error('Failed to toggle follow', err), 129 134 ) 130 - }, [view, store]) 135 + }, [track, view, store.log]) 131 136 132 137 const onPressEditProfile = React.useCallback(() => { 133 138 track('ProfileHeader:EditProfileButtonClicked')
+1 -1
src/view/com/search/HeaderWithInput.tsx
··· 9 9 import {useTheme} from 'lib/ThemeContext' 10 10 import {usePalette} from 'lib/hooks/usePalette' 11 11 import {useStores} from 'state/index' 12 - import {useAnalytics} from 'lib/analytics' 12 + import {useAnalytics} from 'lib/analytics/analytics' 13 13 14 14 const MENU_HITSLOP = {left: 10, top: 10, right: 30, bottom: 10} 15 15
+1 -1
src/view/com/util/ViewHeader.tsx
··· 8 8 import {useStores} from 'state/index' 9 9 import {usePalette} from 'lib/hooks/usePalette' 10 10 import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' 11 - import {useAnalytics} from 'lib/analytics' 11 + import {useAnalytics} from 'lib/analytics/analytics' 12 12 import {NavigationProp} from 'lib/routes/types' 13 13 import {isDesktopWeb} from 'platform/detection' 14 14
+1 -1
src/view/screens/AppPasswords.tsx
··· 12 12 import {observer} from 'mobx-react-lite' 13 13 import {NativeStackScreenProps} from '@react-navigation/native-stack' 14 14 import {CommonNavigatorParams} from 'lib/routes/types' 15 - import {useAnalytics} from 'lib/analytics' 15 + import {useAnalytics} from 'lib/analytics/analytics' 16 16 import {useFocusEffect} from '@react-navigation/native' 17 17 import {ViewHeader} from '../com/util/ViewHeader' 18 18 import {CenteredView} from 'view/com/util/Views'
+1 -1
src/view/screens/Home.tsx
··· 19 19 import {useStores} from 'state/index' 20 20 import {s} from 'lib/styles' 21 21 import {useOnMainScroll} from 'lib/hooks/useOnMainScroll' 22 - import {useAnalytics} from 'lib/analytics' 22 + import {useAnalytics} from 'lib/analytics/analytics' 23 23 import {ComposeIcon2} from 'lib/icons' 24 24 import {isDesktopWeb, isMobileWebMediaQuery, isWeb} from 'platform/detection' 25 25
+1 -1
src/view/screens/Moderation.tsx
··· 15 15 import {Link} from '../com/util/Link' 16 16 import {Text} from '../com/util/text/Text' 17 17 import {usePalette} from 'lib/hooks/usePalette' 18 - import {useAnalytics} from 'lib/analytics' 18 + import {useAnalytics} from 'lib/analytics/analytics' 19 19 import {isDesktopWeb} from 'platform/detection' 20 20 21 21 type Props = NativeStackScreenProps<CommonNavigatorParams, 'Moderation'>
+1 -1
src/view/screens/ModerationBlockedAccounts.tsx
··· 16 16 import {NativeStackScreenProps} from '@react-navigation/native-stack' 17 17 import {CommonNavigatorParams} from 'lib/routes/types' 18 18 import {BlockedAccountsModel} from 'state/models/lists/blocked-accounts' 19 - import {useAnalytics} from 'lib/analytics' 19 + import {useAnalytics} from 'lib/analytics/analytics' 20 20 import {useFocusEffect} from '@react-navigation/native' 21 21 import {ViewHeader} from '../com/util/ViewHeader' 22 22 import {CenteredView} from 'view/com/util/Views'
+1 -1
src/view/screens/ModerationMutedAccounts.tsx
··· 16 16 import {NativeStackScreenProps} from '@react-navigation/native-stack' 17 17 import {CommonNavigatorParams} from 'lib/routes/types' 18 18 import {MutedAccountsModel} from 'state/models/lists/muted-accounts' 19 - import {useAnalytics} from 'lib/analytics' 19 + import {useAnalytics} from 'lib/analytics/analytics' 20 20 import {useFocusEffect} from '@react-navigation/native' 21 21 import {ViewHeader} from '../com/util/ViewHeader' 22 22 import {CenteredView} from 'view/com/util/Views'
+1 -1
src/view/screens/Notifications.tsx
··· 15 15 import {useOnMainScroll} from 'lib/hooks/useOnMainScroll' 16 16 import {useTabFocusEffect} from 'lib/hooks/useTabFocusEffect' 17 17 import {s} from 'lib/styles' 18 - import {useAnalytics} from 'lib/analytics' 18 + import {useAnalytics} from 'lib/analytics/analytics' 19 19 import {isWeb} from 'platform/detection' 20 20 21 21 type Props = NativeStackScreenProps<
+1 -1
src/view/screens/Profile.tsx
··· 23 23 import {Text} from '../com/util/text/Text' 24 24 import {FAB} from '../com/util/fab/FAB' 25 25 import {s, colors} from 'lib/styles' 26 - import {useAnalytics} from 'lib/analytics' 26 + import {useAnalytics} from 'lib/analytics/analytics' 27 27 import {ComposeIcon2} from 'lib/icons' 28 28 import {CustomFeed} from 'view/com/feeds/CustomFeed' 29 29 import {CustomFeedModel} from 'state/models/feeds/custom-feed'
+1 -1
src/view/screens/SavedFeeds.tsx
··· 9 9 } from 'react-native' 10 10 import {useFocusEffect} from '@react-navigation/native' 11 11 import {NativeStackScreenProps} from '@react-navigation/native-stack' 12 - import {useAnalytics} from 'lib/analytics' 12 + import {useAnalytics} from 'lib/analytics/analytics' 13 13 import {usePalette} from 'lib/hooks/usePalette' 14 14 import {CommonNavigatorParams} from 'lib/routes/types' 15 15 import {observer} from 'mobx-react-lite'
+1 -1
src/view/screens/Settings.tsx
··· 35 35 import {usePalette} from 'lib/hooks/usePalette' 36 36 import {useCustomPalette} from 'lib/hooks/useCustomPalette' 37 37 import {AccountData} from 'state/models/session' 38 - import {useAnalytics} from 'lib/analytics' 38 + import {useAnalytics} from 'lib/analytics/analytics' 39 39 import {NavigationProp} from 'lib/routes/types' 40 40 import {isDesktopWeb} from 'platform/detection' 41 41 import {pluralize} from 'lib/strings/helpers'
+1 -1
src/view/shell/Drawer.tsx
··· 36 36 import {Text} from 'view/com/util/text/Text' 37 37 import {useTheme} from 'lib/ThemeContext' 38 38 import {usePalette} from 'lib/hooks/usePalette' 39 - import {useAnalytics} from 'lib/analytics' 39 + import {useAnalytics} from 'lib/analytics/analytics' 40 40 import {pluralize} from 'lib/strings/helpers' 41 41 import {getTabState, TabState} from 'lib/routes/helpers' 42 42 import {NavigationProp} from 'lib/routes/types'
+4 -2
src/view/shell/bottom-bar/BottomBar.tsx
··· 11 11 import {observer} from 'mobx-react-lite' 12 12 import {Text} from 'view/com/util/text/Text' 13 13 import {useStores} from 'state/index' 14 - import {useAnalytics} from 'lib/analytics' 14 + import {useAnalytics} from 'lib/analytics/analytics' 15 15 import {clamp} from 'lib/numbers' 16 16 import { 17 17 HomeIcon, ··· 30 30 import {useNavigationTabState} from 'lib/hooks/useNavigationTabState' 31 31 import {UserAvatar} from 'view/com/util/UserAvatar' 32 32 33 + type TabOptions = 'Home' | 'Search' | 'Notifications' | 'MyProfile' | 'Feeds' 34 + 33 35 export const BottomBar = observer(({navigation}: BottomTabBarProps) => { 34 36 const store = useStores() 35 37 const pal = usePalette('default') ··· 42 44 const {notifications} = store.me 43 45 44 46 const onPressTab = React.useCallback( 45 - (tab: string) => { 47 + (tab: TabOptions) => { 46 48 track(`MobileShell:${tab}ButtonPressed`) 47 49 const state = navigation.getState() 48 50 const tabState = getTabState(state, tab)