Skip to content

Commit

Permalink
chore: [IOPID-1787] Add TRACKING Profile Property (#5857)
Browse files Browse the repository at this point in the history
## Short description
This PR adds the `TRACKING` profile property as described
[here](https://pagopa.atlassian.net/wiki/spaces/CM1/pages/944242689/TPR+Integrazioni+Login+e+Onboarding+in+appIO).

## List of changes proposed in this pull request
- Added `TRACKING`  profile property.
- Added some utility functions to save the new profile property.
- Tried to fix some edge cases that happen during opt-in/opt-out events
by switching function calls and add some timings.

## How to test
Run the application and change your permission about sharing or not MP
data. Check on MP if the profile property is updated correctly and if
all the events are as expected.

See
[here](https://eu.mixpanel.com/project/2460815/view/3003771/app/profile#distinct_id=2A3F1734-A6CE-4878-B1F4-575E19346E3E)
on 13/06/2024, to check the events flow collected during development.
  • Loading branch information
shadowsheep1 authored Jun 17, 2024
1 parent 9e382c2 commit c0fe107
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 15 deletions.
9 changes: 7 additions & 2 deletions ts/mixpanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,13 @@ export const resetMixpanel = () => {

export const terminateMixpanel = () => {
if (mixpanel) {
mixpanel.flush();
mixpanel.optOutTracking();
const mp = mixpanel;
mp.flush();
// Wait for the flush to complete
// (mainly) to let profile properties to update.
setTimeout(() => {
mp.optOutTracking();
}, 1000);
mixpanel = undefined;
}
};
Expand Down
13 changes: 13 additions & 0 deletions ts/mixpanelConfig/mixpanelPropertyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from "../store/reducers/profile";
import { fastLoginOptInSelector } from "../features/fastLogin/store/selectors";
import { ServicesPreferencesModeEnum } from "../../definitions/backend/ServicesPreferencesMode";
import { isMixpanelEnabled } from "./../store/reducers/persistedPreferences";

export type Property<K, T extends keyof K> = {
property: T;
Expand Down Expand Up @@ -59,3 +60,15 @@ export const serviceConfigHandler = (
}
return serviceConfigState;
};

export type MixpanelOptInTrackingType = "accepted" | "declined" | "not set";
export const mixpanelOptInHandler = (
state: GlobalState
): MixpanelOptInTrackingType => {
const isMixpanelEnabledResult = isMixpanelEnabled(state);
return isMixpanelEnabledResult === undefined
? "not set"
: isMixpanelEnabledResult
? "accepted"
: "declined";
};
7 changes: 6 additions & 1 deletion ts/mixpanelConfig/profileProperties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import { idpSelector } from "../store/reducers/authentication";
import { tosVersionSelector } from "../store/reducers/profile";
import { checkNotificationPermissions } from "../features/pushNotifications/utils";
import {
MixpanelOptInTrackingType,
Property,
PropertyToUpdate,
loginSessionConfigHandler,
mixpanelOptInHandler,
notificationConfigurationHandler,
serviceConfigHandler
} from "./mixpanelPropertyUtils";
Expand All @@ -28,6 +30,7 @@ type ProfileProperties = {
NOTIFICATION_CONFIGURATION: NotificationPreferenceConfiguration;
NOTIFICATION_PERMISSION: NotificationPermissionType;
SERVICE_CONFIGURATION: ServiceConfigurationTrackingType;
TRACKING: MixpanelOptInTrackingType;
};

export const updateMixpanelProfileProperties = async (
Expand All @@ -44,6 +47,7 @@ export const updateMixpanelProfileProperties = async (
const NOTIFICATION_CONFIGURATION = notificationConfigurationHandler(state);
const notificationsEnabled = await checkNotificationPermissions();
const SERVICE_CONFIGURATION = serviceConfigHandler(state);
const TRACKING = mixpanelOptInHandler(state);

const profilePropertiesObject: ProfileProperties = {
LOGIN_SESSION,
Expand All @@ -53,7 +57,8 @@ export const updateMixpanelProfileProperties = async (
NOTIFICATION_CONFIGURATION,
NOTIFICATION_PERMISSION:
getNotificationPermissionType(notificationsEnabled),
SERVICE_CONFIGURATION
SERVICE_CONFIGURATION,
TRACKING
};

if (forceUpdateFor) {
Expand Down
5 changes: 5 additions & 0 deletions ts/sagas/mixpanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
sessionInvalid
} from "../store/actions/authentication";
import { GlobalState } from "../store/reducers/types";
import { updateMixpanelProfileProperties } from "../mixpanelConfig/profileProperties";

export function* watchForActionsDifferentFromRequestLogoutThatMustResetMixpanel() {
yield* takeLatest(
Expand Down Expand Up @@ -96,4 +97,8 @@ export function* askMixpanelOptIn() {
NavigationService.dispatchNavigationAction,
StackActions.popToTop()
);
// Update mixpanel profile properties
// (mainly for mixpanel opt-in)
const state = (yield* select()) as GlobalState;
yield* call(updateMixpanelProfileProperties, state);
}
22 changes: 15 additions & 7 deletions ts/screens/onboarding/OnboardingShareDataScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { RNavScreenWithLargeHeader } from "../../components/ui/RNavScreenWithLar
import I18n from "../../i18n";
import { setMixpanelEnabled } from "../../store/actions/mixpanel";
import { abortOnboarding } from "../../store/actions/onboarding";
import { useIODispatch, useIOSelector } from "../../store/hooks";
import { useIODispatch, useIOSelector, useIOStore } from "../../store/hooks";
import { isProfileFirstOnBoardingSelector } from "../../store/reducers/profile";
import { GlobalState } from "../../store/reducers/types";
import { getFlowType } from "../../utils/analytics";
Expand All @@ -28,11 +28,13 @@ type Props = ReturnType<typeof mapDispatchToProps> &

const OnboardingShareDataScreen = (props: Props): React.ReactElement => {
const dispatch = useIODispatch();
const store = useIOStore();
const { present, bottomSheet } = useConfirmOptOutBottomSheet(() => {
const flow = getFlowType(true, isFirstOnBoarding);
trackMixpanelDeclined(flow);
trackMixpanelSetEnabled(false, flow);
props.setMixpanelEnabled(false);
trackMixpanelSetEnabled(false, flow, store.getState()).finally(() => {
props.setMixpanelEnabled(false);
});
});

const isFirstOnBoarding = useIOSelector(isProfileFirstOnBoardingSelector);
Expand Down Expand Up @@ -96,11 +98,17 @@ const OnboardingShareDataScreen = (props: Props): React.ReactElement => {
"profile.main.privacy.shareData.screen.cta.shareData"
),
onPress: () => {
trackMixpanelSetEnabled(
true,
getFlowType(true, isFirstOnBoarding)
);
// Before tracking any event, we need to enable mixpanel
props.setMixpanelEnabled(true);
// We wait some time to allow mixpanel to be enabled
// before tracking the event
setTimeout(() => {
void trackMixpanelSetEnabled(
true,
getFlowType(false, isFirstOnBoarding),
store.getState()
);
}, 1000);
},
testID: "share-data-confirm-button"
}
Expand Down
19 changes: 16 additions & 3 deletions ts/screens/profile/ShareDataScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { isMixpanelEnabled } from "../../store/reducers/persistedPreferences";
import { GlobalState } from "../../store/reducers/types";
import { getFlowType } from "../../utils/analytics";
import { useOnFirstRender } from "../../utils/hooks/useOnFirstRender";
import { useIOStore } from "../../store/hooks";
import { trackMixpanelScreen } from "./analytics";
import {
trackMixpanelDeclined,
Expand All @@ -27,11 +28,14 @@ type Props = ReturnType<typeof mapDispatchToProps> &
ReturnType<typeof mapStateToProps>;

const ShareDataScreen = (props: Props): React.ReactElement => {
const store = useIOStore();
const { present, bottomSheet } = useConfirmOptOutBottomSheet(() => {
const flow = getFlowType(false, false);
trackMixpanelDeclined(flow);
trackMixpanelSetEnabled(false, flow);
props.setMixpanelEnabled(false);
trackMixpanelSetEnabled(false, flow, store.getState()).finally(() => {
props.setMixpanelEnabled(false);
});

IOToast.success(
I18n.t("profile.main.privacy.shareData.screen.confirmToast")
);
Expand Down Expand Up @@ -64,8 +68,17 @@ const ShareDataScreen = (props: Props): React.ReactElement => {
"profile.main.privacy.shareData.screen.cta.dontShareData"
),
onPress: () => {
trackMixpanelSetEnabled(true, getFlowType(false, false));
// Before tracking any event, we need to enable mixpanel
props.setMixpanelEnabled(true);
// We wait some time to allow mixpanel to be enabled
// before tracking the event
setTimeout(() => {
void trackMixpanelSetEnabled(
true,
getFlowType(false, false),
store.getState()
);
}, 1000);
IOToast.success(
I18n.t("profile.main.privacy.shareData.screen.confirmToast")
);
Expand Down
14 changes: 12 additions & 2 deletions ts/screens/profile/analytics/mixpanel/mixpanelAnalytics.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
import { mixpanelTrack } from "../../../../mixpanel";
import { updateMixpanelProfileProperties } from "../../../../mixpanelConfig/profileProperties";
import { GlobalState } from "../../../../store/reducers/types";
import { FlowType, buildEventProperties } from "../../../../utils/analytics";

export function trackMixpanelSetEnabled(value: boolean, flow: FlowType) {
export async function trackMixpanelSetEnabled(
isEnabled: boolean,
flow: FlowType,
state: GlobalState
) {
await updateMixpanelProfileProperties(state, {
property: "TRACKING",
value: isEnabled ? "accepted" : "declined"
});
void mixpanelTrack(
"MIXPANEL_SET_ENABLED",
buildEventProperties(
"UX",
"action",
{
value
value: isEnabled
},
flow
)
Expand Down

0 comments on commit c0fe107

Please sign in to comment.