Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions packages/analytics-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Add independent marketing consent and classify named events by lane ([#10232](https://github.com/MetaMask/core/pull/10232))
- New state and methods: `optedInToMarketing`, `optInToMarketing` / `optOutOfMarketing` / `resetMarketingConsentDecision`, and a persisted `marketingEventNames` list (remote loading arrives in a later phase)
- Named `track` / `view` payloads stamp `context.marketing` (`true` or `false`) at capture so Segment can tell marketing events from product events
- Queues and fragments follow that lane. A fragment that declares both marketing and product event names is treated as marketing

### Changed

- Bump `uuid` from `^8.3.2` to `^9.0.1` ([#10117](https://github.com/MetaMask/core/pull/10117))
Expand Down
22 changes: 16 additions & 6 deletions packages/analytics-controller/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@ The AnalyticsController provides a unified interface for tracking analytics even

## State

| Field | Type | Description | Persisted |
| ---------------- | --------- | --------------------------------------------- | --------- |
| `analyticsId` | `string` | UUIDv4 identifier (client platform-generated) | Yes |
| `optedIn` | `boolean` | User opt-in status | Yes |
| `eventQueue` | `object` | Optional persisted delivery queue | Yes |
| `eventFragments` | `object` | Optional in-progress event fragments | Yes |
| Field | Type | Description | Persisted |
| ------------------------------ | ---------- | ------------------------------------------------------------ | --------- |
| `analyticsId` | `string` | UUIDv4 identifier (client platform-generated) | Yes |
| `optedIn` | `boolean` | Product analytics opt-in status | Yes |
| `consentDecisionMade` | `boolean` | Whether a product consent decision has been made | Yes |
| `optedInToMarketing` | `boolean` | Marketing analytics opt-in status | Yes |
| `marketingConsentDecisionMade` | `boolean` | Whether a marketing consent decision has been made | Yes |
| `marketingEventNames` | `string[]` | Cached marketing event names (empty until a source is wired) | Yes |
| `eventQueue` | `object` | Optional persisted delivery queue | Yes |
| `eventFragments` | `object` | Optional in-progress event fragments | Yes |

### Client Platform Responsibilities

Expand All @@ -30,6 +34,12 @@ The AnalyticsController provides a unified interface for tracking analytics even
3. **Subscribe to state changes**: Persist changes to isolated storage
4. **Persist to isolated storage**: Keep analytics settings separate from main state (protects against state corruption)

Named events in `marketingEventNames` are governed only by `optedInToMarketing`. Every other named payload is governed only by `optedIn`. Queues, fragments, and delivery use the same machinery for both lanes. `identify` has no event name, so it follows `optedIn`.

Until a later phase loads marketing event names from a remote source, `marketingEventNames` stays empty unless the client seeds or persists a list. With an empty list, every named event is treated as product, so marketing consent has no classification impact yet.

Named `track` and `view` payloads are classified once at capture. That lane is stamped on `context.marketing` (`true` or `false`) so a Segment source can tell marketing events from product events without reading properties. Queues and fragments then follow the stamp. `identify` does not set this flag. Destinations should treat a missing `context.marketing` as product, since older app versions never send the field.

## Anonymous Events Feature

When `isAnonymousEventsFeatureEnabled` is enabled in the constructor, events with sensitive properties are split into separate events:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,39 @@ export type AnalyticsControllerResetConsentDecisionAction = {
handler: AnalyticsController['resetConsentDecision'];
};

/**
* Opt in to marketing analytics.
*
* Independent of {@link optIn}. Replays queued marketing events.
*
* @returns A promise that resolves once opt-in processing has completed.
*/
export type AnalyticsControllerOptInToMarketingAction = {
type: `AnalyticsController:optInToMarketing`;
handler: AnalyticsController['optInToMarketing'];
};

/**
* Opt out of marketing analytics.
*
* Independent of {@link optOut}. Discards queued marketing events and
* marketing event fragments.
*/
export type AnalyticsControllerOptOutOfMarketingAction = {
type: `AnalyticsController:optOutOfMarketing`;
handler: AnalyticsController['optOutOfMarketing'];
};

/**
* Reset the marketing consent decision back to undecided.
*
* Independent of {@link resetConsentDecision}.
*/
export type AnalyticsControllerResetMarketingConsentDecisionAction = {
type: `AnalyticsController:resetMarketingConsentDecision`;
handler: AnalyticsController['resetMarketingConsentDecision'];
};

/**
* Union of all AnalyticsController action types.
*/
Expand All @@ -207,4 +240,7 @@ export type AnalyticsControllerMethodActions =
| AnalyticsControllerFinalizeEventFragmentAction
| AnalyticsControllerOptInAction
| AnalyticsControllerOptOutAction
| AnalyticsControllerResetConsentDecisionAction;
| AnalyticsControllerResetConsentDecisionAction
| AnalyticsControllerOptInToMarketingAction
| AnalyticsControllerOptOutOfMarketingAction
| AnalyticsControllerResetMarketingConsentDecisionAction;
Loading