Skip to content

feat: update notification-services-controller to support new Segment schema#8944

Open
zelkibuilds wants to merge 9 commits into
mainfrom
feat/update-notifications-controller
Open

feat: update notification-services-controller to support new Segment schema#8944
zelkibuilds wants to merge 9 commits into
mainfrom
feat/update-notifications-controller

Conversation

@zelkibuilds
Copy link
Copy Markdown

@zelkibuilds zelkibuilds commented Jun 1, 2026

Explanation

Implements the notification-services-controller portion of the notifications-analytics rework (PLAN §5): push events expose first-class analytics fields, and the in-app model gains a notification_subtype.

  • §5.1 — Push parsing. web/push-utils.ts now reads the top-level FCM fields via toPushAnalyticsPayload instead of JSON-parsing data["data"] behind the isOnChainRawNotification gate. All push kinds (on-chain/platform/snap/feature-announcement) flow through one path; the §5.5 banner-display gap goes away.
  • §5.2 — Push event payload. Added PushAnalyticsPayload (notification_id, notification_type, notification_subtype, profile_id?, chain_id?, deeplink?). Both push events (onNewNotifications, pushNotificationClicked) now carry [PushAnalyticsPayload]; createSubscribeToPushNotifications handler signatures updated to match.
  • §5.3 — In-app subtype (model enrich). Added a required notification_subtype field to BaseNotification/INotification, populated in the processors via a new getNotificationSubtype(n) helper (on-chain kind / snap / features_announcement / platform fallback). Type-checking guarantees every processed notification carries it. id/type are
    unchanged.
  • §5.5 — Removed the metadata parsing path and the nested blob.
  • Inbox-on-push. The push payload no longer carries the notification body, so the onNewNotifications subscriber now re-fetches via fetchAndUpdateMetamaskNotifications() instead of inserting the payload.

Open questions

  • Push payload shape (replace vs. augment). Per §4.4 the backend no longer ships the raw body in the FCM map, so the two push events now carry a flat PushAnalyticsPayload instead of INotification — a breaking change. Confirm a flat replacement was intended (not the 3 notification_* fields added onto the notification object).
  • In-app model fields. We added notification_subtype to BaseNotification. Deferred: renaming id → notification_id (redundant with id) and adding a first-class notification_type (collides with the existing structural notification_type on API variants, and its value is unresolved — §2 says wallet_activity, §6.6 uses item.type). Confirm
    the desired end-state.
  • Snap IDs. Snaps still get a client-generated uuid() in processSnapNotification — keep client-side or move upstream?
  • Platform notification_subtype not returned by the inbox API (§4.3). The backend stores/stamps it (PR Enable and fix require-unicode-regexp issues #275 merged) but /api/v3/notifications doesn't expose it (verified against notify-notification-services@main), so the helper falls back to type (platform). Needs a follow-up to add it to the v3 response, then we regenerate schema.ts and flip
    the TODO.
  • Feature-announcement subtype. Using features_announcement as the single value; per-campaign id pending the announcements team.

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

High Risk
Breaking messenger and handler contracts require coordinated extension/mobile updates; push-driven inbox behavior now depends on API refetch instead of inline payload insertion.

Overview
Aligns notification-services-controller with the notifications analytics rework: push paths expose first-class Segment fields, and in-app notifications carry a normalized notification_subtype.

Push (breaking). onNewNotifications and pushNotificationClicked (and createSubscribeToPushNotifications handlers) now emit PushAnalyticsPayload (notification_id, notification_type, notification_subtype, optional profile_id, chain_id, deeplink) instead of INotification. Web push reads top-level FCM data via toPushAnalyticsPayload; the nested data["data"] / on-chain JSON parsing path is removed. Because FCM no longer includes the full body, the inbox subscriber re-fetches with fetchAndUpdateMetamaskNotifications() instead of inserting from the push payload.

In-app model. notification_subtype is required on BaseNotification / INotification, set in API, feature-announcement, and snap processors via shared getNotificationSubtype. getNotificationSubtype and toPushAnalyticsPayload are exported for clients.

Reviewed by Cursor Bugbot for commit 81b5f29. Bugbot is set up for automated code reviews on this repo. Configure here.

@zelkibuilds zelkibuilds force-pushed the feat/update-notifications-controller branch from 9dbca22 to 9811cb9 Compare June 1, 2026 11:48
@zelkibuilds zelkibuilds force-pushed the feat/update-notifications-controller branch from e51bf05 to 16656ed Compare June 1, 2026 12:12
export function getNotificationSubtype(notification: INotification): string {
switch (notification.type) {
case TRIGGER_TYPES.FEATURES_ANNOUNCEMENT:
// §5.3 calls for a stable per-campaign id here, pending confirmation from
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs validation

if (notification.notification_type === 'on-chain') {
return notification.payload.data.kind;
}
// Platform: §5.3 wants the server-set `notification_subtype`. It is
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs validation

@zelkibuilds zelkibuilds marked this pull request as ready for review June 3, 2026 08:44
@zelkibuilds zelkibuilds requested review from a team as code owners June 3, 2026 08:44
Copy link
Copy Markdown
Author

@zelkibuilds zelkibuilds left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some open questions in PR description + code

// re-fetch the inbox from the API to surface the new notification.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.updateMetamaskNotificationsList(notification);
this.fetchAndUpdateMetamaskNotifications();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Snap subtype not backfilled

Medium Severity

fetchAndUpdateMetamaskNotifications reuses existing snap rows from state as-is, so persisted snap notifications never gain the new required notification_subtype field unless they are replaced by a fresh processSnapNotification path.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b1c77d8. Configure here.

notification_type: data.notification_type,
notification_subtype: data.notification_subtype ?? '',
profile_id: data.profile_id || undefined,
chain_id: data.chain_id ? Number(data.chain_id) : undefined,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid chain_id becomes NaN

Low Severity

toPushAnalyticsPayload sets chain_id with Number(data.chain_id) whenever the string is truthy, so malformed FCM values can produce NaN on the analytics payload instead of omitting the field.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b1c77d8. Configure here.

@zelkibuilds zelkibuilds force-pushed the feat/update-notifications-controller branch from b1c77d8 to 81b5f29 Compare June 3, 2026 10:38
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 81b5f29. Configure here.

notification_type: data.notification_type,
notification_subtype: data.notification_subtype ?? '',
profile_id: data.profile_id || undefined,
chain_id: data.chain_id ? Number(data.chain_id) : undefined,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid chain_id becomes NaN

Low Severity

toPushAnalyticsPayload sets chain_id with Number(data.chain_id) whenever the FCM string is truthy, without checking the result. A non-numeric chain_id value becomes NaN, which still satisfies the optional number type but breaks downstream analytics or routing that expect a valid chain id.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 81b5f29. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant