From 7492e945bb3019d8be64df0f8509d04333d592f2 Mon Sep 17 00:00:00 2001 From: Pierpaolo Piras <108515572+PierpaoloPirasSp@users.noreply.github.com> Date: Thu, 17 Oct 2024 16:38:48 +0200 Subject: [PATCH] chore(IT Wallet): [SIW-1648] Mixpanel events new features (#6235) ## Short description Add some mixpanel events and handlers for profile and super properties. ## List of changes proposed in this pull request - Add new mixpanel [events](https://docs.google.com/spreadsheets/d/18aswiKUcLxnIRrZoOQ3c9pBSnhq-e743_gKKljaHTnE/edit?pli=1&gid=712076735#gid=712076735) - Update ITW profile and super properties ## How to test - Retrieve WI. - Add documents. - Filter documents by category. - View document details. - Revoke WI. - Look at mixpanel dashboard in order to watch over properties updates. --------- Co-authored-by: Mario Perrotta Co-authored-by: LazyAfternoons Co-authored-by: Alessandro Mazzon --- ts/features/itwallet/analytics/enum.ts | 24 +- ts/features/itwallet/analytics/index.ts | 674 ++++++++++++------ .../common/components/ItwDiscoveryBanner.tsx | 9 +- .../ItwEidInfoBottomSheetContent.tsx | 13 +- .../common/components/ItwReleaserName.tsx | 16 +- .../saga/checkCredentialsStatusAttestation.ts | 10 +- .../ItwIdentificationModeSelectionScreen.tsx | 14 +- .../screens/cie/ItwCieCardReaderScreen.tsx | 22 +- ...uanceCredentialAsyncContinuationScreen.tsx | 26 +- .../ItwIssuanceCredentialFailureScreen.tsx | 46 +- .../screens/ItwIssuanceEidFailureScreen.tsx | 32 +- .../screens/ItwIssuanceEidResultScreen.tsx | 15 +- .../screens/ItwIdentityNotMatchingScreen.tsx | 6 +- .../itwallet/machine/credential/actions.ts | 13 +- ts/features/itwallet/machine/eid/actions.ts | 5 + ts/features/itwallet/machine/eid/machine.ts | 7 +- .../components/ItwCredentialTrustmark.tsx | 23 +- .../ItwPresentationCredentialCard.tsx | 10 +- .../ItwPresentationDetailsFooter.tsx | 28 +- ...PresentationCredentialAttachmentScreen.tsx | 4 + .../ItwPresentationCredentialDetailScreen.tsx | 41 +- .../components/WalletCategoryFilterTabs.tsx | 12 +- .../components/WalletEmptyScreenContent.tsx | 10 +- .../newWallet/screens/WalletHomeScreen.tsx | 10 +- ts/mixpanelConfig/profileProperties.ts | 38 +- ts/mixpanelConfig/superProperties.ts | 52 +- 26 files changed, 818 insertions(+), 342 deletions(-) diff --git a/ts/features/itwallet/analytics/enum.ts b/ts/features/itwallet/analytics/enum.ts index de9f6880a11..36c692710a6 100644 --- a/ts/features/itwallet/analytics/enum.ts +++ b/ts/features/itwallet/analytics/enum.ts @@ -10,13 +10,17 @@ export enum ITW_SCREENVIEW_EVENTS { ITW_CREDENTIAL_PREVIEW = "ITW_CREDENTIAL_PREVIEW", WALLET = "WALLET", WALLET_ADD_LIST_ITEM = "WALLET_ADD_LIST_ITEM", - ITW_DATA_SHARE = "ITW_DATA_SHARE" + ITW_DATA_SHARE = "ITW_DATA_SHARE", + ITW_CREDENTIAL_DETAIL = "ITW_CREDENTIAL_DETAIL", + ITW_DEFERRED_ISSUING = "ITW_DEFERRED_ISSUING", + "ITW_CREDENTIAL_FAC-SIMILE" = "ITW_CREDENTIAL_FAC-SIMILE" } export enum ITW_ACTIONS_EVENTS { CLOSE_BANNER = "CLOSE_BANNER", TAP_BANNER = "TAP_BANNER", ITW_TOS = "ITW_TOS", + ITW_TOS_ACCEPTED = "ITW_TOS_ACCEPTED", ITW_ID_START = "ITW_ID_START", ITW_ID_METHOD_SELECTED = "ITW_ID_METHOD_SELECTED", ITW_SPID_IDP_SELECTED = "ITW_SPID_IDP_SELECTED", @@ -30,7 +34,18 @@ export enum ITW_ACTIONS_EVENTS { WALLET_ADD_START = "WALLET_ADD_START", ITW_KO_STATE_ACTION_SELECTED = "ITW_KO_STATE_ACTION_SELECTED", ITW_DATA_SHARE_ACCEPTED = "ITW_DATA_SHARE_ACCEPTED", - WALLET_ADD = "WALLET_ADD" + WALLET_ADD = "WALLET_ADD", + ITW_CREDENTIAL_DELETE = "ITW_CREDENTIAL_DELETE", + WALLET_CATEGORY_FILTER = "WALLET_CATEGORY_FILTER", + ITW_CREDENTIAL_SHOW_BACK = "ITW_CREDENTIAL_SHOW_BACK", + ITW_CREDENTIAL_SHOW_ISSUER = "ITW_CREDENTIAL_SHOW_ISSUER", + ITW_CREDENTIAL_SHOW_AUTH_SOURCE = "ITW_CREDENTIAL_SHOW_AUTH_SOURCE", + ITW_CREDENTIAL_SUPPORT = "ITW_CREDENTIAL_SUPPORT", + "ITW_CREDENTIAL_SHOW_FAC-SIMILE" = "ITW_CREDENTIAL_SHOW_FAC-SIMILE", + ITW_CREDENTIAL_SHOW_TRUSTMARK = "ITW_CREDENTIAL_SHOW_TRUSTMARK", + ITW_START_DEACTIVATION = "ITW_START_DEACTIVATION", + ITW_NEW_ID_RESET = "ITW_NEW_ID_RESET", + ITW_CREDENTIAL_RENEW_START = "ITW_CREDENTIAL_RENEW_START" } export enum ITW_ERRORS_EVENTS { @@ -43,9 +58,11 @@ export enum ITW_ERRORS_EVENTS { ITW_CIE_CARD_READING_FAILURE = "ITW_CIE_CARD_READING_FAILURE", ITW_ADD_CREDENTIAL_TIMEOUT = "ITW_ADD_CREDENTIAL_TIMEOUT", ITW_ADD_CREDENTIAL_FAILURE = "ITW_ADD_CREDENTIAL_FAILURE", + ITW_ADD_CREDENTIAL_NOT_ENTITLED_FAILURE = "ITW_ADD_CREDENTIAL_NOT_ENTITLED_FAILURE", ITW_ID_NOT_MATCH = "ITW_ID_NOT_MATCH", ITW_ID_REQUEST_TIMEOUT = "ITW_ID_REQUEST_TIMEOUT", ITW_ID_REQUEST_FAILURE = "ITW_ID_REQUEST_FAILURE", + ITW_ID_REQUEST_UNEXPECTED_FAILURE = "ITW_ID_REQUEST_UNEXPECTED_FAILURE", ITW_LOGIN_ID_NOT_MATCH = "ITW_LOGIN_ID_NOT_MATCH", ITW_ALREADY_HAS_CREDENTIAL = "ITW_ALREADY_HAS_CREDENTIAL" } @@ -61,5 +78,6 @@ export enum ITW_TECH_EVENTS { } export enum ITW_CONFIRM_EVENTS { - ITW_UX_SUCCESS = "ITW_UX_SUCCESS" + ITW_UX_SUCCESS = "ITW_UX_SUCCESS", + ITW_DEACTIVATED = "ITW_DEACTIVATED" } diff --git a/ts/features/itwallet/analytics/index.ts b/ts/features/itwallet/analytics/index.ts index 480bf6d6aa7..06e6910363a 100644 --- a/ts/features/itwallet/analytics/index.ts +++ b/ts/features/itwallet/analytics/index.ts @@ -1,8 +1,10 @@ import { mixpanelTrack } from "../../../mixpanel"; import { updateMixpanelProfileProperties } from "../../../mixpanelConfig/profileProperties"; +import { updateMixpanelSuperProperties } from "../../../mixpanelConfig/superProperties"; import { GlobalState } from "../../../store/reducers/types"; import { buildEventProperties } from "../../../utils/analytics"; import { IdentificationContext } from "../machine/eid/context"; +import { IssuanceFailure } from "../machine/eid/failure"; import { ITW_ACTIONS_EVENTS, ITW_CONFIRM_EVENTS, @@ -13,28 +15,52 @@ import { } from "./enum"; export type KoState = { - reason: string; + reason: unknown; cta_category: "custom_1" | "custom_2"; cta_id: string; }; -const mixPanelCredentials = ["ITW_ID", "ITW_PG", "ITW_CED", "ITW_TS"] as const; +/** + * This is the list of credentials that are tracked in MixPanel + * ITW_ID_V2: PersonIdentificationData + * ITW_PG_V2: MDL + * ITW_CED_V2: EuropeanDisabilityCard + * ITW_TS_V2: EuropeanHealthInsuranceCard + */ +const mixPanelCredentials = [ + "ITW_ID_V2", + "ITW_PG_V2", + "ITW_CED_V2", + "ITW_TS_V2" +] as const; type MixPanelCredential = (typeof mixPanelCredentials)[number]; +type TrackCredentialDetail = { + credential: MixPanelCredential; // MixPanelCredential + credential_status: string; // ItwPg +}; + export type OtherMixPanelCredential = "welfare" | "payment_method" | "CGN"; type NewCredential = MixPanelCredential | OtherMixPanelCredential; +/** + * This map is used to map the credential type to the MixPanel credential + * ITW_ID_V2: PersonIdentificationData + * ITW_PG_V2: MDL + * ITW_CED_V2: EuropeanDisabilityCard + * ITW_TS_V2: EuropeanHealthInsuranceCard + */ export const CREDENTIALS_MAP: Record = { - PersonIdentificationData: "ITW_ID", - MDL: "ITW_PG", - EuropeanDisabilityCard: "ITW_CED", - EuropeanHealthInsuranceCard: "ITW_TS" + PersonIdentificationData: "ITW_ID_V2", + MDL: "ITW_PG_V2", + EuropeanDisabilityCard: "ITW_CED_V2", + EuropeanHealthInsuranceCard: "ITW_TS_V2" }; type BackToWallet = { exit_page: string; - credential: Extract; + credential: Extract; }; type ItwExit = { @@ -48,6 +74,17 @@ type AddCredentialFailure = { type: string; }; +type IdRequestFailure = { + ITW_ID_method: ItwIdMethod; + reason: unknown; + type: string; +}; + +type IdUnexpectedFailure = { + reason: unknown; + type: string; +}; + type ItwIdMethod = IdentificationContext["mode"]; // PROPERTIES TYPES @@ -72,94 +109,124 @@ export type ItwPg = "not_available" | "valid" | "not_valid" | "expiring"; export type ItwTs = "not_available" | "valid" | "not_valid" | "expiring"; export type ItwCed = "not_available" | "valid" | "not_valid" | "expiring"; -export const trackCredentialPreview = (credential: MixPanelCredential) => { +// #region SCREEN VIEW EVENTS +export const trackWalletDataShare = (credential: MixPanelCredential) => { void mixpanelTrack( - ITW_SCREENVIEW_EVENTS.ITW_CREDENTIAL_PREVIEW, + ITW_SCREENVIEW_EVENTS.ITW_DATA_SHARE, buildEventProperties("UX", "screen_view", { credential }) ); }; -export const trackSaveCredentialToWallet = (currentCredential: string) => { - const credential = CREDENTIALS_MAP[currentCredential]; - if (credential) { - void mixpanelTrack( - ITW_ACTIONS_EVENTS.ITW_UX_CONVERSION, - buildEventProperties("UX", "action", { credential }) - ); - } +export const trackOpenWalletScreen = () => { + void mixpanelTrack( + ITW_SCREENVIEW_EVENTS.WALLET, + buildEventProperties("UX", "screen_view") + ); }; -export const trackSaveCredentialSuccess = (credential: MixPanelCredential) => { +export const trackShowCredentialsList = () => { void mixpanelTrack( - ITW_CONFIRM_EVENTS.ITW_UX_SUCCESS, - buildEventProperties("UX", "confirm", { credential }) + ITW_SCREENVIEW_EVENTS.WALLET_ADD_LIST_ITEM, + buildEventProperties("UX", "screen_view") ); }; -export const trackAddFirstCredential = () => { +export const trackCredentialPreview = (credential: MixPanelCredential) => { void mixpanelTrack( - ITW_ACTIONS_EVENTS.ITW_ADD_FIRST_CREDENTIAL, - buildEventProperties("UX", "action") + ITW_SCREENVIEW_EVENTS.ITW_CREDENTIAL_PREVIEW, + buildEventProperties("UX", "screen_view", { credential }) ); }; -export const trackItwExit = ({ exit_page, credential }: ItwExit) => { +export const trackCredentialDetail = ( + credentialDetails: TrackCredentialDetail +) => { void mixpanelTrack( - ITW_EXIT_EVENTS.ITW_USER_EXIT, - buildEventProperties("UX", "exit", { - exit_page, - credential - }) + ITW_SCREENVIEW_EVENTS.ITW_CREDENTIAL_DETAIL, + buildEventProperties("UX", "screen_view", credentialDetails) ); }; -export const trackBackToWallet = ({ exit_page, credential }: BackToWallet) => { +export function trackITWalletBannerVisualized( + properties: TrackITWalletBannerClosureProperties +) { void mixpanelTrack( - ITW_EXIT_EVENTS.ITW_BACK_TO_WALLET, - buildEventProperties("UX", "exit", { - exit_page, - credential - }) + ITW_SCREENVIEW_EVENTS.BANNER, + buildEventProperties("UX", "screen_view", properties) ); -}; +} -export const trackOpenWalletScreen = () => { +export function trackItWalletIntroScreen() { void mixpanelTrack( - ITW_SCREENVIEW_EVENTS.WALLET, + ITW_SCREENVIEW_EVENTS.ITW_INTRO, buildEventProperties("UX", "screen_view") ); -}; +} -export const trackShowCredentialsList = () => { +export function trackItWalletIDMethod() { void mixpanelTrack( - ITW_SCREENVIEW_EVENTS.WALLET_ADD_LIST_ITEM, + ITW_SCREENVIEW_EVENTS.ITW_ID_METHOD, buildEventProperties("UX", "screen_view") ); -}; +} -export const trackStartAddNewCredential = (wallet_item: NewCredential) => { +export function trackItWalletSpidIDPSelection() { void mixpanelTrack( - ITW_ACTIONS_EVENTS.WALLET_ADD_START, - buildEventProperties("UX", "action", { - wallet_item, - add_entry_point: "wallet_home", - payment_home_status: "not_set" - }) + ITW_SCREENVIEW_EVENTS.ITW_SPID_IDP_SELECTION, + buildEventProperties("UX", "screen_view") ); -}; +} -export const trackWalletCreationFailed = (params: KoState) => { +export function trackItWalletCiePinEnter() { void mixpanelTrack( - ITW_ACTIONS_EVENTS.ITW_KO_STATE_ACTION_SELECTED, - buildEventProperties("UX", "action", { ...params }) + ITW_SCREENVIEW_EVENTS.ITW_CIE_PIN_ENTER, + buildEventProperties("UX", "screen_view") ); -}; +} -export const trackWalletDataShare = (credential: MixPanelCredential) => { +export function trackItWalletCieNfcActivation() { void mixpanelTrack( - ITW_SCREENVIEW_EVENTS.ITW_DATA_SHARE, + ITW_SCREENVIEW_EVENTS.ITW_CIE_NFC_ACTIVATION, + buildEventProperties("UX", "screen_view") + ); +} + +export function trackItWalletCieCardReading() { + void mixpanelTrack( + ITW_SCREENVIEW_EVENTS.ITW_CIE_CARD_READING, + buildEventProperties("UX", "screen_view") + ); +} + +export function trackItWalletCieCardReadingSuccess() { + void mixpanelTrack( + ITW_SCREENVIEW_EVENTS.ITW_CARD_READING_SUCCESS, + buildEventProperties("UX", "screen_view") + ); +} + +export function trackItWalletDeferredIssuing(credential: MixPanelCredential) { + void mixpanelTrack( + ITW_SCREENVIEW_EVENTS.ITW_DEFERRED_ISSUING, buildEventProperties("UX", "screen_view", { credential }) ); +} + +export function trackWalletCredentialFAC_SIMILE() { + void mixpanelTrack( + ITW_SCREENVIEW_EVENTS["ITW_CREDENTIAL_FAC-SIMILE"], + buildEventProperties("UX", "screen_view", { credential: "ITW_TS_V2" }) + ); +} +// #endregion SCREEN VIEW EVENTS + +// #region ACTIONS + +export const trackItwCredentialDelete = (credential: MixPanelCredential) => { + void mixpanelTrack( + ITW_ACTIONS_EVENTS.ITW_CREDENTIAL_DELETE, + buildEventProperties("UX", "action", { credential }) + ); }; export const trackWalletDataShareAccepted = ( @@ -178,252 +245,216 @@ export const trackOpenItwTos = () => { ); }; -export const trackAddCredentialTimeout = ({ - credential, - reason, - type -}: AddCredentialFailure) => { +export const trackOpenItwTosAccepted = () => { void mixpanelTrack( - ITW_ERRORS_EVENTS.ITW_ADD_CREDENTIAL_TIMEOUT, - buildEventProperties("KO", "error", { credential, reason, type }) + ITW_ACTIONS_EVENTS.ITW_TOS_ACCEPTED, + buildEventProperties("UX", "action") ); }; -export const trackAddCredentialFailure = ({ - credential, - reason -}: AddCredentialFailure) => { +export const trackStartAddNewCredential = (wallet_item: NewCredential) => { void mixpanelTrack( - ITW_ERRORS_EVENTS.ITW_ADD_CREDENTIAL_FAILURE, - buildEventProperties("KO", "error", { credential, reason }) + ITW_ACTIONS_EVENTS.WALLET_ADD_START, + buildEventProperties("UX", "action", { + wallet_item, + add_entry_point: "wallet_home", + payment_home_status: "not_set" + }) ); }; -export const trackItwRequest = (ITW_ID_method?: ItwIdMethod) => { - if (ITW_ID_method) { - void mixpanelTrack( - ITW_TECH_EVENTS.ITW_ID_REQUEST, - buildEventProperties("TECH", undefined, { ITW_ID_method }) - ); - } -}; - -export const trackItwRequestSuccess = (ITW_ID_method?: ItwIdMethod) => { - if (ITW_ID_method) { - void mixpanelTrack( - ITW_TECH_EVENTS.ITW_ID_REQUEST_SUCCESS, - buildEventProperties("TECH", undefined, { ITW_ID_method, ITW_ID: "L2" }) - ); - } -}; - -export const trackIdNotMatch = (ITW_ID_method: ItwIdMethod) => { +export const trackWalletCreationFailed = (params: KoState) => { void mixpanelTrack( - ITW_ERRORS_EVENTS.ITW_ID_NOT_MATCH, - buildEventProperties("KO", "error", { ITW_ID_method }) + ITW_ACTIONS_EVENTS.ITW_KO_STATE_ACTION_SELECTED, + buildEventProperties("UX", "action", { ...params }) ); }; -export const trackItwIdRequestTimeout = (ITW_ID_method?: ItwIdMethod) => { - if (ITW_ID_method) { - void mixpanelTrack( - ITW_ERRORS_EVENTS.ITW_ID_REQUEST_TIMEOUT, - buildEventProperties("KO", "error", { ITW_ID_method }) - ); - } +export const trackAddFirstCredential = () => { + void mixpanelTrack( + ITW_ACTIONS_EVENTS.ITW_ADD_FIRST_CREDENTIAL, + buildEventProperties("UX", "action") + ); }; -export const trackItwIdRequestFailure = (ITW_ID_method?: ItwIdMethod) => { - if (ITW_ID_method) { +export const trackSaveCredentialToWallet = (currentCredential: string) => { + const credential = CREDENTIALS_MAP[currentCredential]; + if (credential) { void mixpanelTrack( - ITW_ERRORS_EVENTS.ITW_ID_REQUEST_FAILURE, - buildEventProperties("KO", "error", { ITW_ID_method }) + ITW_ACTIONS_EVENTS.ITW_UX_CONVERSION, + buildEventProperties("UX", "action", { credential }) ); } }; -export const trackItwUnsupportedDevice = () => { - void mixpanelTrack( - ITW_ERRORS_EVENTS.ITW_DEVICE_NOT_SUPPORTED, - buildEventProperties("KO", "error") - ); -}; - -export const trackItwIdNotMatch = () => { +export function trackItWalletActivationStart() { void mixpanelTrack( - ITW_ERRORS_EVENTS.ITW_LOGIN_ID_NOT_MATCH, - buildEventProperties("KO", "error") - ); -}; - -export const trackCredentialPropertiesSuccess = async ( - credential: MixPanelCredential, - state: GlobalState -) => { - await updateMixpanelProfileProperties(state, { - property: credential, - value: "valid" - }); -}; - -export const trackAllCredentialProfileProperties = async ( - state: GlobalState -) => { - mixPanelCredentials.forEach( - async credential => - await updateMixpanelProfileProperties(state, { - property: credential, - value: "valid" - }) + ITW_ACTIONS_EVENTS.ITW_ID_START, + buildEventProperties("UX", "action") ); -}; +} -export const trackItwHasAlreadyCredential = () => { - // TODO [SIW-1438] -> add status and credential +export function trackItWalletIDMethodSelected( + properties: TrackITWalletIDMethodSelected +) { void mixpanelTrack( - ITW_ERRORS_EVENTS.ITW_ALREADY_HAS_CREDENTIAL, - buildEventProperties("KO", "error") + ITW_ACTIONS_EVENTS.ITW_ID_METHOD_SELECTED, + buildEventProperties("UX", "action", properties) ); -}; +} -export function trackItWalletBannerClosure( - properties: TrackITWalletBannerClosureProperties +export function trackItWalletSpidIDPSelected( + properties: TrackITWalletSpidIDPSelected ) { void mixpanelTrack( - ITW_ACTIONS_EVENTS.CLOSE_BANNER, + ITW_ACTIONS_EVENTS.ITW_SPID_IDP_SELECTED, buildEventProperties("UX", "action", properties) ); } -export function trackItWalletBannerTap( - properties: TrackITWalletBannerClosureProperties -) { +export function trackItWalletCiePinInfo() { void mixpanelTrack( - ITW_ACTIONS_EVENTS.TAP_BANNER, - buildEventProperties("UX", "action", properties) + ITW_ACTIONS_EVENTS.ITW_CIE_PIN_INFO, + buildEventProperties("UX", "action") ); } -// SCREEN VIEW EVENTS -export function trackITWalletBannerVisualized( - properties: TrackITWalletBannerClosureProperties -) { +export function trackItWalletCiePinForgotten() { void mixpanelTrack( - ITW_SCREENVIEW_EVENTS.BANNER, - buildEventProperties("UX", "screen_view", properties) + ITW_ACTIONS_EVENTS.ITW_CIE_PIN_FORGOTTEN, + buildEventProperties("UX", "action") ); } -export function trackItWalletIntroScreen() { +export function trackItWalletCiePukForgotten() { void mixpanelTrack( - ITW_SCREENVIEW_EVENTS.ITW_INTRO, - buildEventProperties("UX", "screen_view") + ITW_ACTIONS_EVENTS.ITW_CIE_PUK_FORGOTTEN, + buildEventProperties("UX", "action") ); } -export function trackItWalletIDMethod() { +export function trackItWalletCieNfcGoToSettings() { void mixpanelTrack( - ITW_SCREENVIEW_EVENTS.ITW_ID_METHOD, - buildEventProperties("UX", "screen_view") + ITW_ACTIONS_EVENTS.ITW_CIE_NFC_GO_TO_SETTINGS, + buildEventProperties("UX", "action") ); } -export function trackItWalletSpidIDPSelection() { +export function trackItWalletCieRetryPin() { void mixpanelTrack( - ITW_SCREENVIEW_EVENTS.ITW_SPID_IDP_SELECTION, - buildEventProperties("UX", "screen_view") + ITW_ACTIONS_EVENTS.ITW_CIE_RETRY_PIN, + buildEventProperties("UX", "action") ); } -export function trackItWalletCiePinEnter() { +export function trackWalletAdd() { void mixpanelTrack( - ITW_SCREENVIEW_EVENTS.ITW_CIE_PIN_ENTER, - buildEventProperties("UX", "screen_view") + ITW_ACTIONS_EVENTS.WALLET_ADD, + buildEventProperties("UX", "action") ); } -export function trackItWalletCieNfcActivation() { +export function trackItWalletBannerClosure( + properties: TrackITWalletBannerClosureProperties +) { void mixpanelTrack( - ITW_SCREENVIEW_EVENTS.ITW_CIE_NFC_ACTIVATION, - buildEventProperties("UX", "screen_view") + ITW_ACTIONS_EVENTS.CLOSE_BANNER, + buildEventProperties("UX", "action", properties) ); } -export function trackItWalletCieCardReading() { +export function trackItWalletBannerTap( + properties: TrackITWalletBannerClosureProperties +) { void mixpanelTrack( - ITW_SCREENVIEW_EVENTS.ITW_CIE_CARD_READING, - buildEventProperties("UX", "screen_view") + ITW_ACTIONS_EVENTS.TAP_BANNER, + buildEventProperties("UX", "action", properties) ); } -export function trackItWalletCieCardReadingSuccess() { +export function trackWalletCategoryFilter(wallet_category: string) { void mixpanelTrack( - ITW_SCREENVIEW_EVENTS.ITW_CARD_READING_SUCCESS, - buildEventProperties("UX", "screen_view") + ITW_ACTIONS_EVENTS.WALLET_CATEGORY_FILTER, + buildEventProperties("UX", "action", { wallet_category }) ); } -export function trackItWalletActivationStart() { +export function trackWalletShowBack(credential: MixPanelCredential) { void mixpanelTrack( - ITW_ACTIONS_EVENTS.ITW_ID_START, - buildEventProperties("UX", "action") + ITW_ACTIONS_EVENTS.ITW_CREDENTIAL_SHOW_BACK, + buildEventProperties("UX", "action", { credential }) ); } -export function trackItWalletIDMethodSelected( - properties: TrackITWalletIDMethodSelected +export function trackWalletCredentialShowIssuer( + credential: MixPanelCredential ) { void mixpanelTrack( - ITW_ACTIONS_EVENTS.ITW_ID_METHOD_SELECTED, - buildEventProperties("UX", "action", properties) + ITW_ACTIONS_EVENTS.ITW_CREDENTIAL_SHOW_ISSUER, + buildEventProperties("UX", "action", { credential }) ); } -export function trackItWalletSpidIDPSelected( - properties: TrackITWalletSpidIDPSelected +// TODO: To be added on the data origin tooltip +export function trackWalletCredentialShowAuthSource( + credential: MixPanelCredential ) { void mixpanelTrack( - ITW_ACTIONS_EVENTS.ITW_SPID_IDP_SELECTED, - buildEventProperties("UX", "action", properties) + ITW_ACTIONS_EVENTS.ITW_CREDENTIAL_SHOW_AUTH_SOURCE, + buildEventProperties("UX", "action", { credential }) + ); +} +export function trackWalletCredentialSupport(credential: MixPanelCredential) { + void mixpanelTrack( + ITW_ACTIONS_EVENTS.ITW_CREDENTIAL_SUPPORT, + buildEventProperties("UX", "action", { credential }) ); } -export function trackItWalletCiePinInfo() { +export function trackWalletCredentialShowFAC_SIMILE() { void mixpanelTrack( - ITW_ACTIONS_EVENTS.ITW_CIE_PIN_INFO, - buildEventProperties("UX", "action") + ITW_ACTIONS_EVENTS["ITW_CREDENTIAL_SHOW_FAC-SIMILE"], + buildEventProperties("UX", "action", { credential: "ITW_TS_V2" }) ); } -export function trackItWalletCiePinForgotten() { +// ITW_CREDENTIAL_SHOW_TRUSTMARK +export function trackWalletCredentialShowTrustmark( + credential: MixPanelCredential +) { void mixpanelTrack( - ITW_ACTIONS_EVENTS.ITW_CIE_PIN_FORGOTTEN, - buildEventProperties("UX", "action") + ITW_ACTIONS_EVENTS.ITW_CREDENTIAL_SHOW_TRUSTMARK, + buildEventProperties("UX", "action", { credential }) ); } -export function trackItWalletCiePukForgotten() { +export function trackWalletStartDeactivation() { void mixpanelTrack( - ITW_ACTIONS_EVENTS.ITW_CIE_PUK_FORGOTTEN, + ITW_ACTIONS_EVENTS.ITW_START_DEACTIVATION, buildEventProperties("UX", "action") ); } -export function trackItWalletCieNfcGoToSettings() { +export function trackWalletNewIdReset(state: GlobalState) { + updatePropertiesWalletRevoked(state); void mixpanelTrack( - ITW_ACTIONS_EVENTS.ITW_CIE_NFC_GO_TO_SETTINGS, + ITW_ACTIONS_EVENTS.ITW_NEW_ID_RESET, buildEventProperties("UX", "action") ); } -export function trackItWalletCieRetryPin() { +export function trackWalletCredentialRenewStart( + credential: MixPanelCredential +) { void mixpanelTrack( - ITW_ACTIONS_EVENTS.ITW_CIE_RETRY_PIN, - buildEventProperties("UX", "action") + ITW_ACTIONS_EVENTS.ITW_CREDENTIAL_RENEW_START, + buildEventProperties("UX", "action", { credential }) ); } -// ERROR EVENTS +// #endregion ACTIONS + +// #region ERRORS export function trackItWalletErrorCardReading() { void mixpanelTrack( @@ -469,9 +500,246 @@ export function trackItWalletCieCardReadingFailure( ); } -export function trackWalletAdd() { +export const trackIdNotMatch = (ITW_ID_method: ItwIdMethod) => { void mixpanelTrack( - ITW_ACTIONS_EVENTS.WALLET_ADD, - buildEventProperties("UX", "action") + ITW_ERRORS_EVENTS.ITW_ID_NOT_MATCH, + buildEventProperties("KO", "error", { ITW_ID_method }) ); -} +}; + +// TODO: Track IPZS timeout on eID flow +export const trackItwIdRequestTimeout = (ITW_ID_method?: ItwIdMethod) => { + if (ITW_ID_method) { + void mixpanelTrack( + ITW_ERRORS_EVENTS.ITW_ID_REQUEST_TIMEOUT, + buildEventProperties("KO", "error", { ITW_ID_method }) + ); + } +}; + +export const trackItwIdRequestFailure = (properties: IdRequestFailure) => { + if (properties.ITW_ID_method) { + void mixpanelTrack( + ITW_ERRORS_EVENTS.ITW_ID_REQUEST_FAILURE, + buildEventProperties("KO", "error", properties) + ); + } +}; + +export const trackItwUnsupportedDevice = (properties: IssuanceFailure) => { + void mixpanelTrack( + ITW_ERRORS_EVENTS.ITW_DEVICE_NOT_SUPPORTED, + buildEventProperties("KO", "error", { reason: properties.reason }) + ); +}; + +export const trackItwIdNotMatch = () => { + void mixpanelTrack( + ITW_ERRORS_EVENTS.ITW_LOGIN_ID_NOT_MATCH, + buildEventProperties("KO", "error") + ); +}; + +export const trackItwHasAlreadyCredential = ( + properties: TrackCredentialDetail +) => { + void mixpanelTrack( + ITW_ERRORS_EVENTS.ITW_ALREADY_HAS_CREDENTIAL, + buildEventProperties("KO", "error", properties) + ); +}; + +export const trackAddCredentialTimeout = ({ + credential, + reason, + type +}: AddCredentialFailure) => { + void mixpanelTrack( + ITW_ERRORS_EVENTS.ITW_ADD_CREDENTIAL_TIMEOUT, + buildEventProperties("KO", "error", { credential, reason, type }) + ); +}; + +export const trackAddCredentialFailure = ({ + credential, + reason, + type +}: AddCredentialFailure) => { + void mixpanelTrack( + ITW_ERRORS_EVENTS.ITW_ADD_CREDENTIAL_FAILURE, + buildEventProperties("KO", "error", { credential, reason, type }) + ); +}; + +export const trackCredentialNotEntitledFailure = ({ + credential, + reason, + type +}: AddCredentialFailure) => { + void mixpanelTrack( + ITW_ERRORS_EVENTS.ITW_ADD_CREDENTIAL_NOT_ENTITLED_FAILURE, + buildEventProperties("KO", "error", { credential, reason, type }) + ); +}; + +export const trackItwIdRequestUnexpected = ({ + reason, + type +}: IdUnexpectedFailure) => { + void mixpanelTrack( + ITW_ERRORS_EVENTS.ITW_ID_REQUEST_UNEXPECTED_FAILURE, + buildEventProperties("KO", "error", { reason, type }) + ); +}; + +// #endregion ERRORS + +// #region PROFILE PROPERTIES + +export const trackCredentialDeleteProperties = async ( + credential: MixPanelCredential, + state: GlobalState +) => { + await updateMixpanelProfileProperties(state, { + property: credential, + value: "not_available" + }); + await updateMixpanelSuperProperties(state, { + property: credential, + value: "not_available" + }); +}; + +export const trackAllCredentialProfileAndSuperProperties = async ( + state: GlobalState +) => { + const promises = mixPanelCredentials.map(async credential => { + await Promise.all([ + updateMixpanelProfileProperties(state, { + property: credential, + value: "valid" + }), + updateMixpanelSuperProperties(state, { + property: credential, + value: "valid" + }) + ]); + }); + await Promise.all(promises); +}; + +// #endregion PROFILE PROPERTIES + +// #region CONFIRM + +export const trackSaveCredentialSuccess = (credential: MixPanelCredential) => { + void mixpanelTrack( + ITW_CONFIRM_EVENTS.ITW_UX_SUCCESS, + buildEventProperties("UX", "confirm", { credential }) + ); +}; + +export const trackItwDeactivated = (state: GlobalState) => { + void mixpanelTrack( + ITW_CONFIRM_EVENTS.ITW_DEACTIVATED, + buildEventProperties("UX", "confirm") + ); + updatePropertiesWalletRevoked(state); +}; + +// #endregion CONFIRM + +// #region EXIT + +export const trackItwExit = ({ exit_page, credential }: ItwExit) => { + void mixpanelTrack( + ITW_EXIT_EVENTS.ITW_USER_EXIT, + buildEventProperties("UX", "exit", { + exit_page, + credential + }) + ); +}; + +export const trackBackToWallet = ({ exit_page, credential }: BackToWallet) => { + void mixpanelTrack( + ITW_EXIT_EVENTS.ITW_BACK_TO_WALLET, + buildEventProperties("UX", "exit", { + exit_page, + credential + }) + ); +}; +// #endregion EXIT + +// #region TECH + +export const trackItwRequest = (ITW_ID_method?: ItwIdMethod) => { + if (ITW_ID_method) { + void mixpanelTrack( + ITW_TECH_EVENTS.ITW_ID_REQUEST, + buildEventProperties("TECH", undefined, { ITW_ID_method }) + ); + } +}; + +export const trackItwRequestSuccess = (ITW_ID_method?: ItwIdMethod) => { + if (ITW_ID_method) { + void mixpanelTrack( + ITW_TECH_EVENTS.ITW_ID_REQUEST_SUCCESS, + buildEventProperties("TECH", undefined, { + ITW_ID_method, + ITW_ID_V2: "L2" + }) + ); + } +}; +// #endregion TECH + +// #region PROFILE AND SUPER PROPERTIES UPDATE + +export const updateITWStatusAndIDProperties = (state: GlobalState) => { + void updateMixpanelProfileProperties(state, { + property: "ITW_STATUS_V2", + value: "L2" + }); + void updateMixpanelSuperProperties(state, { + property: "ITW_STATUS_V2", + value: "L2" + }); + void updateMixpanelProfileProperties(state, { + property: "ITW_ID_V2", + value: "valid" + }); + void updateMixpanelSuperProperties(state, { + property: "ITW_ID_V2", + value: "valid" + }); +}; + +/** + * This function is used to set all to not_available / not_active when wallet is revoked or when the wallet section is visualized in empty state + * @param state + */ +export const updatePropertiesWalletRevoked = (state: GlobalState) => { + mixPanelCredentials.forEach(property => { + void updateMixpanelProfileProperties(state, { + property, + value: "not_available" + }); + void updateMixpanelSuperProperties(state, { + property, + value: "not_available" + }); + }); + void updateMixpanelProfileProperties(state, { + property: "ITW_STATUS_V2", + value: "not_active" + }); + void updateMixpanelSuperProperties(state, { + property: "ITW_STATUS_V2", + value: "not_active" + }); +}; + +// #endregion PROFILE AND SUPER PROPERTIES UPDATE diff --git a/ts/features/itwallet/common/components/ItwDiscoveryBanner.tsx b/ts/features/itwallet/common/components/ItwDiscoveryBanner.tsx index 9692de932a5..0b12d58865c 100644 --- a/ts/features/itwallet/common/components/ItwDiscoveryBanner.tsx +++ b/ts/features/itwallet/common/components/ItwDiscoveryBanner.tsx @@ -1,7 +1,7 @@ import { Banner, IOVisualCostants } from "@pagopa/io-app-design-system"; import React, { ReactElement } from "react"; import { StyleSheet, View } from "react-native"; -import { useRoute } from "@react-navigation/native"; +import { useFocusEffect, useRoute } from "@react-navigation/native"; import I18n from "../../../../i18n"; import { useIONavigation } from "../../../../navigation/params/AppParamsList"; import { useIOSelector } from "../../../../store/hooks"; @@ -9,13 +9,11 @@ import { isItwTrialActiveSelector } from "../../../trialSystem/store/reducers"; import { ITW_ROUTES } from "../../navigation/routes"; import { itwLifecycleIsValidSelector } from "../../lifecycle/store/selectors"; import { isItwEnabledSelector } from "../../../../store/reducers/backendStatus"; -import { useOnFirstRender } from "../../../../utils/hooks/useOnFirstRender"; import { trackItWalletBannerTap, trackItWalletBannerClosure, trackITWalletBannerVisualized } from "../../analytics"; -import { itwTrialId } from "../../../../config"; type ItwDiscoveryBannerProps = { withTitle?: boolean; @@ -50,14 +48,14 @@ export const ItwDiscoveryBanner = ({ const trackBannerProperties = React.useMemo( () => ({ - banner_id: itwTrialId, + banner_id: "itwDiscoveryBannerTestID", banner_page: route.name, banner_landing: "ITW_INTRO" }), [route.name] ); - useOnFirstRender(() => { + useFocusEffect(() => { if (!shouldBeHidden) { trackITWalletBannerVisualized(trackBannerProperties); } @@ -81,6 +79,7 @@ export const ItwDiscoveryBanner = ({ trackItWalletBannerClosure(trackBannerProperties); setVisible(false); }; + // trailID return ( diff --git a/ts/features/itwallet/common/components/ItwEidInfoBottomSheetContent.tsx b/ts/features/itwallet/common/components/ItwEidInfoBottomSheetContent.tsx index 0284a412e0d..db6b709a5e4 100644 --- a/ts/features/itwallet/common/components/ItwEidInfoBottomSheetContent.tsx +++ b/ts/features/itwallet/common/components/ItwEidInfoBottomSheetContent.tsx @@ -1,14 +1,14 @@ import React from "react"; import { View } from "react-native"; import { + Alert, + ButtonSolid, Divider, + H4, HStack, Icon, IOStyles, - VStack, - H4, - Alert, - ButtonSolid + VStack } from "@pagopa/io-app-design-system"; import * as O from "fp-ts/lib/Option"; import { constNull, pipe } from "fp-ts/lib/function"; @@ -21,6 +21,7 @@ import { useIONavigation } from "../../../../navigation/params/AppParamsList"; import { parseClaims, WellKnownClaim } from "../utils/itwClaimsUtils"; import { ITW_ROUTES } from "../../navigation/routes"; import { StoredCredential } from "../utils/itwTypesUtils"; +import { trackWalletStartDeactivation } from "../../analytics"; import { ItwCredentialClaim } from "./ItwCredentialClaim"; export const ItwEidInfoBottomSheetTitle = () => ( @@ -44,10 +45,12 @@ const ItwEidInfoBottomSheetContent = ({ navigation }: Props) => { exclude: [WellKnownClaim.unique_id, WellKnownClaim.content] }); - const navigateToWalletRevocationScreen = () => + const navigateToWalletRevocationScreen = () => { + trackWalletStartDeactivation(); navigation.navigate(ITW_ROUTES.MAIN, { screen: ITW_ROUTES.WALLET_REVOCATION_SCREEN }); + }; return ( diff --git a/ts/features/itwallet/common/components/ItwReleaserName.tsx b/ts/features/itwallet/common/components/ItwReleaserName.tsx index 2b5ec66b577..1ddb3c064da 100644 --- a/ts/features/itwallet/common/components/ItwReleaserName.tsx +++ b/ts/features/itwallet/common/components/ItwReleaserName.tsx @@ -1,8 +1,12 @@ import { ListItemInfo } from "@pagopa/io-app-design-system"; -import React, { useMemo } from "react"; +import React, { useCallback, useMemo } from "react"; import I18n from "../../../../i18n"; import { useItwInfoBottomSheet } from "../hooks/useItwInfoBottomSheet"; import { StoredCredential } from "../utils/itwTypesUtils"; +import { + CREDENTIALS_MAP, + trackWalletCredentialShowIssuer +} from "../../analytics"; type Props = { credential: StoredCredential; @@ -38,6 +42,12 @@ export const ItwReleaserName = ({ credential, isPreview }: Props) => { } ] }); + + const onPressWithMixpanelEvent = useCallback(() => { + trackWalletCredentialShowIssuer(CREDENTIALS_MAP[credential.credentialType]); + releasedByBottomSheet.present(); + }, [credential.credentialType, releasedByBottomSheet]); + const endElement: ListItemInfo["endElement"] = useMemo(() => { if (isPreview) { return; @@ -48,10 +58,10 @@ export const ItwReleaserName = ({ credential, isPreview }: Props) => { componentProps: { icon: "info", accessibilityLabel: "Info", - onPress: () => releasedByBottomSheet.present() + onPress: onPressWithMixpanelEvent } }; - }, [isPreview, releasedByBottomSheet]); + }, [isPreview, onPressWithMixpanelEvent]); if (!releaserName) { return null; diff --git a/ts/features/itwallet/credentials/saga/checkCredentialsStatusAttestation.ts b/ts/features/itwallet/credentials/saga/checkCredentialsStatusAttestation.ts index f63981c6a33..637e1a0c77a 100644 --- a/ts/features/itwallet/credentials/saga/checkCredentialsStatusAttestation.ts +++ b/ts/features/itwallet/credentials/saga/checkCredentialsStatusAttestation.ts @@ -12,6 +12,9 @@ import { import { ReduxSagaEffect } from "../../../../types/utils"; import { itwLifecycleIsValidSelector } from "../../lifecycle/store/selectors"; import { itwCredentialsStore } from "../store/actions"; +import { updateMixpanelProfileProperties } from "../../../../mixpanelConfig/profileProperties"; +import { updateMixpanelSuperProperties } from "../../../../mixpanelConfig/superProperties"; +import { GlobalState } from "../../../../store/reducers/types"; export function* updateCredentialStatusAttestationSaga( credential: StoredCredential @@ -46,6 +49,8 @@ export function* updateCredentialStatusAttestationSaga( * This saga is responsible to check the status attestation for each credential in the wallet. */ export function* checkCredentialsStatusAttestation() { + const state: GlobalState = yield* select(); + const isWalletValid = yield* select(itwLifecycleIsValidSelector); // Credentials can be requested only when the wallet is valid, i.e. the eID was issued @@ -57,7 +62,7 @@ export function* checkCredentialsStatusAttestation() { const credentialsToCheck = pipe( credentials, - RA.filterMap(O.filter(x => shouldRequestStatusAttestation(x))) + RA.filterMap(O.filter(shouldRequestStatusAttestation)) ); if (credentialsToCheck.length === 0) { @@ -71,4 +76,7 @@ export function* checkCredentialsStatusAttestation() { ); yield* put(itwCredentialsStore(updatedCredentials)); + + void updateMixpanelProfileProperties(state); + void updateMixpanelSuperProperties(state); } diff --git a/ts/features/itwallet/identification/screens/ItwIdentificationModeSelectionScreen.tsx b/ts/features/itwallet/identification/screens/ItwIdentificationModeSelectionScreen.tsx index 103fe69defa..549a841cc15 100644 --- a/ts/features/itwallet/identification/screens/ItwIdentificationModeSelectionScreen.tsx +++ b/ts/features/itwallet/identification/screens/ItwIdentificationModeSelectionScreen.tsx @@ -10,7 +10,7 @@ import React from "react"; import { useFocusEffect } from "@react-navigation/native"; import { IOScrollViewWithLargeHeader } from "../../../../components/ui/IOScrollViewWithLargeHeader"; import I18n from "../../../../i18n"; -import { useIOSelector, useIOStore } from "../../../../store/hooks"; +import { useIOSelector } from "../../../../store/hooks"; import { cieFlowForDevServerEnabled } from "../../../cieLogin/utils"; import { ItwEidIssuanceMachineContext } from "../../machine/provider"; import ItwMarkdown from "../../common/components/ItwMarkdown"; @@ -19,15 +19,12 @@ import { trackItWalletIDMethod, trackItWalletIDMethodSelected } from "../../analytics"; -import { updateMixpanelProfileProperties } from "../../../../mixpanelConfig/profileProperties"; export const ItwIdentificationModeSelectionScreen = () => { const machineRef = ItwEidIssuanceMachineContext.useActorRef(); const isCieSupportedPot = useIOSelector(itwIsCieSupportedSelector); - const store = useIOStore(); - const isCieSupported = React.useMemo( () => cieFlowForDevServerEnabled || pot.getOrElse(isCieSupportedPot, false), [isCieSupportedPot] @@ -95,14 +92,7 @@ export const ItwIdentificationModeSelectionScreen = () => { /> - - updateMixpanelProfileProperties(store.getState(), { - property: "ITW_HAS_READ_IPZS_POLICY", - value: true - }) - } - > + {I18n.t("features.itWallet.identification.mode.privacy")} diff --git a/ts/features/itwallet/identification/screens/cie/ItwCieCardReaderScreen.tsx b/ts/features/itwallet/identification/screens/cie/ItwCieCardReaderScreen.tsx index 3214f255de0..9d047a68af8 100644 --- a/ts/features/itwallet/identification/screens/cie/ItwCieCardReaderScreen.tsx +++ b/ts/features/itwallet/identification/screens/cie/ItwCieCardReaderScreen.tsx @@ -1,22 +1,22 @@ import { + Body, ButtonLink, ContentWrapper, H3, IOColors, IOPictograms, IOStyles, - VSpacer, - Body + VSpacer } from "@pagopa/io-app-design-system"; import { Millisecond } from "@pagopa/ts-commons/lib/units"; import * as O from "fp-ts/lib/Option"; import React, { memo, useCallback, useRef, useState } from "react"; import { + AccessibilityInfo, Platform, ScrollView, - View, StyleSheet, - AccessibilityInfo + View } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; import { useFocusEffect, useNavigation } from "@react-navigation/native"; @@ -36,15 +36,12 @@ import { ItwEidIssuanceMachineContext } from "../../../machine/provider"; import { selectCieAuthUrlOption, selectCiePin, - selectIdentification, selectIsLoading } from "../../../machine/eid/selectors"; import { ItwParamsList } from "../../../navigation/ItwParamsList"; import LoadingScreenContent from "../../../../../components/screens/LoadingScreenContent"; import { itwIdpHintTest } from "../../../../../config"; import { - trackItwIdRequestFailure, - trackItwIdRequestTimeout, trackItWalletCieCardReading, trackItWalletCieCardReadingFailure, trackItWalletCieCardReadingSuccess, @@ -163,8 +160,6 @@ export const ItwCieCardReaderScreen = () => { const machineRef = ItwEidIssuanceMachineContext.useActorRef(); const isMachineLoading = ItwEidIssuanceMachineContext.useSelector(selectIsLoading); - const identification = - ItwEidIssuanceMachineContext.useSelector(selectIdentification); const ciePin = ItwEidIssuanceMachineContext.useSelector(selectCiePin); const cieAuthUrl = ItwEidIssuanceMachineContext.useSelector( selectCieAuthUrlOption @@ -217,12 +212,13 @@ export const ItwCieCardReaderScreen = () => { const handleCieReadError = (error: Cie.CieError) => { handleAccessibilityAnnouncement(error); - // TODO Add **trackItWalletCieCardReadingFailure({ reason: "ADPU not supported" })** when ItwADPUnotsupported screen is added - switch (error.type) { case Cie.CieErrorType.WEB_VIEW_ERROR: break; case Cie.CieErrorType.NFC_ERROR: + if (error.message === "APDU not supported") { + trackItWalletCieCardReadingFailure({ reason: error.message }); + } setReadingState(ReadingState.error); break; case Cie.CieErrorType.PIN_LOCKED: @@ -239,11 +235,7 @@ export const ItwCieCardReaderScreen = () => { navigation.navigate(ITW_ROUTES.IDENTIFICATION.CIE.CIE_EXPIRED_SCREEN); break; case Cie.CieErrorType.GENERIC: - trackItwIdRequestTimeout(identification?.mode); - break; case Cie.CieErrorType.AUTHENTICATION_ERROR: - trackItwIdRequestFailure(identification?.mode); - break; default: trackItWalletCieCardReadingFailure({ reason: "KO" }); navigation.navigate(ITW_ROUTES.IDENTIFICATION.CIE.UNEXPECTED_ERROR); diff --git a/ts/features/itwallet/issuance/screens/ItwIssuanceCredentialAsyncContinuationScreen.tsx b/ts/features/itwallet/issuance/screens/ItwIssuanceCredentialAsyncContinuationScreen.tsx index 961f58ac83b..97730427fd2 100644 --- a/ts/features/itwallet/issuance/screens/ItwIssuanceCredentialAsyncContinuationScreen.tsx +++ b/ts/features/itwallet/issuance/screens/ItwIssuanceCredentialAsyncContinuationScreen.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect } from "react"; import * as O from "fp-ts/lib/Option"; import { pipe } from "fp-ts/lib/function"; import I18n from "../../../../i18n"; @@ -15,6 +15,7 @@ import { ITW_ROUTES } from "../../navigation/routes"; import { itwLifecycleIsValidSelector } from "../../lifecycle/store/selectors"; import { ItwCredentialIssuanceMachineContext } from "../../machine/provider"; import { getCredentialStatus } from "../../common/utils/itwClaimsUtils"; +import { CREDENTIALS_MAP, trackItwHasAlreadyCredential } from "../../analytics"; import { ItwIssuanceCredentialTrustIssuerScreen } from "./ItwIssuanceCredentialTrustIssuerScreen"; export type ItwIssuanceCredentialAsyncContinuationNavigationParams = { @@ -39,6 +40,22 @@ export const ItwIssuanceCredentialAsyncContinuationScreen = ({ ); const isWalletValid = useIOSelector(itwLifecycleIsValidSelector); + const isCredentialValid = pipe( + credentialOption, + O.map(getCredentialStatus), + O.map(status => status === "valid"), + O.getOrElse(() => false) + ); + + useEffect(() => { + if (isCredentialValid) { + trackItwHasAlreadyCredential({ + credential: CREDENTIALS_MAP[credentialType], + credential_status: "valid" + }); + } + }, [credentialType, isCredentialValid]); + if (!isWalletValid) { const ns = "features.itWallet.issuance.walletInstanceNotActive" as const; return ( @@ -67,13 +84,6 @@ export const ItwIssuanceCredentialAsyncContinuationScreen = ({ ); } - const isCredentialValid = pipe( - credentialOption, - O.map(getCredentialStatus), - O.map(status => status === "valid"), - O.getOrElse(() => false) - ); - if (isCredentialValid) { const ns = "features.itWallet.issuance.credentialAlreadyAdded" as const; return ( diff --git a/ts/features/itwallet/issuance/screens/ItwIssuanceCredentialFailureScreen.tsx b/ts/features/itwallet/issuance/screens/ItwIssuanceCredentialFailureScreen.tsx index 168a890c5ba..e9bbdaab043 100644 --- a/ts/features/itwallet/issuance/screens/ItwIssuanceCredentialFailureScreen.tsx +++ b/ts/features/itwallet/issuance/screens/ItwIssuanceCredentialFailureScreen.tsx @@ -13,7 +13,7 @@ import { CredentialIssuanceFailureTypeEnum } from "../../machine/credential/failure"; import { - selectCredential, + selectCredentialTypeOption, selectFailureOption } from "../../machine/credential/selectors"; import { ItwCredentialIssuanceMachineContext } from "../../machine/provider"; @@ -21,7 +21,10 @@ import { useItwDisableGestureNavigation } from "../../common/hooks/useItwDisable import { useAvoidHardwareBackButton } from "../../../../utils/useAvoidHardwareBackButton"; import { CREDENTIALS_MAP, + trackAddCredentialFailure, trackAddCredentialTimeout, + trackCredentialNotEntitledFailure, + trackItWalletDeferredIssuing, trackWalletCreationFailed } from "../../analytics"; import ROUTES from "../../../../navigation/routes"; @@ -54,13 +57,14 @@ type ContentViewProps = { failure: CredentialIssuanceFailure }; */ const ContentView = ({ failure }: ContentViewProps) => { const machineRef = ItwCredentialIssuanceMachineContext.useActorRef(); - const storedCredential = - ItwCredentialIssuanceMachineContext.useSelector(selectCredential); + const credentialType = ItwCredentialIssuanceMachineContext.useSelector( + selectCredentialTypeOption + ); const closeIssuance = (cta_id: string) => { machineRef.send({ type: "close" }); trackWalletCreationFailed({ - reason: failure.type, + reason: failure.reason, cta_category: "custom_2", cta_id }); @@ -68,7 +72,7 @@ const ContentView = ({ failure }: ContentViewProps) => { const retryIssuance = (cta_id: string) => { machineRef.send({ type: "retry" }); trackWalletCreationFailed({ - reason: failure.type, + reason: failure.reason, cta_category: "custom_1", cta_id }); @@ -144,14 +148,38 @@ const ContentView = ({ failure }: ContentViewProps) => { }; useEffect(() => { - if (storedCredential) { - trackAddCredentialTimeout({ + if (O.isNone(credentialType)) { + return; + } + + if (failure.type === CredentialIssuanceFailureTypeEnum.ASYNC_ISSUANCE) { + trackItWalletDeferredIssuing(CREDENTIALS_MAP[credentialType.value]); + return; + } + + if (failure.type === CredentialIssuanceFailureTypeEnum.NOT_ENTITLED) { + trackCredentialNotEntitledFailure({ reason: failure.reason, type: failure.type, - credential: CREDENTIALS_MAP[storedCredential.credentialType] + credential: CREDENTIALS_MAP[credentialType.value] }); + return; } - }, [failure, storedCredential]); + + if (failure.type === CredentialIssuanceFailureTypeEnum.GENERIC) { + trackAddCredentialFailure({ + reason: failure.reason, + type: failure.type, + credential: CREDENTIALS_MAP[credentialType.value] + }); + return; + } + trackAddCredentialTimeout({ + reason: failure.reason, + type: failure.type, + credential: CREDENTIALS_MAP[credentialType.value] + }); + }, [credentialType, failure]); const resultScreenProps = resultScreensMap[failure.type]; return ; diff --git a/ts/features/itwallet/issuance/screens/ItwIssuanceEidFailureScreen.tsx b/ts/features/itwallet/issuance/screens/ItwIssuanceEidFailureScreen.tsx index f67ab078973..e03d0d94ca5 100644 --- a/ts/features/itwallet/issuance/screens/ItwIssuanceEidFailureScreen.tsx +++ b/ts/features/itwallet/issuance/screens/ItwIssuanceEidFailureScreen.tsx @@ -15,21 +15,17 @@ import { selectFailureOption, selectIdentification } from "../../machine/eid/selectors"; -import { - ItwCredentialIssuanceMachineContext, - ItwEidIssuanceMachineContext -} from "../../machine/provider"; +import { ItwEidIssuanceMachineContext } from "../../machine/provider"; import { useAvoidHardwareBackButton } from "../../../../utils/useAvoidHardwareBackButton"; import { useItwDisableGestureNavigation } from "../../common/hooks/useItwDisableGestureNavigation"; import { - CREDENTIALS_MAP, KoState, - trackAddCredentialTimeout, trackIdNotMatch, + trackItwIdRequestFailure, + trackItwIdRequestUnexpected, trackItwUnsupportedDevice, trackWalletCreationFailed } from "../../analytics"; -import { selectCredential } from "../../machine/credential/selectors"; export const ItwIssuanceEidFailureScreen = () => { const machineRef = ItwEidIssuanceMachineContext.useActorRef(); @@ -37,8 +33,6 @@ export const ItwIssuanceEidFailureScreen = () => { ItwEidIssuanceMachineContext.useSelector(selectFailureOption); const identification = ItwEidIssuanceMachineContext.useSelector(selectIdentification); - const storedCredential = - ItwCredentialIssuanceMachineContext.useSelector(selectCredential); useItwDisableGestureNavigation(); useAvoidHardwareBackButton(); @@ -78,7 +72,7 @@ export const ItwIssuanceEidFailureScreen = () => { ), onPress: () => closeIssuance({ - reason: failure.type, + reason: failure.reason, cta_category: "custom_1", cta_id: I18n.t( "features.itWallet.issuance.genericError.primaryAction" @@ -91,7 +85,7 @@ export const ItwIssuanceEidFailureScreen = () => { ), onPress: () => closeIssuance({ - reason: failure.type, + reason: failure.reason, cta_category: "custom_2", cta_id: I18n.t( "features.itWallet.issuance.genericError.secondaryAction" @@ -156,16 +150,22 @@ export const ItwIssuanceEidFailureScreen = () => { trackIdNotMatch(identification.mode); } if (failure.type === IssuanceFailureType.UNSUPPORTED_DEVICE) { - trackItwUnsupportedDevice(); + trackItwUnsupportedDevice(failure); } if ( failure.type === IssuanceFailureType.ISSUER_GENERIC && - storedCredential + identification ) { - trackAddCredentialTimeout({ + trackItwIdRequestFailure({ + ITW_ID_method: identification.mode, + reason: failure.reason, + type: failure.type + }); + } + if (failure.type === IssuanceFailureType.GENERIC) { + trackItwIdRequestUnexpected({ reason: failure.reason, - type: failure.type, - credential: CREDENTIALS_MAP[storedCredential.credentialType] + type: failure.type }); } }, [failure]); diff --git a/ts/features/itwallet/issuance/screens/ItwIssuanceEidResultScreen.tsx b/ts/features/itwallet/issuance/screens/ItwIssuanceEidResultScreen.tsx index 0c79b683cd1..96ef3dd24d2 100644 --- a/ts/features/itwallet/issuance/screens/ItwIssuanceEidResultScreen.tsx +++ b/ts/features/itwallet/issuance/screens/ItwIssuanceEidResultScreen.tsx @@ -8,15 +8,21 @@ import { useAvoidHardwareBackButton } from "../../../../utils/useAvoidHardwareBa import { trackAddFirstCredential, trackBackToWallet, - trackSaveCredentialSuccess + trackSaveCredentialSuccess, + updateITWStatusAndIDProperties } from "../../analytics"; +import { useIOStore } from "../../../../store/hooks"; -const ITW_CREDENTIAL = "ITW_ID"; +const ITW_CREDENTIAL = "ITW_ID_V2"; export const ItwIssuanceEidResultScreen = () => { const route = useRoute(); + const store = useIOStore(); - useFocusEffect(() => trackSaveCredentialSuccess(ITW_CREDENTIAL)); + useFocusEffect(() => { + trackSaveCredentialSuccess(ITW_CREDENTIAL); + updateITWStatusAndIDProperties(store.getState()); + }); const machineRef = ItwEidIssuanceMachineContext.useActorRef(); @@ -28,9 +34,10 @@ export const ItwIssuanceEidResultScreen = () => { trackAddFirstCredential(); }; - const handleClose = () => { + const handleClose = async () => { machineRef.send({ type: "go-to-wallet" }); trackBackToWallet({ exit_page: route.name, credential: ITW_CREDENTIAL }); + updateITWStatusAndIDProperties(store.getState()); }; return ( diff --git a/ts/features/itwallet/lifecycle/screens/ItwIdentityNotMatchingScreen.tsx b/ts/features/itwallet/lifecycle/screens/ItwIdentityNotMatchingScreen.tsx index 51f82e0e918..2b659a2f245 100644 --- a/ts/features/itwallet/lifecycle/screens/ItwIdentityNotMatchingScreen.tsx +++ b/ts/features/itwallet/lifecycle/screens/ItwIdentityNotMatchingScreen.tsx @@ -8,8 +8,8 @@ import { import { OperationResultScreenContent } from "../../../../components/screens/OperationResultScreenContent"; import { logoutRequest } from "../../../../store/actions/authentication"; import { useAvoidHardwareBackButton } from "../../../../utils/useAvoidHardwareBackButton"; -import { useIODispatch } from "../../../../store/hooks"; -import { trackItwIdNotMatch } from "../../analytics"; +import { useIODispatch, useIOStore } from "../../../../store/hooks"; +import { trackItwIdNotMatch, trackWalletNewIdReset } from "../../analytics"; export const ItwIdentityNotMatchingScreen = () => { useAvoidHardwareBackButton(); @@ -17,10 +17,12 @@ export const ItwIdentityNotMatchingScreen = () => { useFocusEffect(trackItwIdNotMatch); const dispatch = useIODispatch(); + const store = useIOStore(); const resetWallet = () => { dispatch(itwLifecycleWalletReset()); dispatch(itwLifecycleIdentityCheckCompleted()); + trackWalletNewIdReset(store.getState()); }; const handleCancel = () => { diff --git a/ts/features/itwallet/machine/credential/actions.ts b/ts/features/itwallet/machine/credential/actions.ts index 26972fd2d3e..2040989a26c 100644 --- a/ts/features/itwallet/machine/credential/actions.ts +++ b/ts/features/itwallet/machine/credential/actions.ts @@ -10,6 +10,8 @@ import { CREDENTIALS_MAP, trackSaveCredentialSuccess } from "../../analytics"; import { itwCredentialsStore } from "../../credentials/store/actions"; import { ITW_ROUTES } from "../../navigation/routes"; import { itwWalletInstanceAttestationStore } from "../../walletInstance/store/actions"; +import { updateMixpanelProfileProperties } from "../../../../mixpanelConfig/profileProperties"; +import { updateMixpanelSuperProperties } from "../../../../mixpanelConfig/superProperties"; import { itwWalletInstanceAttestationSelector } from "../../walletInstance/store/reducers"; import { Context } from "./context"; import { CredentialIssuanceEvents } from "./events"; @@ -46,7 +48,16 @@ export default ( >) => { toast.success(I18n.t("features.itWallet.issuance.credentialResult.toast")); if (context.credentialType) { - trackSaveCredentialSuccess(CREDENTIALS_MAP[context.credentialType]); + const credential = CREDENTIALS_MAP[context.credentialType]; + trackSaveCredentialSuccess(credential); + void updateMixpanelProfileProperties(store.getState(), { + property: credential, + value: "valid" + }); + void updateMixpanelSuperProperties(store.getState(), { + property: credential, + value: "valid" + }); } navigation.reset({ index: 1, diff --git a/ts/features/itwallet/machine/eid/actions.ts b/ts/features/itwallet/machine/eid/actions.ts index c66ddc2d09f..a3487ab4899 100644 --- a/ts/features/itwallet/machine/eid/actions.ts +++ b/ts/features/itwallet/machine/eid/actions.ts @@ -17,6 +17,7 @@ import { import { ItwLifecycleState } from "../../lifecycle/store/reducers"; import { ITW_ROUTES } from "../../navigation/routes"; import { itwWalletInstanceAttestationStore } from "../../walletInstance/store/actions"; +import { trackItwDeactivated } from "../../analytics"; import { itwWalletInstanceAttestationSelector } from "../../walletInstance/store/reducers"; import { itwIntegrityKeyTagSelector } from "../../issuance/store/selectors"; import { Context } from "./context"; @@ -180,6 +181,10 @@ export const createEidIssuanceActionsImplementation = ( toast.success(I18n.t("features.itWallet.issuance.eidResult.success.toast")); }, + trackWalletInstanceRevocation: () => { + trackItwDeactivated(store.getState()); + }, + onInit: assign( () => { const state = store.getState(); diff --git a/ts/features/itwallet/machine/eid/machine.ts b/ts/features/itwallet/machine/eid/machine.ts index 4abed3849db..1807fb4833c 100644 --- a/ts/features/itwallet/machine/eid/machine.ts +++ b/ts/features/itwallet/machine/eid/machine.ts @@ -48,6 +48,7 @@ export const itwEidIssuanceMachine = setup({ handleSessionExpired: notImplemented, abortIdentification: notImplemented, resetWalletInstance: notImplemented, + trackWalletInstanceRevocation: notImplemented, setFailure: assign(({ event }) => ({ failure: mapEventToFailure(event) })), onInit: notImplemented }, @@ -144,7 +145,11 @@ export const itwEidIssuanceMachine = setup({ invoke: { src: "revokeWalletInstance", onDone: { - actions: ["resetWalletInstance", "closeIssuance"] + actions: [ + "resetWalletInstance", + "closeIssuance", + "trackWalletInstanceRevocation" + ] }, onError: { actions: assign( diff --git a/ts/features/itwallet/presentation/components/ItwCredentialTrustmark.tsx b/ts/features/itwallet/presentation/components/ItwCredentialTrustmark.tsx index f496b9eb20b..5399c19212c 100644 --- a/ts/features/itwallet/presentation/components/ItwCredentialTrustmark.tsx +++ b/ts/features/itwallet/presentation/components/ItwCredentialTrustmark.tsx @@ -1,14 +1,14 @@ import React from "react"; -import { Pressable, StyleSheet, View, Image } from "react-native"; +import { Image, Pressable, StyleSheet, View } from "react-native"; import LinearGradient from "react-native-linear-gradient"; import { - WithTestID, - Caption, - VStack, Body, + Caption, FeatureInfo, + H6, VSpacer, - H6 + VStack, + WithTestID } from "@pagopa/io-app-design-system"; import Animated from "react-native-reanimated"; import I18n from "../../../../i18n"; @@ -23,6 +23,10 @@ import { import { CredentialType } from "../../common/utils/itwMocksUtils"; import { getCredentialStatus } from "../../common/utils/itwClaimsUtils"; import { itwEaaVerifierBaseUrl } from "../../../../config"; +import { + CREDENTIALS_MAP, + trackWalletCredentialShowTrustmark +} from "../../analytics"; type ItwCredentialTrustmarkProps = WithTestID<{ credential: StoredCredential; @@ -63,10 +67,17 @@ export const ItwCredentialTrustmark = ({ return null; } + const onPressWithTrackEvent = () => { + trackWalletCredentialShowTrustmark( + CREDENTIALS_MAP[credential.credentialType] + ); + trustmarkBottomSheet.present(); + }; + return ( <> { const [isFlipped, setIsFlipped] = React.useState(false); + const handleOnPress = useCallback(() => { + trackWalletShowBack(CREDENTIALS_MAP[credential.credentialType]); + setIsFlipped(_ => !_); + }, [credential.credentialType]); + const { backgroundColor } = getThemeColorByCredentialType( credential.credentialType ); @@ -66,7 +72,7 @@ const ItwPresentationCredentialCard = ({ credential }: Props) => { isFlipped ? "flipCardBack" : "flipCardFront" }` )} - onPress={() => setIsFlipped(_ => !_)} + onPress={handleOnPress} icon="switchCard" iconPosition="end" /> diff --git a/ts/features/itwallet/presentation/components/ItwPresentationDetailsFooter.tsx b/ts/features/itwallet/presentation/components/ItwPresentationDetailsFooter.tsx index 44cdce9778a..eb7190e8d5a 100644 --- a/ts/features/itwallet/presentation/components/ItwPresentationDetailsFooter.tsx +++ b/ts/features/itwallet/presentation/components/ItwPresentationDetailsFooter.tsx @@ -9,10 +9,16 @@ import { Alert } from "react-native"; import { useStartSupportRequest } from "../../../../hooks/useStartSupportRequest"; import I18n from "../../../../i18n"; import { useIONavigation } from "../../../../navigation/params/AppParamsList"; -import { useIODispatch } from "../../../../store/hooks"; +import { useIODispatch, useIOStore } from "../../../../store/hooks"; import { CredentialType } from "../../common/utils/itwMocksUtils"; import { StoredCredential } from "../../common/utils/itwTypesUtils"; import { itwCredentialsRemove } from "../../credentials/store/actions"; +import { + CREDENTIALS_MAP, + trackCredentialDeleteProperties, + trackItwCredentialDelete, + trackWalletCredentialSupport +} from "../../analytics"; type ItwPresentationDetailFooterProps = { credential: StoredCredential; @@ -22,6 +28,8 @@ const ItwPresentationDetailsFooter = ({ credential }: ItwPresentationDetailFooterProps) => { const dispatch = useIODispatch(); + const store = useIOStore(); + const navigation = useIONavigation(); const toast = useIOToast(); @@ -34,11 +42,17 @@ const ItwPresentationDetailsFooter = ({ toast.success( I18n.t("features.itWallet.presentation.credentialDetails.toast.removed") ); + void trackCredentialDeleteProperties( + CREDENTIALS_MAP[credential.credentialType], + store.getState() + ); + navigation.pop(); }; - const showRemoveCredentialDialog = () => - Alert.alert( + const showRemoveCredentialDialog = () => { + trackItwCredentialDelete(CREDENTIALS_MAP[credential.credentialType]); + return Alert.alert( I18n.t( "features.itWallet.presentation.credentialDetails.dialogs.remove.title" ), @@ -59,6 +73,12 @@ const ItwPresentationDetailsFooter = ({ } ] ); + }; + + const startAndTrackSupportRequest = () => { + trackWalletCredentialSupport(CREDENTIALS_MAP[credential.credentialType]); + startSupportRequest(); + }; return ( @@ -72,7 +92,7 @@ const ItwPresentationDetailsFooter = ({ accessibilityLabel={I18n.t( "features.itWallet.presentation.credentialDetails.actions.requestAssistance" )} - onPress={() => startSupportRequest()} + onPress={startAndTrackSupportRequest} /> {credential.credentialType !== CredentialType.PID ? ( { ) }); + useFocusEffect(() => { + if (O.isNone(credentialOption)) { + return; + } + const credential = credentialOption.value; + + trackCredentialDetail({ + credential: CREDENTIALS_MAP[credential.credentialType], + credential_status: + credential.storedStatusAttestation?.credentialStatus || "not_valid" + }); + }); + if (O.isNone(credentialOption)) { // This is unlikely to happen, but we want to handle the case where the credential is not found // because of inconsistencies in the state, and assert that the credential is O.some @@ -87,19 +106,25 @@ const getCtaProps = ( ): CredentialCtaProps | undefined => { const { parsedCredential } = credential; + const onPress = () => { + if (CREDENTIALS_MAP[credential.credentialType] === "ITW_TS_V2") { + trackWalletCredentialShowFAC_SIMILE(); + } + + navigation.navigate(ITW_ROUTES.MAIN, { + screen: ITW_ROUTES.PRESENTATION.CREDENTIAL_ATTACHMENT, + params: { + attachmentClaim: parsedCredential[WellKnownClaim.content] + } + }); + }; + // If the "content" claim exists, return a CTA to view and download it. if (parsedCredential[WellKnownClaim.content]) { return { label: I18n.t("features.itWallet.presentation.ctas.openPdf"), icon: "docPaymentTitle", - onPress: () => { - navigation.navigate(ITW_ROUTES.MAIN, { - screen: ITW_ROUTES.PRESENTATION.CREDENTIAL_ATTACHMENT, - params: { - attachmentClaim: parsedCredential[WellKnownClaim.content] - } - }); - } + onPress }; } diff --git a/ts/features/newWallet/components/WalletCategoryFilterTabs.tsx b/ts/features/newWallet/components/WalletCategoryFilterTabs.tsx index 348aa8d9880..96f4fea1c0a 100644 --- a/ts/features/newWallet/components/WalletCategoryFilterTabs.tsx +++ b/ts/features/newWallet/components/WalletCategoryFilterTabs.tsx @@ -13,6 +13,7 @@ import { selectWalletCategoryFilter } from "../store/selectors"; import { walletCardCategoryFilters } from "../types"; import { itwLifecycleIsValidSelector } from "../../itwallet/lifecycle/store/selectors"; import { itwIsWalletEmptySelector } from "../../itwallet/credentials/store/selectors"; +import { trackWalletCategoryFilter } from "../../itwallet/analytics"; const WalletCategoryFilterTabs = () => { const dispatch = useIODispatch(); @@ -30,11 +31,12 @@ const WalletCategoryFilterTabs = () => { : 0; const handleFilterSelected = (index: number) => { - dispatch( - walletSetCategoryFilter( - index === 0 ? undefined : walletCardCategoryFilters[index - 1] - ) - ); + const categoryByIndex = + index === 0 ? undefined : walletCardCategoryFilters[index - 1]; + dispatch(walletSetCategoryFilter(categoryByIndex)); + if (categoryByIndex) { + trackWalletCategoryFilter(categoryByIndex); + } }; return ( diff --git a/ts/features/newWallet/components/WalletEmptyScreenContent.tsx b/ts/features/newWallet/components/WalletEmptyScreenContent.tsx index 41c89403a22..6a8c331cf14 100644 --- a/ts/features/newWallet/components/WalletEmptyScreenContent.tsx +++ b/ts/features/newWallet/components/WalletEmptyScreenContent.tsx @@ -7,13 +7,21 @@ import { } from "@pagopa/io-app-design-system"; import React from "react"; import { StyleSheet, View } from "react-native"; +import { useFocusEffect } from "@react-navigation/native"; import I18n from "../../../i18n"; import { useIONavigation } from "../../../navigation/params/AppParamsList"; import { ITW_ROUTES } from "../../itwallet/navigation/routes"; -import { trackWalletAdd } from "../../itwallet/analytics"; +import { + trackWalletAdd, + updatePropertiesWalletRevoked +} from "../../itwallet/analytics"; +import { useIOStore } from "../../../store/hooks"; const WalletEmptyScreenContent = () => { const navigation = useIONavigation(); + const store = useIOStore(); + + useFocusEffect(() => updatePropertiesWalletRevoked(store.getState())); const handleAddToWalletButtonPress = () => { trackWalletAdd(); diff --git a/ts/features/newWallet/screens/WalletHomeScreen.tsx b/ts/features/newWallet/screens/WalletHomeScreen.tsx index ad68ed79b2f..d09830a5ef5 100644 --- a/ts/features/newWallet/screens/WalletHomeScreen.tsx +++ b/ts/features/newWallet/screens/WalletHomeScreen.tsx @@ -23,7 +23,7 @@ import { WalletPaymentsRedirectBanner } from "../components/WalletPaymentsRedire import { walletToggleLoadingState } from "../store/actions/placeholders"; import { selectWalletCards } from "../store/selectors"; import { - trackAllCredentialProfileProperties, + trackAllCredentialProfileAndSuperProperties, trackOpenWalletScreen, trackWalletAdd } from "../../itwallet/analytics"; @@ -35,18 +35,12 @@ const WalletHomeScreen = ({ route }: Props) => { const store = useIOStore(); useFocusEffect(() => { trackOpenWalletScreen(); - void trackAllCredentialProfileProperties(store.getState()); + void trackAllCredentialProfileAndSuperProperties(store.getState()); }); const dispatch = useIODispatch(); const isNewElementAdded = React.useRef(route.params?.newMethodAdded || false); - useFocusEffect( - React.useCallback(() => { - trackOpenWalletScreen(); - }, []) - ); - useOnFirstRender(() => { fetchWalletSectionData(); }); diff --git a/ts/mixpanelConfig/profileProperties.ts b/ts/mixpanelConfig/profileProperties.ts index 157dd42523f..e9aa7c6cf37 100644 --- a/ts/mixpanelConfig/profileProperties.ts +++ b/ts/mixpanelConfig/profileProperties.ts @@ -43,12 +43,11 @@ type ProfileProperties = { NOTIFICATION_PERMISSION: NotificationPermissionType; SERVICE_CONFIGURATION: ServiceConfigurationTrackingType; TRACKING: MixpanelOptInTrackingType; - ITW_STATUS: ItwStatus; - ITW_ID: ItwId; - ITW_PG: ItwPg; - ITW_TS: ItwTs; - ITW_CED: ItwCed; - ITW_HAS_READ_IPZS_POLICY: boolean; + ITW_STATUS_V2: ItwStatus; + ITW_ID_V2: ItwId; + ITW_PG_V2: ItwPg; + ITW_TS_V2: ItwTs; + ITW_CED_V2: ItwCed; SAVED_PAYMENT_METHOD: number; }; @@ -67,11 +66,11 @@ export const updateMixpanelProfileProperties = async ( const notificationsEnabled = await checkNotificationPermissions(); const SERVICE_CONFIGURATION = serviceConfigHandler(state); const TRACKING = mixpanelOptInHandler(state); - const ITW_STATUS = walletStatusHandler(); - const ITW_ID = idStatusHandler(state); - const ITW_PG = pgStatusHandler(state); - const ITW_TS = tsStatusHandler(state); - const ITW_CED = cedStatusHandler(state); + const ITW_STATUS_V2 = walletStatusHandler(state); + const ITW_ID_V2 = idStatusHandler(state); + const ITW_PG_V2 = pgStatusHandler(state); + const ITW_TS_V2 = tsStatusHandler(state); + const ITW_CED_V2 = cedStatusHandler(state); const paymentsAnalyticsData = getPaymentsAnalyticsConfiguration(state); const profilePropertiesObject: ProfileProperties = { @@ -84,12 +83,11 @@ export const updateMixpanelProfileProperties = async ( getNotificationPermissionType(notificationsEnabled), SERVICE_CONFIGURATION, TRACKING, - ITW_HAS_READ_IPZS_POLICY: false, - ITW_STATUS, - ITW_ID, - ITW_PG, - ITW_TS, - ITW_CED, + ITW_STATUS_V2, + ITW_ID_V2, + ITW_PG_V2, + ITW_TS_V2, + ITW_CED_V2, SAVED_PAYMENT_METHOD: paymentsAnalyticsData.savedPaymentMethods || 0 }; @@ -121,8 +119,10 @@ const tosVersionHandler = (state: GlobalState): number | string => { return optInState ? optInState : "not set"; }; -// TODO [SIW-1438]: Add dynamic profile properties -const walletStatusHandler = (): ItwStatus => "L2"; +const walletStatusHandler = (state: GlobalState): ItwStatus => { + const credentialsState = itwCredentialsSelector(state); + return credentialsState.eid ? "L2" : "not_active"; +}; const idStatusHandler = (state: GlobalState): ItwId => { const credentialsState = itwCredentialsSelector(state); diff --git a/ts/mixpanelConfig/superProperties.ts b/ts/mixpanelConfig/superProperties.ts index c1e8cbea804..c7e2ce064d4 100644 --- a/ts/mixpanelConfig/superProperties.ts +++ b/ts/mixpanelConfig/superProperties.ts @@ -18,6 +18,17 @@ import { GlobalState } from "../store/reducers/types"; import { mixpanel } from "../mixpanel"; import { LoginSessionDuration } from "../features/fastLogin/analytics/optinAnalytics"; import { checkNotificationPermissions } from "../features/pushNotifications/utils"; +import { + ItwCed, + ItwId, + ItwPg, + ItwStatus, + ItwTs +} from "../features/itwallet/analytics"; +import { + itwCredentialsByTypeSelector, + itwCredentialsSelector +} from "../features/itwallet/credentials/store/selectors"; import { Property, PropertyToUpdate, @@ -37,6 +48,11 @@ type SuperProperties = { NOTIFICATION_CONFIGURATION: NotificationPreferenceConfiguration; NOTIFICATION_PERMISSION: NotificationPermissionType; SERVICE_CONFIGURATION: ServiceConfigurationTrackingType; + ITW_STATUS_V2: ItwStatus; + ITW_ID_V2: ItwId; + ITW_PG_V2: ItwPg; + ITW_TS_V2: ItwTs; + ITW_CED_V2: ItwCed; }; export const updateMixpanelSuperProperties = async ( @@ -55,6 +71,11 @@ export const updateMixpanelSuperProperties = async ( const NOTIFICATION_CONFIGURATION = notificationConfigurationHandler(state); const notificationsEnabled = await checkNotificationPermissions(); const SERVICE_CONFIGURATION = serviceConfigHandler(state); + const ITW_STATUS_V2 = walletStatusHandler(state); + const ITW_ID_V2 = idStatusHandler(state); + const ITW_PG_V2 = pgStatusHandler(state); + const ITW_TS_V2 = tsStatusHandler(state); + const ITW_CED_V2 = cedStatusHandler(state); const superPropertiesObject: SuperProperties = { isScreenReaderEnabled: screenReaderEnabled, @@ -67,7 +88,12 @@ export const updateMixpanelSuperProperties = async ( NOTIFICATION_CONFIGURATION, NOTIFICATION_PERMISSION: getNotificationPermissionType(notificationsEnabled), - SERVICE_CONFIGURATION + SERVICE_CONFIGURATION, + ITW_STATUS_V2, + ITW_ID_V2, + ITW_PG_V2, + ITW_TS_V2, + ITW_CED_V2 }; if (forceUpdateFor) { @@ -84,3 +110,27 @@ const forceUpdate = ( // eslint-disable-next-line functional/immutable-data superPropertiesObject[toUpdate.property] = toUpdate.value; }; + +const walletStatusHandler = (state: GlobalState): ItwStatus => { + const credentialsState = itwCredentialsSelector(state); + return credentialsState.eid ? "L2" : "not_active"; +}; + +const idStatusHandler = (state: GlobalState): ItwId => { + const credentialsState = itwCredentialsSelector(state); + return credentialsState.eid ? "valid" : "not_available"; +}; +const pgStatusHandler = (state: GlobalState): ItwPg => { + const credentialsByType = itwCredentialsByTypeSelector(state); + return credentialsByType.MDL ? "valid" : "not_available"; +}; +const tsStatusHandler = (state: GlobalState): ItwTs => { + const credentialsByType = itwCredentialsByTypeSelector(state); + return credentialsByType.EuropeanHealthInsuranceCard + ? "valid" + : "not_available"; +}; +const cedStatusHandler = (state: GlobalState): ItwCed => { + const credentialsByType = itwCredentialsByTypeSelector(state); + return credentialsByType.EuropeanDisabilityCard ? "valid" : "not_available"; +};