Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GTM event tracking #51599

Merged
merged 20 commits into from
Nov 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions __mocks__/@react-native-firebase/analytics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default function analytics() {
return {
logEvent: jest.fn(),
};
}
2 changes: 1 addition & 1 deletion config/webpack/webpack.common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ const getCommonConfiguration = ({file = '.env', platform = 'web'}: Environment):
isWeb: platform === 'web',
isProduction: file === '.env.production',
isStaging: file === '.env.staging',
useThirdPartyScripts: process.env.USE_THIRD_PARTY_SCRIPTS === 'true' || (platform === 'web' && file === '.env.production'),
useThirdPartyScripts: process.env.USE_THIRD_PARTY_SCRIPTS === 'true' || (platform === 'web' && ['.env.production', '.env.staging'].includes(file)),
}),
new PreloadWebpackPlugin({
rel: 'preload',
Expand Down
8 changes: 8 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6202,6 +6202,14 @@ const CONST = {
HAS_VIOLATIONS: 'hasViolations',
HAS_TRANSACTION_THREAD_VIOLATIONS: 'hasTransactionThreadViolations',
},

ANALYTICS: {
EVENT: {
SIGN_UP: 'sign_up',
WORKSPACE_CREATED: 'workspace_created',
PAID_ADOPTION: 'paid_adoption',
},
},
} as const;

type Country = keyof typeof CONST.ALL_COUNTRIES;
Expand Down
5 changes: 4 additions & 1 deletion src/libs/GoogleTagManager/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import type {ValueOf} from 'type-fest';
import type CONST from '@src/CONST';

/**
* An event that can be published to Google Tag Manager. New events must be configured in GTM before they can be used
* in the app.
*/
type GoogleTagManagerEvent = 'sign_up' | 'workspace_created' | 'paid_adoption';
type GoogleTagManagerEvent = ValueOf<typeof CONST.ANALYTICS.EVENT>;

type GoogleTagManagerModule = {
publishEvent: (event: GoogleTagManagerEvent, accountID: number) => void;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import {createStackNavigator} from '@react-navigation/stack';
import React, {useCallback} from 'react';
import React, {useCallback, useEffect} from 'react';
import {View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import NoDropZone from '@components/DragAndDrop/NoDropZone';
import FocusTrapForScreens from '@components/FocusTrap/FocusTrapForScreen';
import useKeyboardShortcut from '@hooks/useKeyboardShortcut';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import GoogleTagManager from '@libs/GoogleTagManager';
import OnboardingModalNavigatorScreenOptions from '@libs/Navigation/AppNavigator/OnboardingModalNavigatorScreenOptions';
import type {OnboardingModalNavigatorParamList} from '@libs/Navigation/types';
import OnboardingRefManager from '@libs/OnboardingRefManager';
Expand All @@ -14,6 +16,7 @@ import OnboardingEmployees from '@pages/OnboardingEmployees';
import OnboardingPersonalDetails from '@pages/OnboardingPersonalDetails';
import OnboardingPurpose from '@pages/OnboardingPurpose';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import SCREENS from '@src/SCREENS';
import Overlay from './Overlay';

Expand All @@ -23,6 +26,17 @@ function OnboardingModalNavigator() {
const styles = useThemeStyles();
const {onboardingIsMediumOrLargerScreenWidth} = useResponsiveLayout();
const outerViewRef = React.useRef<View>(null);
const [accountID] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.accountID ?? 0});

// Publish a sign_up event when we start the onboarding flow. This should track basic sign ups
// as well as Google and Apple SSO.
useEffect(() => {
if (!accountID) {
return;
}

GoogleTagManager.publishEvent(CONST.ANALYTICS.EVENT.SIGN_UP, accountID);
}, [accountID]);
Comment on lines +31 to +39
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there cases when this effect could run multiple times for the user? ie they will get to this point and close the app. They will come back to it later and we will again send the sign_up event. Not sure if its a problem though, just thought about it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah if they restart/refresh the app during onboarding, this would get called again. Since it's kind of an edge case we're just ignoring that for now. Also it's not too big of a deal as long as it stays consistent between A/B tests.


const handleOuterClick = useCallback(() => {
OnboardingRefManager.handleOuterClick();
Expand Down
10 changes: 10 additions & 0 deletions src/libs/actions/IOU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import DateUtils from '@libs/DateUtils';
import DistanceRequestUtils from '@libs/DistanceRequestUtils';
import * as ErrorUtils from '@libs/ErrorUtils';
import * as FileUtils from '@libs/fileDownload/FileUtils';
import GoogleTagManager from '@libs/GoogleTagManager';
import * as IOUUtils from '@libs/IOUUtils';
import * as LocalePhoneNumber from '@libs/LocalePhoneNumber';
import * as Localize from '@libs/Localize';
Expand Down Expand Up @@ -3415,6 +3416,7 @@ function categorizeTrackedExpense(
comment: string,
merchant: string,
created: string,
isDraftPolicy: boolean,
category?: string,
tag?: string,
taxCode = '',
Expand Down Expand Up @@ -3471,6 +3473,12 @@ function categorizeTrackedExpense(
};

API.write(WRITE_COMMANDS.CATEGORIZE_TRACKED_EXPENSE, parameters, {optimisticData, successData, failureData});

// If a draft policy was used, then the CategorizeTrackedExpense command will create a real one
// so let's track that conversion here
if (isDraftPolicy) {
GoogleTagManager.publishEvent(CONST.ANALYTICS.EVENT.WORKSPACE_CREATED, userAccountID);
}
}

function shareTrackedExpense(
Expand Down Expand Up @@ -3770,6 +3778,7 @@ function trackExpense(
payeeAccountID: number,
participant: Participant,
comment: string,
isDraftPolicy: boolean,
receipt?: Receipt,
category?: string,
tag?: string,
Expand Down Expand Up @@ -3865,6 +3874,7 @@ function trackExpense(
comment,
merchant,
created,
isDraftPolicy,
category,
tag,
taxCode,
Expand Down
28 changes: 18 additions & 10 deletions src/libs/actions/PaymentMethods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type {
} from '@libs/API/parameters';
import {READ_COMMANDS, SIDE_EFFECT_REQUEST_COMMANDS, WRITE_COMMANDS} from '@libs/API/types';
import * as CardUtils from '@libs/CardUtils';
import GoogleTagManager from '@libs/GoogleTagManager';
import Navigation from '@libs/Navigation/Navigation';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
Expand Down Expand Up @@ -159,7 +160,7 @@ function makeDefaultPaymentMethod(bankAccountID: number, fundID: number, previou
* Calls the API to add a new card.
*
*/
function addPaymentCard(params: PaymentCardParams) {
function addPaymentCard(accountID: number, params: PaymentCardParams) {
const cardMonth = CardUtils.getMonthFromExpirationDateString(params.expirationDate);
const cardYear = CardUtils.getYearFromExpirationDateString(params.expirationDate);

Expand Down Expand Up @@ -203,21 +204,26 @@ function addPaymentCard(params: PaymentCardParams) {
successData,
failureData,
});

GoogleTagManager.publishEvent(CONST.ANALYTICS.EVENT.PAID_ADOPTION, accountID);
}

/**
* Calls the API to add a new card.
*
*/
function addSubscriptionPaymentCard(cardData: {
cardNumber: string;
cardYear: string;
cardMonth: string;
cardCVV: string;
addressName: string;
addressZip: string;
currency: ValueOf<typeof CONST.PAYMENT_CARD_CURRENCY>;
}) {
function addSubscriptionPaymentCard(
accountID: number,
cardData: {
cardNumber: string;
cardYear: string;
cardMonth: string;
cardCVV: string;
addressName: string;
addressZip: string;
currency: ValueOf<typeof CONST.PAYMENT_CARD_CURRENCY>;
},
) {
const {cardNumber, cardYear, cardMonth, cardCVV, addressName, addressZip, currency} = cardData;

const parameters: AddPaymentCardParams = {
Expand Down Expand Up @@ -265,6 +271,8 @@ function addSubscriptionPaymentCard(cardData: {
failureData,
});
}

GoogleTagManager.publishEvent(CONST.ANALYTICS.EVENT.PAID_ADOPTION, accountID);
}

/**
Expand Down
10 changes: 10 additions & 0 deletions src/libs/actions/Policy/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import * as CurrencyUtils from '@libs/CurrencyUtils';
import DateUtils from '@libs/DateUtils';
import * as ErrorUtils from '@libs/ErrorUtils';
import getIsNarrowLayout from '@libs/getIsNarrowLayout';
import GoogleTagManager from '@libs/GoogleTagManager';
import Log from '@libs/Log';
import * as NetworkStore from '@libs/Network/NetworkStore';
import * as NumberUtils from '@libs/NumberUtils';
Expand Down Expand Up @@ -1838,6 +1839,11 @@ function createWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policyName
const {optimisticData, failureData, successData, params} = buildPolicyData(policyOwnerEmail, makeMeAdmin, policyName, policyID, undefined, engagementChoice);
API.write(WRITE_COMMANDS.CREATE_WORKSPACE, params, {optimisticData, successData, failureData});

// Publish a workspace created event if this is their first policy
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NAB, I wonder if the events and should be called workspace or policy since we try to keep everything in the BE as policy. Also its weird to see both of them in the comment on one line :D

Suggested change
// Publish a workspace created event if this is their first policy
// Publish a workspace created event if this is their first workspace

if (getAdminPolicies().length === 0) {
GoogleTagManager.publishEvent(CONST.ANALYTICS.EVENT.WORKSPACE_CREATED, sessionAccountID);
}

return params;
}

Expand Down Expand Up @@ -3743,6 +3749,10 @@ function setWorkspaceEReceiptsEnabled(policyID: string, eReceipts: boolean) {
API.write(WRITE_COMMANDS.SET_WORKSPACE_ERECEIPTS_ENABLED, parameters, onyxData);
}

function getAdminPolicies(): Policy[] {
return Object.values(allPolicies ?? {}).filter<Policy>((policy): policy is Policy => !!policy && policy.role === CONST.POLICY.ROLE.ADMIN && policy.type !== CONST.POLICY.TYPE.PERSONAL);
}

function getAdminPoliciesConnectedToSageIntacct(): Policy[] {
return Object.values(allPolicies ?? {}).filter<Policy>((policy): policy is Policy => !!policy && policy.role === CONST.POLICY.ROLE.ADMIN && !!policy?.connections?.intacct);
}
Expand Down
16 changes: 9 additions & 7 deletions src/pages/iou/request/step/IOURequestStepAmount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,24 +45,25 @@ type IOURequestStepAmountProps = WithCurrentUserPersonalDetailsProps &
function IOURequestStepAmount({
report,
route: {
params: {iouType, reportID, transactionID, backTo, pageIndex, action, currency: selectedCurrency = ''},
params: {iouType, reportID, transactionID = '-1', backTo, pageIndex, action, currency: selectedCurrency = ''},
},
transaction,
currentUserPersonalDetails,
shouldKeepUserInput = false,
}: IOURequestStepAmountProps) {
const [splitDraftTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID ?? -1}`);
const [draftTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID ?? -1}`);
const [skipConfirmation] = useOnyx(`${ONYXKEYS.COLLECTION.SKIP_CONFIRMATION}${transactionID ?? -1}`);
const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST);
const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : -1}`);

const {translate} = useLocalize();
const textInput = useRef<BaseTextInputRef | null>(null);
const focusTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const isSaveButtonPressed = useRef(false);
const iouRequestType = getRequestType(transaction);
const policyID = report?.policyID ?? '-1';

const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID ?? -1}`);
const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`);
const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST);
const [draftTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`);
const [splitDraftTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.SPLIT_TRANSACTION_DRAFT}${transactionID}`);
const [skipConfirmation] = useOnyx(`${ONYXKEYS.COLLECTION.SKIP_CONFIRMATION}${transactionID}`);

const isEditing = action === CONST.IOU.ACTION.EDIT;
const isSplitBill = iouType === CONST.IOU.TYPE.SPLIT;
Expand Down Expand Up @@ -236,6 +237,7 @@ function IOURequestStepAmount({
currentUserPersonalDetails.accountID,
participants.at(0) ?? {},
'',
false,
);
return;
}
Expand Down
3 changes: 3 additions & 0 deletions src/pages/iou/request/step/IOURequestStepConfirmation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ function IOURequestStepConfirmation({

const report = reportReal ?? reportDraft;
const policy = policyReal ?? policyDraft;
const isDraftPolicy = policy === policyDraft;
const policyCategories = policyCategoriesReal ?? policyCategoriesDraft;

const styles = useThemeStyles();
Expand Down Expand Up @@ -287,6 +288,7 @@ function IOURequestStepConfirmation({
currentUserPersonalDetails.accountID,
participant,
trimmedComment,
isDraftPolicy,
receiptObj,
transaction.category,
transaction.tag,
Expand Down Expand Up @@ -317,6 +319,7 @@ function IOURequestStepConfirmation({
policyCategories,
action,
customUnitRateID,
isDraftPolicy,
],
);

Expand Down
1 change: 1 addition & 0 deletions src/pages/iou/request/step/IOURequestStepDistance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ function IOURequestStepDistance({
currentUserPersonalDetails.accountID,
participant,
'',
false,
{},
'',
'',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ function IOURequestStepScan({
currentUserPersonalDetails.accountID,
participant,
'',
false,
receipt,
);
} else {
Expand Down Expand Up @@ -335,6 +336,7 @@ function IOURequestStepScan({
currentUserPersonalDetails.accountID,
participant,
'',
false,
receipt,
'',
'',
Expand Down
2 changes: 2 additions & 0 deletions src/pages/iou/request/step/IOURequestStepScan/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ function IOURequestStepScan({
currentUserPersonalDetails.accountID,
participant,
'',
false,
receipt,
);
} else {
Expand Down Expand Up @@ -365,6 +366,7 @@ function IOURequestStepScan({
currentUserPersonalDetails.accountID,
participant,
'',
false,
receipt,
'',
'',
Expand Down
28 changes: 16 additions & 12 deletions src/pages/settings/Subscription/PaymentCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ function AddPaymentCard() {
const styles = useThemeStyles();
const {translate} = useLocalize();
const [privateSubscription] = useOnyx(ONYXKEYS.NVP_PRIVATE_SUBSCRIPTION);
const [accountID] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.accountID ?? 0});

const subscriptionPlan = useSubscriptionPlan();
const subscriptionPrice = useSubscriptionPrice();
Expand All @@ -43,18 +44,21 @@ function AddPaymentCard() {
};
}, []);

const addPaymentCard = useCallback((values: FormOnyxValues<typeof ONYXKEYS.FORMS.ADD_PAYMENT_CARD_FORM>) => {
const cardData = {
cardNumber: CardUtils.getMCardNumberString(values.cardNumber),
cardMonth: CardUtils.getMonthFromExpirationDateString(values.expirationDate),
cardYear: CardUtils.getYearFromExpirationDateString(values.expirationDate),
cardCVV: values.securityCode,
addressName: values.nameOnCard,
addressZip: values.addressZipCode,
currency: values.currency ?? CONST.PAYMENT_CARD_CURRENCY.USD,
};
PaymentMethods.addSubscriptionPaymentCard(cardData);
}, []);
const addPaymentCard = useCallback(
(values: FormOnyxValues<typeof ONYXKEYS.FORMS.ADD_PAYMENT_CARD_FORM>) => {
const cardData = {
cardNumber: CardUtils.getMCardNumberString(values.cardNumber),
cardMonth: CardUtils.getMonthFromExpirationDateString(values.expirationDate),
cardYear: CardUtils.getYearFromExpirationDateString(values.expirationDate),
cardCVV: values.securityCode,
addressName: values.nameOnCard,
addressZip: values.addressZipCode,
currency: values.currency ?? CONST.PAYMENT_CARD_CURRENCY.USD,
};
PaymentMethods.addSubscriptionPaymentCard(accountID ?? 0, cardData);
},
[accountID],
);

const [formData] = useOnyx(ONYXKEYS.FORMS.ADD_PAYMENT_CARD_FORM);
const prevFormDataSetupComplete = usePrevious(!!formData?.setupComplete);
Expand Down
Loading
Loading