Skip to content

Commit

Permalink
[Wallet] Analytics instrumentation and standardization across Valora (#…
Browse files Browse the repository at this point in the history
…4311)

### Description
Added events throughout the app and standardized naming and placement. Also added copyable sessionId to the drawer menu and including it in support emails to aid in debugging.

### Other changes
Using specific analytics naming to make all related events easily searchable. For example, search "SendEvents" to view all analytics events related to the Send flow.

### Tested
Yes

### Related issues
- Part of #4008

### Backwards compatibility
Yes
  • Loading branch information
tarikbellamine authored Jul 6, 2020
1 parent bc624f9 commit 2588f4d
Show file tree
Hide file tree
Showing 86 changed files with 1,121 additions and 835 deletions.
1 change: 1 addition & 0 deletions packages/mobile/__mocks__/src/analytics/ValoraAnalytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class ValoraAnalytics {
}
) => console.log(eventName, eventProperties)
)
getSessionId = jest.fn(() => 'this is a session id')
setUserAddress = jest.fn((address: string | null | undefined) => console.log(address))
track = jest.fn(
<EventName extends keyof AnalyticsPropertiesList>(
Expand Down
40 changes: 13 additions & 27 deletions packages/mobile/src/account/AccountKeyEducation.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { BtnTypes } from '@celo/react-components/components/Button.v2'
import { StackScreenProps } from '@react-navigation/stack'
import React from 'react'
import React, { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import Education from 'src/account/Education'
import { AnalyticsEvents } from 'src/analytics/Events'
import Education, { EducationTopic } from 'src/account/Education'
import { OnboardingEvents } from 'src/analytics/Events'
import ValoraAnalytics from 'src/analytics/ValoraAnalytics'
import { Namespaces } from 'src/i18n'
import { accountKey1, accountKey2, accountKey3, accountKey4 } from 'src/images/Images'
import { navigate } from 'src/navigator/NavigationService'
Expand All @@ -14,6 +15,7 @@ type Props = StackScreenProps<StackParamList, Screens.AccountKeyEducation>

export default function AccountKeyEducation(props: Props) {
function onComplete() {
ValoraAnalytics.track(OnboardingEvents.backup_education_complete)
if (props.route.params?.nextScreen) {
navigate(props.route.params?.nextScreen)
} else {
Expand All @@ -25,6 +27,10 @@ export default function AccountKeyEducation(props: Props) {

const steps = useSteps()

useEffect(() => {
ValoraAnalytics.track(OnboardingEvents.backup_education_start)
}, [])

return (
<Education
isClosable={true}
Expand All @@ -42,30 +48,10 @@ function useSteps() {
return React.useMemo(
() =>
[
{
image: accountKey1,
cancelEvent: AnalyticsEvents.backup_educate_1_cancel,
progressEvent: AnalyticsEvents.backup_educate_1_next,
screenName: 'AccountKeyEducation',
},
{
image: accountKey2,
cancelEvent: AnalyticsEvents.backup_educate_2_cancel,
progressEvent: AnalyticsEvents.backup_educate_2_next,
screenName: 'AccountKeyEducation',
},
{
image: accountKey3,
cancelEvent: AnalyticsEvents.backup_educate_3_cancel,
progressEvent: AnalyticsEvents.backup_educate_3_next,
screenName: 'AccountKeyEducation',
},
{
image: accountKey4,
cancelEvent: AnalyticsEvents.backup_educate_4_cancel,
progressEvent: AnalyticsEvents.backup_educate_4_next,
screenName: 'AccountKeyEducation',
},
{ image: accountKey1, topic: EducationTopic.backup },
{ image: accountKey2, topic: EducationTopic.backup },
{ image: accountKey3, topic: EducationTopic.backup },
{ image: accountKey4, topic: EducationTopic.backup },
].map((step, index) => {
return {
...step,
Expand Down
4 changes: 2 additions & 2 deletions packages/mobile/src/account/EditProfile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { WithTranslation } from 'react-i18next'
import { ScrollView, StyleSheet } from 'react-native'
import { connect } from 'react-redux'
import { setName } from 'src/account/actions'
import { AnalyticsEvents } from 'src/analytics/Events'
import { SettingsEvents } from 'src/analytics/Events'
import ValoraAnalytics from 'src/analytics/ValoraAnalytics'
import { Namespaces, withTranslation } from 'src/i18n'
import { RootState } from 'src/redux/reducers'
Expand Down Expand Up @@ -43,7 +43,7 @@ export class EditProfile extends React.Component<Props, State> {

nameSubmitted = () => {
this.props.setName(this.state.name)
ValoraAnalytics.track(AnalyticsEvents.edit_name)
ValoraAnalytics.track(SettingsEvents.settings_profile_name_edit)
}

render() {
Expand Down
8 changes: 2 additions & 6 deletions packages/mobile/src/account/Education.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ import 'react-native'
import { fireEvent, render } from 'react-native-testing-library'
import { Provider } from 'react-redux'
import * as renderer from 'react-test-renderer'
import Education, { Props } from 'src/account/Education'
import { AnalyticsEvents } from 'src/analytics/Events'
import Education, { EducationTopic, Props } from 'src/account/Education'
import { navigateBack } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import { createMockStore } from 'test/utils'

const BUTTON_TEXT = 'Done'
Expand All @@ -15,11 +13,9 @@ const educationProps: Props = {
stepInfo: [
{
image: null,
topic: EducationTopic.celo,
title: 'Step 1',
text: 'The Journey Begins',
cancelEvent: AnalyticsEvents.gold_cancel1,
progressEvent: AnalyticsEvents.gold_educate_1_next,
screenName: Screens.Debug,
},
],
buttonText: 'next',
Expand Down
55 changes: 41 additions & 14 deletions packages/mobile/src/account/Education.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import * as React from 'react'
import { Image, ImageSourcePropType, StyleSheet, Text, View } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'
import Swiper from 'react-native-swiper'
import { AnalyticsEventType } from 'src/analytics/Events'
import { OnboardingEvents } from 'src/analytics/Events'
import { ScrollDirection } from 'src/analytics/types'
import ValoraAnalytics from 'src/analytics/ValoraAnalytics'
import DrawerTopBar from 'src/navigator/DrawerTopBar'
import { navigateBack } from 'src/navigator/NavigationService'
Expand All @@ -18,13 +19,16 @@ interface State {
step: number
}

export enum EducationTopic {
backup = 'backup',
celo = 'celo',
}

interface EducationStep {
image: ImageSourcePropType | null
topic: EducationTopic
title: string
text: string
cancelEvent: AnalyticsEventType
progressEvent: AnalyticsEventType
screenName: string
}

export interface Props {
Expand All @@ -44,15 +48,27 @@ export default class Education extends React.Component<Props, State> {
swiper = React.createRef<Swiper>()

goBack = () => {
const currentStepInfo = this.props.stepInfo[this.state.step]
if (currentStepInfo.cancelEvent && currentStepInfo.screenName) {
ValoraAnalytics.track(currentStepInfo.cancelEvent, {
screen: currentStepInfo.screenName,
})
}
if (this.state.step === 0) {
const { step } = this.state
const { topic } = this.props.stepInfo[this.state.step]
if (step === 0) {
if (topic === EducationTopic.backup) {
ValoraAnalytics.track(OnboardingEvents.backup_education_cancel)
} else if (topic === EducationTopic.celo) {
ValoraAnalytics.track(OnboardingEvents.celo_education_cancel)
}
navigateBack()
} else {
if (topic === EducationTopic.backup) {
ValoraAnalytics.track(OnboardingEvents.backup_education_scroll, {
currentStep: step,
direction: ScrollDirection.previous,
})
} else if (topic === EducationTopic.celo) {
ValoraAnalytics.track(OnboardingEvents.celo_education_scroll, {
currentStep: step,
direction: ScrollDirection.previous,
})
}
this.swiper?.current?.scrollBy(-1, true)
}
}
Expand All @@ -62,14 +78,25 @@ export default class Education extends React.Component<Props, State> {
}

nextStep = () => {
const isLastStep = this.state.step === this.props.stepInfo.length - 1
const currentStepInfo = this.props.stepInfo[this.state.step]
ValoraAnalytics.track(currentStepInfo.progressEvent)
const { step } = this.state
const { topic } = this.props.stepInfo[this.state.step]
const isLastStep = step === this.props.stepInfo.length - 1

if (isLastStep) {
this.props.onFinish()
this.swiper?.current?.scrollTo(0)
} else {
if (topic === EducationTopic.backup) {
ValoraAnalytics.track(OnboardingEvents.backup_education_scroll, {
currentStep: step,
direction: ScrollDirection.next,
})
} else if (topic === EducationTopic.celo) {
ValoraAnalytics.track(OnboardingEvents.celo_education_scroll, {
currentStep: step,
direction: ScrollDirection.next,
})
}
this.swiper?.current?.scrollBy(1, true)
}
}
Expand Down
28 changes: 12 additions & 16 deletions packages/mobile/src/account/GoldEducation.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { BtnTypes } from '@celo/react-components/components/Button.v2'
import * as React from 'react'
import React, { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { useDispatch } from 'react-redux'
import Education from 'src/account/Education'
import { AnalyticsEvents } from 'src/analytics/Events'
import Education, { EducationTopic } from 'src/account/Education'
import { OnboardingEvents } from 'src/analytics/Events'
import ValoraAnalytics from 'src/analytics/ValoraAnalytics'
import { setEducationCompleted } from 'src/goldToken/actions'
import { Namespaces } from 'src/i18n'
Expand All @@ -20,7 +20,7 @@ export default function GoldEducation() {
const isCeloEducationComplete = useSelector((state) => state.goldToken.educationCompleted)

const onFinish = () => {
ValoraAnalytics.track(AnalyticsEvents.exchange_gold_nux)
ValoraAnalytics.track(OnboardingEvents.celo_education_complete)

if (isCeloEducationComplete) {
navigateBack()
Expand All @@ -32,6 +32,10 @@ export default function GoldEducation() {

const stepInfo = useStep()

useEffect(() => {
ValoraAnalytics.track(OnboardingEvents.celo_education_start)
}, [])

return (
<Education
isClosable={isCeloEducationComplete}
Expand All @@ -51,27 +55,19 @@ function useStep() {
return [
{
image: celoEducation1,
cancelEvent: AnalyticsEvents.gold_cancel1,
progressEvent: AnalyticsEvents.gold_educate_1_next,
screenName: 'Gold_Nux_1',
topic: EducationTopic.celo,
},
{
image: celoEducation2,
cancelEvent: AnalyticsEvents.gold_cancel2,
progressEvent: AnalyticsEvents.gold_educate_2_next,
screenName: 'Gold_Nux_2',
topic: EducationTopic.celo,
},
{
image: celoEducation3,
cancelEvent: AnalyticsEvents.gold_cancel3,
progressEvent: AnalyticsEvents.gold_educate_3_next,
screenName: 'Gold_Nux_3',
topic: EducationTopic.celo,
},
{
image: celoEducation4, // Placeholder Image
cancelEvent: AnalyticsEvents.gold_cancel4,
progressEvent: AnalyticsEvents.gold_educate_4_next,
screenName: 'Gold_Nux_4',
topic: EducationTopic.celo,
},
].map((step, index) => {
return {
Expand Down
8 changes: 1 addition & 7 deletions packages/mobile/src/account/Invite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import { SafeAreaView, StyleSheet, View } from 'react-native'
import { connect } from 'react-redux'
import { defaultCountryCodeSelector } from 'src/account/selectors'
import { hideAlert, showError } from 'src/alert/actions'
import { AnalyticsEvents } from 'src/analytics/Events'
import ValoraAnalytics from 'src/analytics/ValoraAnalytics'
import { ErrorMessages } from 'src/app/ErrorMessages'
import i18n, { Namespaces, withTranslation } from 'src/i18n'
import ContactPermission from 'src/icons/ContactPermission'
Expand Down Expand Up @@ -85,10 +83,7 @@ class Invite extends React.Component<Props, State> {

const hasGivenContactPermission = await requestContactsPermission()
this.setState({ hasGivenContactPermission })

if (hasGivenContactPermission) {
this.props.importContacts()
}
this.props.importContacts()
}

onSearchQueryChanged = (searchQuery: string) => {
Expand All @@ -98,7 +93,6 @@ class Invite extends React.Component<Props, State> {
onSelectRecipient = (recipient: Recipient) => {
this.props.hideAlert()
if (recipient.e164PhoneNumber) {
ValoraAnalytics.track(AnalyticsEvents.friend_invited)
navigate(Screens.InviteReview, { recipient })
} else {
this.props.showError(ErrorMessages.CANT_SELECT_INVALID_PHONE)
Expand Down
5 changes: 0 additions & 5 deletions packages/mobile/src/account/InviteReview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import { ActivityIndicator, StyleSheet, View } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'
import { connect } from 'react-redux'
import { hideAlert, showError } from 'src/alert/actions'
import { AnalyticsEvents } from 'src/analytics/Events'
import ValoraAnalytics from 'src/analytics/ValoraAnalytics'
import Avatar from 'src/components/Avatar'
import CurrencyDisplay, { DisplayType, FormatType } from 'src/components/CurrencyDisplay'
import FeeIcon from 'src/components/FeeIcon'
Expand Down Expand Up @@ -99,12 +97,10 @@ export class InviteReview extends React.Component<Props, State> {
}

onInviteSMS = async () => {
ValoraAnalytics.track(AnalyticsEvents.invite_friends_sms)
await this.onInvite(InviteBy.SMS)
}

onInviteWhatsApp = async () => {
ValoraAnalytics.track(AnalyticsEvents.invite_friends_whatsapp)
await this.onInvite(InviteBy.WhatsApp)
}

Expand All @@ -126,7 +122,6 @@ export class InviteReview extends React.Component<Props, State> {
}

onEdit = () => {
ValoraAnalytics.track(AnalyticsEvents.invite_edit)
navigateBack()
}

Expand Down
Loading

0 comments on commit 2588f4d

Please sign in to comment.