Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
cb9ef22
feat(analytics-controller): add onSetupCompleted lifecycle hook
NicolasMassart Nov 6, 2025
a7dfa7c
Merge branch 'main' into refactor/7079_update_analytics_controller
NicolasMassart Nov 6, 2025
34e6357
feat(analytics-controller): add isOptedIn action and refactor identif…
NicolasMassart Nov 7, 2025
18659a9
feat(analytics-controller): add error type and fix linter errors
NicolasMassart Nov 7, 2025
d6488c1
refactor(analytics-controller): update trackEvent to accept Analytics…
NicolasMassart Nov 17, 2025
b19d7ee
format: fix lint
NicolasMassart Nov 17, 2025
b844ad4
Merge branch 'main' into refactor/7079_update_analytics_controller
NicolasMassart Nov 17, 2025
8f93f3f
fix: update AnalyticsController-method-action-types.ts
NicolasMassart Nov 17, 2025
704ae19
Merge branch 'refactor/7079_update_analytics_controller' of github.co…
NicolasMassart Nov 17, 2025
2195150
refactor(analytics-controller): replace isAnonymous with isSensitive …
NicolasMassart Nov 18, 2025
bc3f7dd
refactor(analytics-controller): improve test quality and reduce redun…
NicolasMassart Nov 18, 2025
01e0924
refactor(analytics-controller): improve test quality and reduce redun…
NicolasMassart Nov 18, 2025
e71689f
Merge branch 'refactor/7079_update_analytics_controller' of github.co…
NicolasMassart Nov 18, 2025
301a04b
Merge branch 'main' into refactor/7079_update_analytics_controller
NicolasMassart Nov 18, 2025
087fc5e
format(analytics-controller): fix lint
NicolasMassart Nov 19, 2025
16f7af7
Merge branch 'main' into refactor/7079_update_analytics_controller
NicolasMassart Nov 19, 2025
21a5dda
fix: regenerate action types
NicolasMassart Nov 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 72 additions & 11 deletions packages/analytics-controller/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,24 @@ The controller delegates platform-specific analytics implementation to a `Analyt
import type { AnalyticsPlatformAdapter } from '@metamask/analytics-controller';

const platformAdapter: AnalyticsPlatformAdapter = {
trackEvent: (eventName: string, properties: Record<string, unknown>) => {
track: (eventName: string, properties: Record<string, unknown>) => {
// Platform-specific implementation (e.g., Segment, Mixpanel, etc.)
segment.track(eventName, properties);
},
identify: (userId: string, traits?: Record<string, unknown>) => {
segment.identify(userId, traits);
},
trackPage: (pageName: string, properties?: Record<string, unknown>) => {
segment.page(pageName, properties);
view: (name: string, properties?: Record<string, unknown>) => {
segment.page(name, properties);
},
onSetupCompleted: (analyticsId: string) => {
// Lifecycle hook called after controller initialization
// The analyticsId is guaranteed to be set when this method is called
// Use this for platform-specific setup that requires the analytics ID
// For example, adding plugins that need the analytics ID:
segment.add({
plugin: new PrivacyPlugin(analyticsId),
});
},
};
```
Expand Down Expand Up @@ -112,7 +121,7 @@ console.log(controller.state.analyticsId); // '550e8400-e29b-41d4-a716-446655440
### 5. Track Page Views

```typescript
controller.trackPage('home', {
controller.trackView('home', {
referrer: 'google',
campaign: 'summer-2024',
});
Expand All @@ -125,9 +134,13 @@ controller.trackPage('home', {
controller.enable();
controller.disable();

// Opt in/out
controller.optIn();
controller.optOut();
// Opt in/out for regular account
controller.optInForRegularAccount();
controller.optOutForRegularAccount();

// Opt in/out for social account
controller.optInForSocialAccount();
controller.optOutForSocialAccount();
```

### 7. Use Messenger Actions
Expand All @@ -148,10 +161,10 @@ messenger.call(
},
);

