-
Notifications
You must be signed in to change notification settings - Fork 107
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
[IOCOM-1848] Analytics for Push Notifications Engagement #6598
base: master
Are you sure you want to change the base?
Changes from 16 commits
56e7331
c6860d3
29eaa68
ac7066d
d8cf6e4
3d7b294
48b498b
7301876
80b0aeb
8583e59
b541a1f
785a895
90e0863
6b2423b
83380c0
f677b7d
6076c08
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -7,6 +7,14 @@ import { | |||||
trackNotificationsOptInReminderOnPermissionsOff, | ||||||
trackNotificationsOptInReminderStatus, | ||||||
trackNotificationsOptInSkipSystemPermissions, | ||||||
trackPushNotificationBannerDismissAlert, | ||||||
trackPushNotificationBannerDismissOutcome, | ||||||
trackPushNotificationBannerForceShow, | ||||||
trackPushNotificationBannerStillHidden, | ||||||
trackPushNotificationsBannerClosure, | ||||||
trackPushNotificationsBannerTap, | ||||||
trackPushNotificationsBannerVisualized, | ||||||
trackPushNotificationSystemPopupShown, | ||||||
trackPushNotificationTokenUploadFailure, | ||||||
trackPushNotificationTokenUploadSucceeded, | ||||||
trackSystemNotificationPermissionScreenOutcome, | ||||||
|
@@ -15,6 +23,8 @@ import { | |||||
import { PushNotificationsContentTypeEnum } from "../../../../../definitions/backend/PushNotificationsContentType"; | ||||||
import { ReminderStatusEnum } from "../../../../../definitions/backend/ReminderStatus"; | ||||||
import * as Mixpanel from "../../../../mixpanel"; | ||||||
import ROUTES from "../../../../navigation/routes"; | ||||||
import { MESSAGES_ROUTES } from "../../../messages/navigation/routes"; | ||||||
|
||||||
describe("pushNotifications analytics", () => { | ||||||
beforeEach(() => { | ||||||
|
@@ -236,6 +246,136 @@ describe("pushNotifications analytics", () => { | |||||
new_notification_status: notificationPermissionsEnabled | ||||||
}); | ||||||
}); | ||||||
[MESSAGES_ROUTES.MESSAGES_HOME, ROUTES.SETTINGS_MAIN].forEach(route => { | ||||||
it(`'trackPushNotificationsBannerVisualized' should have expected event name and properties for '${route}'`, () => { | ||||||
const mockMixpanelTrack = getMockMixpanelTrack(); | ||||||
|
||||||
void trackPushNotificationsBannerVisualized(route); | ||||||
|
||||||
expect(mockMixpanelTrack.mock.calls.length).toBe(1); | ||||||
expect(mockMixpanelTrack.mock.calls[0].length).toBe(2); | ||||||
expect(mockMixpanelTrack.mock.calls[0][0]).toBe("BANNER"); | ||||||
expect(mockMixpanelTrack.mock.calls[0][1]).toEqual({ | ||||||
event_category: "UX", | ||||||
event_type: "screen_view", | ||||||
banner_id: "push_notif_activation", | ||||||
banner_page: | ||||||
route === "MESSAGES_HOME" ? "MESSAGES_HOME" : "SETTINGS_MAIN", | ||||||
banner_landing: "os_notification_settings" | ||||||
}); | ||||||
}); | ||||||
it(`'trackPushNotificationsBannerTap' should have expected event name and properties for '${route}'`, () => { | ||||||
const mockMixpanelTrack = getMockMixpanelTrack(); | ||||||
|
||||||
void trackPushNotificationsBannerTap(route); | ||||||
|
||||||
expect(mockMixpanelTrack.mock.calls.length).toBe(1); | ||||||
expect(mockMixpanelTrack.mock.calls[0].length).toBe(2); | ||||||
expect(mockMixpanelTrack.mock.calls[0][0]).toBe("TAP_BANNER"); | ||||||
expect(mockMixpanelTrack.mock.calls[0][1]).toEqual({ | ||||||
event_category: "UX", | ||||||
event_type: "action", | ||||||
banner_id: "push_notif_activation", | ||||||
banner_page: | ||||||
route === "MESSAGES_HOME" ? "MESSAGES_HOME" : "SETTINGS_MAIN", | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
since this is a literal type, why not directly use it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because it is a tracked data that appears in Mixpanel reports. Let's say that we just do With this redundancy in the test, if someone changes the literal value, the test fails so we have an error that should trigger the author (of the change) to reflect upon the impact on mixpanel data. |
||||||
banner_landing: "os_notification_settings" | ||||||
}); | ||||||
}); | ||||||
}); | ||||||
it(`'trackPushNotificationsBannerClosure' should have expected event name and properties`, () => { | ||||||
const mockMixpanelTrack = getMockMixpanelTrack(); | ||||||
|
||||||
void trackPushNotificationsBannerClosure(); | ||||||
|
||||||
expect(mockMixpanelTrack.mock.calls.length).toBe(1); | ||||||
expect(mockMixpanelTrack.mock.calls[0].length).toBe(2); | ||||||
expect(mockMixpanelTrack.mock.calls[0][0]).toBe("CLOSE_BANNER"); | ||||||
expect(mockMixpanelTrack.mock.calls[0][1]).toEqual({ | ||||||
event_category: "UX", | ||||||
event_type: "action", | ||||||
banner_id: "push_notif_activation", | ||||||
banner_page: "MESSAGES_HOME", | ||||||
banner_landing: "os_notification_settings" | ||||||
}); | ||||||
}); | ||||||
it(`'trackPushNotificationSystemPopupShown' should have expected event name and properties`, () => { | ||||||
const mockMixpanelTrack = getMockMixpanelTrack(); | ||||||
|
||||||
void trackPushNotificationSystemPopupShown(); | ||||||
|
||||||
expect(mockMixpanelTrack.mock.calls.length).toBe(1); | ||||||
expect(mockMixpanelTrack.mock.calls[0].length).toBe(2); | ||||||
expect(mockMixpanelTrack.mock.calls[0][0]).toBe("PUSH_NOTIF_SYSTEM_ALERT"); | ||||||
expect(mockMixpanelTrack.mock.calls[0][1]).toEqual({ | ||||||
event_category: "UX", | ||||||
event_type: "screen_view" | ||||||
}); | ||||||
}); | ||||||
it(`'trackPushNotificationBannerDismissAlert' should have expected event name and properties`, () => { | ||||||
const mockMixpanelTrack = getMockMixpanelTrack(); | ||||||
|
||||||
void trackPushNotificationBannerDismissAlert(); | ||||||
|
||||||
expect(mockMixpanelTrack.mock.calls.length).toBe(1); | ||||||
expect(mockMixpanelTrack.mock.calls[0].length).toBe(2); | ||||||
expect(mockMixpanelTrack.mock.calls[0][0]).toBe( | ||||||
"PUSH_NOTIF_THIRD_DISMISS_ALERT" | ||||||
); | ||||||
expect(mockMixpanelTrack.mock.calls[0][1]).toEqual({ | ||||||
event_category: "UX", | ||||||
event_type: "screen_view" | ||||||
}); | ||||||
}); | ||||||
["deactivate" as const, "remind_later" as const].forEach(outcome => | ||||||
it(`'trackPushNotificationBannerDismissOutcome' should have expected event name and properties for '${outcome}'`, () => { | ||||||
const mockMixpanelTrack = getMockMixpanelTrack(); | ||||||
|
||||||
void trackPushNotificationBannerDismissOutcome(outcome); | ||||||
|
||||||
expect(mockMixpanelTrack.mock.calls.length).toBe(1); | ||||||
expect(mockMixpanelTrack.mock.calls[0].length).toBe(2); | ||||||
expect(mockMixpanelTrack.mock.calls[0][0]).toBe( | ||||||
"PUSH_NOTIF_THIRD_DISMISS_ALERT_INTERACTION" | ||||||
); | ||||||
expect(mockMixpanelTrack.mock.calls[0][1]).toEqual({ | ||||||
event_category: "UX", | ||||||
event_type: "action", | ||||||
outcome | ||||||
}); | ||||||
}) | ||||||
); | ||||||
it(`'trackPushNotificationBannerForceShow' should have expected event name and properties`, () => { | ||||||
const mockMixpanelTrack = getMockMixpanelTrack(); | ||||||
|
||||||
void trackPushNotificationBannerForceShow(); | ||||||
|
||||||
expect(mockMixpanelTrack.mock.calls.length).toBe(1); | ||||||
expect(mockMixpanelTrack.mock.calls[0].length).toBe(2); | ||||||
expect(mockMixpanelTrack.mock.calls[0][0]).toBe( | ||||||
"PUSH_NOTIF_BANNER_FORCE_SHOW" | ||||||
); | ||||||
expect(mockMixpanelTrack.mock.calls[0][1]).toEqual({ | ||||||
event_category: "TECH", | ||||||
event_type: undefined | ||||||
}); | ||||||
}); | ||||||
it(`'trackPushNotificationBannerStillHidden' should have expected event name and properties`, () => { | ||||||
const mockMixpanelTrack = getMockMixpanelTrack(); | ||||||
const unreadMessagesCount = 3; | ||||||
|
||||||
void trackPushNotificationBannerStillHidden(unreadMessagesCount); | ||||||
|
||||||
expect(mockMixpanelTrack.mock.calls.length).toBe(1); | ||||||
expect(mockMixpanelTrack.mock.calls[0].length).toBe(2); | ||||||
expect(mockMixpanelTrack.mock.calls[0][0]).toBe( | ||||||
"PUSH_NOTIF_BANNER_STILL_HIDDEN" | ||||||
); | ||||||
expect(mockMixpanelTrack.mock.calls[0][1]).toEqual({ | ||||||
event_category: "TECH", | ||||||
event_type: undefined, | ||||||
unread_count: unreadMessagesCount | ||||||
}); | ||||||
}); | ||||||
}); | ||||||
|
||||||
const getMockMixpanelTrack = () => | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
import { PushNotificationsContentTypeEnum } from "../../../../definitions/backend/PushNotificationsContentType"; | ||
import { ReminderStatusEnum } from "../../../../definitions/backend/ReminderStatus"; | ||
import { mixpanelTrack } from "../../../mixpanel"; | ||
import ROUTES from "../../../navigation/routes"; | ||
import { buildEventProperties } from "../../../utils/analytics"; | ||
import { MESSAGES_ROUTES } from "../../messages/navigation/routes"; | ||
|
||
export const trackNotificationInstallationTokenNotChanged = () => | ||
void mixpanelTrack( | ||
|
@@ -90,3 +92,73 @@ export const trackNotificationPermissionsStatus = ( | |
}); | ||
void mixpanelTrack(eventName, props); | ||
}; | ||
|
||
export const trackPushNotificationsBannerVisualized = ( | ||
bannerPage: typeof MESSAGES_ROUTES.MESSAGES_HOME | typeof ROUTES.SETTINGS_MAIN | ||
) => { | ||
const eventName = "BANNER"; | ||
const props = buildEventProperties("UX", "screen_view", { | ||
banner_id: "push_notif_activation", | ||
banner_page: bannerPage, | ||
banner_landing: "os_notification_settings" | ||
}); | ||
void mixpanelTrack(eventName, props); | ||
}; | ||
|
||
export const trackPushNotificationsBannerTap = ( | ||
bannerPage: typeof MESSAGES_ROUTES.MESSAGES_HOME | typeof ROUTES.SETTINGS_MAIN | ||
) => { | ||
const eventName = "TAP_BANNER"; | ||
const props = buildEventProperties("UX", "action", { | ||
banner_id: "push_notif_activation", | ||
banner_page: bannerPage, | ||
banner_landing: "os_notification_settings" | ||
}); | ||
void mixpanelTrack(eventName, props); | ||
}; | ||
|
||
export const trackPushNotificationsBannerClosure = () => { | ||
const eventName = "CLOSE_BANNER"; | ||
const props = buildEventProperties("UX", "action", { | ||
banner_id: "push_notif_activation", | ||
banner_page: MESSAGES_ROUTES.MESSAGES_HOME, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. since this banner is not attached to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The event is related to the user manually closing the banner but (the banner) appears only in the messages' home and in the settings screen. Latter does not offer an option to close the banner so we do not have a use-case where the banner can be close anywhere but the messages' home |
||
banner_landing: "os_notification_settings" | ||
}); | ||
void mixpanelTrack(eventName, props); | ||
}; | ||
|
||
export const trackPushNotificationSystemPopupShown = () => { | ||
const eventName = "PUSH_NOTIF_SYSTEM_ALERT"; | ||
const props = buildEventProperties("UX", "screen_view"); | ||
void mixpanelTrack(eventName, props); | ||
}; | ||
|
||
export const trackPushNotificationBannerDismissAlert = () => { | ||
const eventName = "PUSH_NOTIF_THIRD_DISMISS_ALERT"; | ||
const props = buildEventProperties("UX", "screen_view"); | ||
void mixpanelTrack(eventName, props); | ||
}; | ||
|
||
export const trackPushNotificationBannerDismissOutcome = ( | ||
outcome: "deactivate" | "remind_later" | ||
) => { | ||
const eventName = "PUSH_NOTIF_THIRD_DISMISS_ALERT_INTERACTION"; | ||
const props = buildEventProperties("UX", "action", { outcome }); | ||
void mixpanelTrack(eventName, props); | ||
}; | ||
|
||
export const trackPushNotificationBannerForceShow = () => { | ||
const eventName = "PUSH_NOTIF_BANNER_FORCE_SHOW"; | ||
const props = buildEventProperties("TECH", undefined); | ||
void mixpanelTrack(eventName, props); | ||
}; | ||
|
||
export const trackPushNotificationBannerStillHidden = ( | ||
unreadMessagesCount: number | ||
) => { | ||
const eventName = "PUSH_NOTIF_BANNER_STILL_HIDDEN"; | ||
const props = buildEventProperties("TECH", undefined, { | ||
unread_count: unreadMessagesCount | ||
}); | ||
void mixpanelTrack(eventName, props); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,15 @@ import { | |
shouldResetNotificationBannerDismissStateSelector, | ||
timesPushNotificationBannerDismissedSelector | ||
} from "../store/selectors/notificationsBannerDismissed"; | ||
import { MESSAGES_ROUTES } from "../../messages/navigation/routes"; | ||
import { | ||
trackPushNotificationBannerDismissAlert, | ||
trackPushNotificationBannerDismissOutcome, | ||
trackPushNotificationBannerForceShow, | ||
trackPushNotificationsBannerClosure, | ||
trackPushNotificationsBannerTap, | ||
trackPushNotificationsBannerVisualized | ||
} from "../analytics"; | ||
type Props = { | ||
closeHandler: () => void; | ||
}; | ||
|
@@ -31,24 +40,35 @@ export const PushNotificationsBanner = ({ closeHandler }: Props) => { | |
|
||
React.useEffect(() => { | ||
if (shouldResetDismissState) { | ||
trackPushNotificationBannerForceShow(); | ||
dispatch(resetNotificationBannerDismissState()); | ||
} | ||
}, [dispatch, shouldResetDismissState]); | ||
React.useEffect(() => { | ||
trackPushNotificationsBannerVisualized(MESSAGES_ROUTES.MESSAGES_HOME); | ||
}, []); | ||
|
||
const dismissionCount = useIOSelector( | ||
timesPushNotificationBannerDismissedSelector | ||
); | ||
const discardModal = usePushNotificationsBannerBottomSheet(closeHandler); | ||
|
||
const onClose = () => { | ||
trackPushNotificationsBannerClosure(); | ||
if (dismissionCount >= 2) { | ||
trackPushNotificationBannerDismissAlert(); | ||
discardModal.present(); | ||
} else { | ||
dispatch(setUserDismissedNotificationsBanner()); | ||
closeHandler(); | ||
} | ||
}; | ||
|
||
const onPress = React.useCallback(() => { | ||
trackPushNotificationsBannerTap(MESSAGES_ROUTES.MESSAGES_HOME); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not use the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because this banner does not have an use case for a screen that is not the messages' home |
||
openSystemNotificationSettingsScreen(); | ||
}, []); | ||
|
||
return ( | ||
<View style={styles.margins} testID="pushnotif-bannerContainer"> | ||
<Banner | ||
|
@@ -61,7 +81,7 @@ export const PushNotificationsBanner = ({ closeHandler }: Props) => { | |
size="big" | ||
onClose={onClose} | ||
labelClose={I18n.t("global.buttons.close")} | ||
onPress={openSystemNotificationSettingsScreen} | ||
onPress={onPress} | ||
/> | ||
{discardModal.bottomSheet} | ||
</View> | ||
|
@@ -73,8 +93,15 @@ const usePushNotificationsBannerBottomSheet = ( | |
) => { | ||
const dispatch = useIODispatch(); | ||
|
||
const fullCloseHandler = () => | ||
const internalRemindLaterHandler = () => { | ||
trackPushNotificationBannerDismissOutcome("remind_later"); | ||
remindLaterHandler(); | ||
}; | ||
|
||
const fullCloseHandler = () => { | ||
trackPushNotificationBannerDismissOutcome("deactivate"); | ||
dispatch(setPushNotificationBannerForceDismissed()); | ||
}; | ||
|
||
return useIOBottomSheetModal({ | ||
title: I18n.t( | ||
|
@@ -88,7 +115,7 @@ const usePushNotificationsBannerBottomSheet = ( | |
label: I18n.t( | ||
"features.messages.pushNotifications.banner.bottomSheet.cta" | ||
), | ||
onPress: remindLaterHandler | ||
onPress: internalRemindLaterHandler | ||
}, | ||
secondary: { | ||
label: I18n.t( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here :p