diff --git a/packages/sdk-install-modal-web/src/components.d.ts b/packages/sdk-install-modal-web/src/components.d.ts index 07a464fe6..fa7cade6e 100644 --- a/packages/sdk-install-modal-web/src/components.d.ts +++ b/packages/sdk-install-modal-web/src/components.d.ts @@ -5,6 +5,8 @@ * It contains typing information for all components that exist in this project. */ import { HTMLStencilElement, JSXBase } from "@stencil/core/internal"; +import { TrackingEvents } from "./components/misc/tracking-events"; +export { TrackingEvents } from "./components/misc/tracking-events"; export namespace Components { interface MmInstallModal { /** @@ -47,6 +49,7 @@ declare global { interface HTMLMmInstallModalElementEventMap { "close": any; "startDesktopOnboarding": any; + "trackAnalytics": { event: TrackingEvents, params?: Record }; } interface HTMLMmInstallModalElement extends Components.MmInstallModal, HTMLStencilElement { addEventListener(type: K, listener: (this: HTMLMmInstallModalElement, ev: MmInstallModalCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; @@ -113,6 +116,7 @@ declare namespace LocalJSX { "link"?: string; "onClose"?: (event: MmInstallModalCustomEvent) => void; "onStartDesktopOnboarding"?: (event: MmInstallModalCustomEvent) => void; + "onTrackAnalytics"?: (event: MmInstallModalCustomEvent<{ event: TrackingEvents, params?: Record }>) => void; "preferDesktop"?: boolean; "sdkVersion"?: string; } diff --git a/packages/sdk-install-modal-web/src/components/misc/tracking-events.ts b/packages/sdk-install-modal-web/src/components/misc/tracking-events.ts new file mode 100644 index 000000000..6bfd70889 --- /dev/null +++ b/packages/sdk-install-modal-web/src/components/misc/tracking-events.ts @@ -0,0 +1,5 @@ +export enum TrackingEvents { + SDK_MODAL_VIEWED = 'sdk_modal_viewed', + SDK_MODAL_BUTTON_CLICKED = 'sdk_modal_button_clicked', + SDK_MODAL_TOGGLE_CHANGED = 'sdk_modal_toggle_changed', +} diff --git a/packages/sdk-install-modal-web/src/components/mm-install-modal/mm-install-modal.tsx b/packages/sdk-install-modal-web/src/components/mm-install-modal/mm-install-modal.tsx index 178b698e1..5e0b3c88c 100644 --- a/packages/sdk-install-modal-web/src/components/mm-install-modal/mm-install-modal.tsx +++ b/packages/sdk-install-modal-web/src/components/mm-install-modal/mm-install-modal.tsx @@ -10,6 +10,8 @@ import CloseButton from '../misc/CloseButton'; import Logo from '../misc/Logo'; import encodeQR from '@paulmillr/qr'; import { SimpleI18n } from '../misc/simple-i18n'; +import { TrackingEvents } from '../misc/tracking-events'; + @Component({ tag: 'mm-install-modal', styleUrl: '../style.css', @@ -31,6 +33,8 @@ export class InstallModal { @Event() startDesktopOnboarding: EventEmitter; + @Event() trackAnalytics: EventEmitter<{ event: TrackingEvents, params?: Record }>; + @State() tab: number = 1; @State() isDefaultTab: boolean = true; @@ -49,6 +53,16 @@ export class InstallModal { this.i18nInstance = new SimpleI18n(); } + componentDidLoad() { + this.trackAnalytics.emit({ + event: TrackingEvents.SDK_MODAL_VIEWED, + params: { + extensionInstalled: false, + tab: this.tab === 1 ? 'desktop' : 'mobile', + }, + }); + } + async connectedCallback() { await this.i18nInstance.init({ fallbackLng: 'en' @@ -70,11 +84,27 @@ export class InstallModal { } onStartDesktopOnboardingHandler() { + this.trackAnalytics.emit({ + event: TrackingEvents.SDK_MODAL_BUTTON_CLICKED, + params: { + button_type: 'install_extension', + tab: 'desktop', + }, + }); this.startDesktopOnboarding.emit(); } - setTab(newTab: number) { - this.tab = newTab + setTab(newTab: number, isUserAction: boolean = false) { + if (isUserAction) { + this.trackAnalytics.emit({ + event: TrackingEvents.SDK_MODAL_TOGGLE_CHANGED, + params: { + toggle: this.tab === 1 ? 'desktop_to_mobile' : 'mobile_to_desktop', + }, + }); + } + + this.tab = newTab; this.isDefaultTab = false; } @@ -84,8 +114,7 @@ export class InstallModal { } const t = (key: string) => this.i18nInstance.t(key); - - const currentTab = this.isDefaultTab ? this.preferDesktop ? 1 : 2 : this.tab + const currentTab = this.isDefaultTab ? this.preferDesktop ? 1 : 2 : this.tab; const svgElement = encodeQR(this.link, "svg", { ecc: "medium", @@ -110,13 +139,13 @@ export class InstallModal {
this.setTab(1)} + onClick={() => this.setTab(1, true)} class={`tab flexItem ${currentTab === 1 ? 'tabactive': ''}`} > {t('DESKTOP')}
this.setTab(2)} + onClick={() => this.setTab(2, true)} class={`tab flexItem ${currentTab === 2 ? 'tabactive': ''}`} > {t('MOBILE')} diff --git a/packages/sdk/src/services/RemoteConnection/ModalManager/showInstallModal.test.ts b/packages/sdk/src/services/RemoteConnection/ModalManager/showInstallModal.test.ts index 6dc88bdf7..22967ca2f 100644 --- a/packages/sdk/src/services/RemoteConnection/ModalManager/showInstallModal.test.ts +++ b/packages/sdk/src/services/RemoteConnection/ModalManager/showInstallModal.test.ts @@ -52,6 +52,7 @@ describe('showInstallModal', () => { terminate: expect.any(Function), debug: state.developerMode, connectWithExtension: expect.any(Function), + onAnalyticsEvent: expect.any(Function), }); expect(mockModalsInstall).toHaveBeenCalledTimes(1); expect(mockInstallModalMount).toHaveBeenCalledWith(link); diff --git a/packages/sdk/src/services/RemoteConnection/ModalManager/showInstallModal.ts b/packages/sdk/src/services/RemoteConnection/ModalManager/showInstallModal.ts index 7f181419e..97001f366 100644 --- a/packages/sdk/src/services/RemoteConnection/ModalManager/showInstallModal.ts +++ b/packages/sdk/src/services/RemoteConnection/ModalManager/showInstallModal.ts @@ -1,3 +1,4 @@ +import { TrackingEvents } from '@metamask/sdk-communication-layer'; import { logger } from '../../../utils/logger'; import { RemoteConnectionProps, @@ -34,6 +35,22 @@ export function showInstallModal( options.connectWithExtensionProvider?.(); return false; }, + onAnalyticsEvent: ({ + event, + params, + }: { + event: TrackingEvents; + params?: Record; + }) => { + const extended = { + ...params, + sdkVersion: options.sdk.getVersion(), + dappId: options.dappMetadata?.name, + source: options._source, + url: options.dappMetadata?.url, + }; + state.analytics?.send({ event, params: extended }); + }, }); state.installModal?.mount?.(link); } diff --git a/packages/sdk/src/services/RemoteConnection/RemoteConnection.ts b/packages/sdk/src/services/RemoteConnection/RemoteConnection.ts index db873a882..7fea22475 100644 --- a/packages/sdk/src/services/RemoteConnection/RemoteConnection.ts +++ b/packages/sdk/src/services/RemoteConnection/RemoteConnection.ts @@ -8,6 +8,7 @@ import { KeyInfo, RemoteCommunication, StorageManagerProps, + TrackingEvents, } from '@metamask/sdk-communication-layer'; import { MetaMaskInstaller } from '../../Platform/MetaMaskInstaller'; import { PlatformManager } from '../../Platform/PlatfformManager'; @@ -55,13 +56,20 @@ export interface RemoteConnectionProps { */ modals: { onPendingModalDisconnect?: () => void; - install?: (params: { + install?: (args: { link: string; debug?: boolean; preferDesktop?: boolean; installer: MetaMaskInstaller; terminate?: () => void; connectWithExtension?: () => void; + onAnalyticsEvent: ({ + event, + params, + }: { + event: TrackingEvents; + params?: Record; + }) => void; }) => { unmount?: (shouldTerminate?: boolean) => void; mount?: (link: string) => void; diff --git a/packages/sdk/src/ui/InstallModal/InstallModal-web.ts b/packages/sdk/src/ui/InstallModal/InstallModal-web.ts index d4c253453..fcf99a626 100644 --- a/packages/sdk/src/ui/InstallModal/InstallModal-web.ts +++ b/packages/sdk/src/ui/InstallModal/InstallModal-web.ts @@ -1,3 +1,4 @@ +import { TrackingEvents } from '@metamask/sdk-communication-layer'; import packageJson from '../../../package.json'; import { MetaMaskInstaller } from '../../Platform/MetaMaskInstaller'; import { logger } from '../../utils/logger'; @@ -10,6 +11,7 @@ const sdkWebInstallModal = ({ terminate, connectWithExtension, preferDesktop, + onAnalyticsEvent, }: { link: string; debug?: boolean; @@ -17,6 +19,13 @@ const sdkWebInstallModal = ({ installer: MetaMaskInstaller; terminate?: () => void; connectWithExtension?: () => void; + onAnalyticsEvent: ({ + event, + params, + }: { + event: TrackingEvents; + params?: Record; + }) => void; }) => { let modalLoader: ModalLoader | null = null; let div: HTMLDivElement | null = null; @@ -94,6 +103,7 @@ const sdkWebInstallModal = ({ link, metaMaskInstaller: installer, onClose: unmount, + onAnalyticsEvent, }) .catch((err) => { console.error(`[UI: InstallModal-web: sdkWebInstallModal()]`, err); diff --git a/packages/sdk/src/ui/InstallModal/Modal-web.ts b/packages/sdk/src/ui/InstallModal/Modal-web.ts index 3e58207f4..7eae806bd 100644 --- a/packages/sdk/src/ui/InstallModal/Modal-web.ts +++ b/packages/sdk/src/ui/InstallModal/Modal-web.ts @@ -1,3 +1,4 @@ +import { TrackingEvents } from '@metamask/sdk-communication-layer'; import type { Components } from '@metamask/sdk-install-modal-web'; export interface InstallWidgetProps extends Components.MmInstallModal { @@ -6,6 +7,10 @@ export interface InstallWidgetProps extends Components.MmInstallModal { metaMaskInstaller: { startDesktopOnboarding: () => void; }; + onAnalyticsEvent: (event: { + event: TrackingEvents; + params?: Record; + }) => void; } export interface PendingWidgetProps extends Components.MmPendingModal { @@ -80,6 +85,9 @@ export default class ModalLoader { 'startDesktopOnboarding', props.metaMaskInstaller.startDesktopOnboarding, ); + + modal.addEventListener('trackAnalytics', ((e: CustomEvent) => + props.onAnalyticsEvent?.(e.detail)) as EventListener); props.parentElement.appendChild(modal); }