messenger.call('AnalyticsController:enable');
messenger.call('AnalyticsController:disable');
messenger.call('AnalyticsController:optIn');
messenger.call('AnalyticsController:optOut');
messenger.call('AnalyticsController:optInForRegularAccount');
messenger.call('AnalyticsController:optOutForRegularAccount');
messenger.call('AnalyticsController:optInForSocialAccount');
messenger.call('AnalyticsController:optOutForSocialAccount');
```

### 8. Subscribe to State Changes
Expand Down Expand Up @@ -193,6 +206,54 @@ const defaultState = getDefaultAnalyticsControllerState();

**Analytics ID:** The `analyticsId` is a UUIDv4 string. If not provided in the `state` parameter, the controller automatically generates one on initialization. This ID is persisted in state and remains consistent across restarts. If you provide an `analyticsId` in the `state` parameter, it will be used instead (useful for migrations).

## Lifecycle Hooks

### `onSetupCompleted`

The `onSetupCompleted` lifecycle hook is called once after the `AnalyticsController` is fully initialized. This hook allows platform-specific adapters to perform setup that requires access to the controller's state (e.g., `analyticsId`).

**When it's called:**

- After the controller is fully initialized
- Only when `analyticsId` is set in controller state (the presence of `analyticsId` is the definition of "completed" setup)
- The `analyticsId` parameter is guaranteed to be set and be a valid UUIDv4 when this method is called

**What it's used for:**

Platform-specific setup that requires access to adapter implementation like adding plugins or middleware that need the `analyticsId`
Any initialization that depends on the controller being ready

**Example usage:**

```typescript
const platformAdapter: AnalyticsPlatformAdapter = {
// ... other methods ...
onSetupCompleted: (analyticsId: string) => {
// Add platform-specific plugins that require analyticsId
client.add({
plugin: new PrivacyPlugin(analyticsId),
});
},
};
```

**Error handling:**

- Errors thrown in `onSetupCompleted` are caught and logged

**Best practices:**

- Use `onSetupCompleted` for setup that requires controller state
- Keep setup logic minimal and focused
- Handle errors gracefully within the hook
- If you don't need setup, provide a no-op implementation:

```typescript
onSetupCompleted: (_analyticsId: string) => {
// No-op: this adapter doesn't need setup
},
```

## Debugging

To display analytics-controller logs in the mobile app, you can add the following to your `.js.env` file:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import type { AnalyticsController } from './AnalyticsController';
*
* Events are only tracked if analytics is enabled.
*
* @param eventName - The name of the event
* @param properties - Event properties
* @param event - Analytics event with properties and sensitive properties
*/
export type AnalyticsControllerTrackEventAction = {
type: `AnalyticsController:trackEvent`;
Expand All @@ -21,7 +20,6 @@ export type AnalyticsControllerTrackEventAction = {
/**
* Identify a user for analytics.
*
* @param userId - The user identifier (e.g., metametrics ID)
* @param traits - User traits/properties
*/
export type AnalyticsControllerIdentifyAction = {
Expand All @@ -30,46 +28,50 @@ export type AnalyticsControllerIdentifyAction = {
};

/**
* Track a page view.
* Track a page or screen view.
*
* @param pageName - The name of the page
* @param properties - Page properties
* @param name - The identifier/name of the page or screen being viewed (e.g., "home", "settings", "wallet")
* @param properties - Optional properties associated with the view
*/
export type AnalyticsControllerTrackPageAction = {
type: `AnalyticsController:trackPage`;
handler: AnalyticsController['trackPage'];
export type AnalyticsControllerTrackViewAction = {
type: `AnalyticsController:trackView`;
handler: AnalyticsController['trackView'];
};

/**
* Enable analytics tracking.
* Opt in to analytics for regular account.
* This updates the user's opt-in status for regular account.
*/
export type AnalyticsControllerEnableAction = {
type: `AnalyticsController:enable`;
handler: AnalyticsController['enable'];
export type AnalyticsControllerOptInForRegularAccountAction = {
type: `AnalyticsController:optInForRegularAccount`;
handler: AnalyticsController['optInForRegularAccount'];
};

/**
* Disable analytics tracking.
* Opt out of analytics for regular account.
* This updates the user's opt-in status for regular account.
*/
export type AnalyticsControllerDisableAction = {
type: `AnalyticsController:disable`;
handler: AnalyticsController['disable'];
export type AnalyticsControllerOptOutForRegularAccountAction = {
type: `AnalyticsController:optOutForRegularAccount`;
handler: AnalyticsController['optOutForRegularAccount'];
};

/**
* Opt in to analytics.
* Opt in to analytics for social account.
* This updates the user's opt-in status for social account.
*/
export type AnalyticsControllerOptInAction = {
type: `AnalyticsController:optIn`;
handler: AnalyticsController['optIn'];
export type AnalyticsControllerOptInForSocialAccountAction = {
type: `AnalyticsController:optInForSocialAccount`;
handler: AnalyticsController['optInForSocialAccount'];
};

/**
* Opt out of analytics.
* Opt out of analytics for social account.
* This updates the user's opt-in status for social account.
*/
export type AnalyticsControllerOptOutAction = {
type: `AnalyticsController:optOut`;
handler: AnalyticsController['optOut'];
export type AnalyticsControllerOptOutForSocialAccountAction = {
type: `AnalyticsController:optOutForSocialAccount`;
handler: AnalyticsController['optOutForSocialAccount'];
};

/**
Expand All @@ -78,8 +80,8 @@ export type AnalyticsControllerOptOutAction = {
export type AnalyticsControllerMethodActions =
| AnalyticsControllerTrackEventAction
| AnalyticsControllerIdentifyAction
| AnalyticsControllerTrackPageAction
| AnalyticsControllerEnableAction
| AnalyticsControllerDisableAction
| AnalyticsControllerOptInAction
| AnalyticsControllerOptOutAction;
| AnalyticsControllerTrackViewAction
| AnalyticsControllerOptInForRegularAccountAction
| AnalyticsControllerOptOutForRegularAccountAction
| AnalyticsControllerOptInForSocialAccountAction
| AnalyticsControllerOptOutForSocialAccountAction;
Loading
Loading