Track marketing notification opens - #6067
Conversation
Recognize a marketing push payload (type 'marketing' with a campaignId and optional deep-link url) in parsePushMessage. When the user opens the app from one, report a Marketing_Notification_Opened event carrying the campaignId to analytics, and navigate to the deep link when present. Unrecognized or malformed urls degrade to track-only. Includes a parser unit test.
b4eafc8 to
5d8b895
Compare
f149ce7 to
b8a1392
Compare
b8a1392 to
ff38b7b
Compare
| const MARKETING_SCENES: ReadonlySet<string> = new Set([ | ||
| 'edgeTabs', | ||
| 'walletsTab' | ||
| ]) |
There was a problem hiding this comment.
Scene allow-list ignores nested screens
Medium Severity
isAllowedMarketingLink only checks a scene's sceneName, then handleLink passes the full query into navigate. An allowed edge://scene/edgeTabs URL can therefore set screen to devTab or extraTab. Those tabs are hidden in the menu (ENV.DEV_TAB / missing extraTab config) but remain registered, so a campaign can open the production debug scene or mount ExtraTabScene, which throws when config.extraTab is missing.
Reviewed by Cursor Bugbot for commit ff38b7b. Configure here.
ff38b7b to
83177c4
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 83177c4. Configure here.
A campaign URL exists to take the user to the place a campaign is talking about, so the filter names those places: the tab bar and its tabs, the wallet list, swap, the buy/sell quote scenes, earn, markets, promo-code activation, and the fund-account prompt. A URL pointing anywhere else — account actions like password recovery or edge login, payment flows, provider returns, scenes nobody listed — tracks the open without navigating. An allow-list fails closed: a new link type or scene stays unreachable from a push until someone adds it here, which is the right default for a URL that reaches every recipient of a campaign at once. Sends are already gated behind a marketer API key, so this guards operator error and key compromise, not outside attackers. A deep.edge.app attribution wrapper is judged by the destination it carries, so wrapping cannot smuggle a refused link through, and a campaign that asks for attribution keeps it.
83177c4 to
1cdb570
Compare


CHANGELOG
Does this branch warrant an entry to the CHANGELOG?
Dependencies
none
Requirements
If you have made any visual changes to the GUI. Make sure you have:
No visual changes — this is push-payload parsing plus navigation/analytics, no new UI.
Description
Recognizes a marketing push notification and, when the user opens the app from it, reports the campaign open to analytics and navigates to an optional deep link.
parsePushMessagenow recognizes a{ type: 'marketing', campaignId, url? }FCMdatapayload and returns a newMarketingLinkdeep-link type. An unrecognized or malformedurldegrades to track-only.Marketing_Notification_OpenedPostHog event carrying thecampaignId, then navigates to the optional deep link through the existing handler.PushMessageParserunit test.Verified with the unit test and end-to-end on a real Android device against a local push server: the open was tracked in PostHog and the deep link navigated.
The sending side lives in edge-push-server (companion PR EdgeApp/edge-push-server#78). This PR is the receiving side and is safe to merge on its own — it only acts on notifications that carry the marketing payload.
Note
Medium Risk
Push URLs can drive navigation for all campaign recipients; the allow-list mitigates operator error or compromised send keys, but expanding destinations or parser gaps could still route users to sensitive flows.
Overview
Marketing push notifications (
type: marketingwithcampaignIdand optionalurl) are now recognized when the user opens the app from the notification.parsePushMessagereturns a newMarketingLinkdeep-link type. Optional URLs are parsed through the existing deep-link parser; only allow-listed campaign destinations (home, wallets, earn, markets, swap, buy/sell quotes, promotions, fund-account modal) trigger navigation—sensitive or unlisted targets (recovery, edge-login, arbitrary scenes, provider returns) are track-only. Wrappeddeep.edge.appURLs are judged by their inner destination; malformed URLs still record the open.On handle,
DeepLinkingActionslogsMarketing_Notification_Openedto analytics withcampaignId, then navigates via the shared link handler when a permitted link was parsed.PushMessageParser.test.tscovers destinations, refusals, attribution wrappers, and existing price-change payloads.Reviewed by Cursor Bugbot for commit 1cdb570. Bugbot is set up for automated code reviews on this repo. Configure here.