feat(analytics): screen.viewed consolidation (#2883)#937
Open
piyalbasu wants to merge 8 commits into
Open
Conversation
…n helpers Introduce the single canonical screen-view event for Slice B (#2883): - AnalyticsEvent.SCREEN_VIEWED = "screen.viewed" - AnalyticsFlow enum + ScreenViewedProps type - deriveScreenName(): deterministic legacy-string -> slug - SCREEN_METADATA: per-screen flow/step catalog keyed by legacy string - buildScreenViewedProps() / getScreenViewedProps() / isScreenViewEvent() The VIEW_* members are retained as the legacy-string catalog that screen_name derives from; their values become catalog keys only and are no longer emitted. Route-mapping logic (transformRouteToEventName / CUSTOM_ROUTE_MAPPINGS / processRouteForAnalytics) is unchanged and still resolves the legacy string. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CDXpJqihDLf2yE3Ur7UaUT
…ints Hard cutover for Slice B (#2883): every screen load now emits the single canonical screen.viewed event instead of a distinct "loaded screen: X" event. - useNavigationAnalytics: route -> buildScreenViewedProps -> SCREEN_VIEWED - BottomSheet: retarget legacy screen analyticsEvent props to SCREEN_VIEWED (all 12 manual screen-view call sites flow through this one component, two via SignTransactionDetails which forwards here). Non-screen events pass through unchanged. surface is supplied by the Slice-A common context (getSurface()). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CDXpJqihDLf2yE3Ur7UaUT
- analyticsConfig.test.ts: deriveScreenName determinism, isScreenViewEvent, buildScreenViewedProps (screen_name + flow + step), getScreenViewedProps retarget/passthrough, and the route path feeding screen.viewed. - core.test.ts: emission asserts name=screen.viewed with screen_name/flow/ surface (+ step for completion screens), and that NO legacy "loaded screen:" event is emitted for any catalog screen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CDXpJqihDLf2yE3Ur7UaUT
Contributor
|
iOS Simulator preview build is ready: https://github.com/stellar/freighter-mobile/releases/tag/untagged-555f57df2961ceebc717 (SDF collaborators only — install instructions in the release description) |
piyalbasu
marked this pull request as ready for review
July 16, 2026 16:11
…undation' into feat/analytics-screen-viewed-slice-b # Conflicts: # src/services/analytics/core.test.ts
…ranch Removes local .claude settings and superpowers spec/plan working docs that an errant 'git add -A' committed. Untracked here only; files remain on disk.
Named VIEW_* screens now declare their screen_name in SCREEN_CATALOG (renamed from SCREEN_METADATA) instead of deriving it from the mutable display string at emit time — decoupling the analytics id from the UI copy. deriveScreenName stays only as the fallback for auto-mapped routes (transformRouteToEventName) not in the catalog, so the long tail still emits a non-empty screen_name. Values are unchanged; pure refactor. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Screens are now identified directly by their canonical screen_name: each VIEW_* enum member holds its screen_name as its value, the route transform (routeToScreenName, was transformRouteToEventName) yields a screen_name directly, and SCREEN_CATALOG is keyed by screen_name (holding only flow/step). Deletes deriveScreenName + LEGACY_SCREEN_PREFIX; isScreenViewEvent is now a catalog-membership check instead of a "loaded screen: " prefix sniff. No "loaded screen: X" string is emitted or used as a key anymore. Emitted screen_name values are unchanged; the 15 screen call-sites are untouched (they reference the enum members, whose values changed). Net -116 lines. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
AnalyticsEvent.SCREEN_VIEWED = "screen.viewed", anAnalyticsFlowenum, aScreenViewedPropstype, and helpers insrc/config/analyticsConfig.ts:deriveScreenName()— deterministic legacy-string → slug (strip"loaded screen: ", trim, lowercase, each run of non-alphanumeric chars → single_).SCREEN_METADATA— per-screenflow/stepcatalog keyed by the legacy string.buildScreenViewedProps(),getScreenViewedProps(),isScreenViewEvent().screen.viewed:src/hooks/useNavigationAnalytics.ts— route navigation.src/components/BottomSheet.tsx— screens presented as bottom sheets. All 12 manual screen-view call sites flow through this one component (2 viaSignTransactionDetails, which forwards toBottomSheet), so no call site changed. Non-screenanalyticsEventvalues pass through unchanged.transformRouteToEventName/CUSTOM_ROUTE_MAPPINGS/processRouteForAnalytics/ROUTE_TO_ANALYTICS_EVENT_MAP) unchanged; it still resolves a route to its legacy string, which is now fed intobuildScreenViewedPropsinstead of being emitted.__tests__/config/analyticsConfig.test.tsandsrc/services/analytics/core.test.ts.Why
The RFC (#2883) unifies mobile + extension analytics onto a shared schema. Per-screen
"loaded screen: X"events are high-cardinality and don't align cross-platform. A singlescreen.viewedevent with a deterministicscreen_name(identical legacy strings on both platforms ⇒ identical slugs) plusflow/surface/stepdimensions makes screen views funnel-able and consistent across surfaces.surfaceis supplied by the Slice-A common context (getSurface()), so it is not duplicated onto the event.Known limitations
screen_namevalues are mechanical, not catalog-polished (intentional, per the RFC task) — see the reconciliation checklist below.steptoday (send_payment_confirm/swap_confirm→confirm,send_payment_processing→processing). Other linear sub-steps carry nostep; the mechanism is in place to extend coverage if the RFC wants finer step tagging.Decisions, screen_name reconciliation checklist, and verification
Decisions (made autonomously — please sanity-check)
step, not bespoke events (as instructed).screen_nameuses the mechanical value for cross-platform determinism.VIEW_*enum members. They are retained as the legacy-string catalogscreen_namederives from and are still referenced as keys by the bottom-sheet/detail components andCUSTOM_ROUTE_MAPPINGS; their values are now catalog keys only and are never emitted. No member became fully dead, so none was removed (consistent with "remove only if unreferenced").surfacenot duplicated — added by the Slice-A common context;analyticsConfigcan't importgetSurface()fromcorewithout a circular dependency. Verified present on the emitted event in tests.unlock_account,security,show_recovery_phrase,import_secret_key→security; home/receive-QR/buy/token-management →assets; wallet management →settings.screen_namereconciliation checklist (reconcile each against the RFC canonical catalog)welcome— onboardingaccount_creator— onboardingmnemonic_phrase_alert— onboardingmnemonic_phrase— onboardingconfirm_mnemonic_phrase— onboardingrecover_account— onboardingunlock_account— securityaccount— assetsaccount_history— historydiscover— discoveryasset_detail— assetsview_public_key_generator— assetsgrant_access— signingsign_transaction— signingsign_transaction_details— signingsign_auth_entry_details— signingsend_payment_to— sendsend_payment_amount— sendsend_payment_settings— sendsend_payment_fee— sendsend_payment_timeout— sendsend_payment_confirm— send · stepconfirmsend_transaction_details— sendsend_payment_processing— send · stepprocessingswap— swapswap_amount— swapswap_fee— swapswap_slippage— swapswap_timeout— swapswap_settings— swapswap_confirm— swap · stepconfirmswap_transaction_details— swapsettings— settingspreferences— settingsmanage_network— settingsnetwork_settings— settingsleave_feedback— settingsabout— settingssecurity— securityshow_recovery_phrase— securitymanage_connected_apps— settingsmanage_assets— assetsadd_asset— assetsremove_asset— assetsmanage_wallets— settingsimport_secret_key— securityadd_fund— assetssearch_asset— assetsadd_asset_manually— assets49 screens total; every screen is assigned a flow.
Verification
jest src/services/analytics/core.test.ts __tests__/config/analyticsConfig.test.ts→ 40 passedjest __tests__/services/Analytics.test.ts→ passed (legacyVIEW_*enum still exported)jeston BottomSheet-affected components (SimpleBalancesList,SendReviewBottomSheet,TransactionAmountScreen,SendCollectibleReview) → 58 passed (no regressions from the choke-point change)tsc --noEmit(yarn lint:ts) → cleaneslint --fix+prettier --writeon all changed files → cleanChecklist
PR structure
Testing
Release
screen.viewed); dashboards/funnels keyed on"loaded screen: X"must migrate. Coordinated via the #2883 RFC.🤖 Generated with Claude Code
https://claude.ai/code/session_01CDXpJqihDLf2yE3Ur7UaUT
Generated by Claude Code