Skip to content

feat: align checkpoint tracking with download-first onboarding redesign#387

Open
juanmahidalgo wants to merge 2 commits into
mainfrom
feat/checkpoints-redesign
Open

feat: align checkpoint tracking with download-first onboarding redesign#387
juanmahidalgo wants to merge 2 commits into
mainfrom
feat/checkpoints-redesign

Conversation

@juanmahidalgo

Copy link
Copy Markdown
Contributor

Onboarding redesign — Download First (auth)

Summary

Aligns the auth UI with the new 3-checkpoint model. The auth page is now responsible only for CP2 (authenticated): reached on LoginPage mount and completed after each auth method (Magic / Metamask / Email OTP) succeeds. CP3/CP4 tracking (profile creation, avatar setup) is removed — the new model leaves those out of the funnel.

Why

The old code tracked CP3 and CP4 from QuickSetupPage, SetupPage, and AvatarSetupPage, plus a CP2 reached at multiple inconsistent points (post-OAuth, post-Metamask connect, post-Email-OTP send). With the redesigned funnel — CP1 download / CP2 auth / CP3 in-world — auth only owns CP2. Identity through the funnel is anchored on the Segment anonymousId (shared with landing on decentraland.org), so callers no longer need to compute userIdentifier manually: trackCheckpoint defaults to it. markReturningUser is gone because explicit CP2 completed calls already cover that case.

Changes

File Why
src/shared/onboarding/trackCheckpoint.ts Defaults userIdentifier to analytics.user().anonymousId() with identifierType='anon'. Widened types: identifierType ∈ {anon,email,wallet}, source ∈ {auth,landing,explorer}.
src/components/Pages/LoginPage/LoginPage.tsx CP2 reached on mount (one-shot useEffect). CP2 completed after Metamask connects and after Email OTP verifies. Removed legacy CP2 reached on email-submit. Dropped markReturningUser import.
src/components/Pages/LoginPage/AutoLoginRedirect.tsx CP2 completed on auto-login Metamask success (replaces old CP2 reached). Dropped markReturningUser.
src/components/Pages/CallbackPage/CallbackPage.tsx CP2 completed on Magic / social OAuth return — payload enriched with wallet and email (when available). Dropped markReturningUser.
src/components/Pages/QuickSetupPage/QuickSetupPage.tsx Removed CP3 completed call. Removed unused trackCheckpoint import.
src/components/Pages/SetupPage/SetupPage.tsx Removed CP3 reached + completed calls. Removed unused import.
src/components/Pages/AvatarSetupPage/AvatarSetupPage.tsx Removed CP3 completed, CP4 reached, CP4 completed. Removed unused imports.
src/shared/onboarding/markReturningUser.ts (deleted) Function only fired CP2 completed events that are now emitted explicitly per auth flow.
src/shared/onboarding/markReturningUser.spec.ts (deleted) Tests for the removed function.
src/components/Pages/LoginPage/LoginPage.spec.tsx Removed the obsolete jest.mock('../../../shared/onboarding/markReturningUser', …).

Test plan

  • pnpm test — affected suites pass (LoginPage, CallbackPage, AvatarSetupPage, QuickSetupPage).
  • Manual: open LoginPage in a fresh browser → Segment receives Onboarding Checkpoint checkpointId=2, action=reached, identifierType=anon, userIdentifier=<ajs_anonymous_id>.
  • Magic login (Google) → Segment receives CP2 completed with wallet and email populated; same anonymousId.
  • Metamask login → CP2 completed with wallet, no email.
  • Email OTP login → CP2 completed with wallet and email.
  • Verify in auth-server that CP2 row has completed_at IS NOT NULL and wallet/email are filled by COALESCE.

Related PRs

  • auth-serverfeat/onboarding-redesign-download-first (schema, validators, nudges).
  • events-notifierfeat/onboarding-redesign-download-first (Segment → auth-server bridge).
  • landing-sitefeat/onboarding-redesign-download-first (CP1 landing flow).

@vercel

vercel Bot commented Apr 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
auth Ready Ready Preview, Comment Apr 27, 2026 2:31pm

Request Review

@decentraland-bot decentraland-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review — PR #387

feat: align checkpoint tracking with download-first onboarding redesign

Summary

Clean refactor that narrows auth's checkpoint ownership to CP2 only, removes CP3/CP4 tracking, deletes the now-redundant markReturningUser, and defaults userIdentifier to the Segment anonymousId. All CI checks pass (tests, e2e, audit, lint). The architecture is sound — auth owns only its checkpoint, and the anonymousId-based identity stitching across landing/auth/explorer is well-designed.

ADR-6 Conventions ✅

  • PR title: feat: align checkpoint tracking with download-first onboarding redesign — correct format
  • Branch: feat/checkpoints-redesign — correct prefix

Security Review ✅

  • No hardcoded secrets or credentials
  • No new PII exposure (wallet/email sent to Segment is existing behavior)
  • No XSS vectors introduced
  • No auth/authz changes
  • anonymousId usage is standard Segment practice
  • No security issues found

Findings

[P2 — Missing unit tests for new trackCheckpoint logic]
trackCheckpoint.ts now has meaningful business logic: safeAnonymousId() extraction, defaulting userIdentifier/identifierType, silent event dropping when no identifier is available, and deferred firing via trackCheckpointWhenReady. There are no dedicated unit tests for this module. The deleted markReturningUser.spec.ts tested the old indirection, but nothing tests the new defaulting/fallback behavior directly. Consider adding tests for: (a) anonymousId used when no explicit identifier is passed, (b) event dropped when analytics isn't available, (c) trackCheckpointWhenReady defers correctly.

[P2 — Double getAnalytics() call]
trackCheckpoint() calls getAnalytics() (line 42) and then safeAnonymousId() calls it again (line 22). Minor redundancy — could pass the analytics instance through or extract it once at the top of trackCheckpoint.

[P2 — Silent event dropping may complicate funnel debugging]
When anonymousId isn't available and no explicit identifier is passed, the event is silently dropped (line 49). This is documented in the JSDoc and intentional, but if Segment is slow to initialize, early CP2-reached events could be lost. The trackCheckpointWhenReady wrapper mitigates this for the mount case, but a console.debug in development builds could help surface dropped events during debugging.

[P2 — YAGNI: 'landing' | 'explorer' source types]
The widened source union includes 'landing' and 'explorer', neither of which is used in this PR. Pragmatic for the coordinated multi-repo rollout mentioned in the description, so not blocking — just noting.

Verdict: ✅ APPROVE

No P0 or P1 issues. The changes are well-structured, thoroughly commented, and correctly implement the new checkpoint model. All four CP2-completed paths are covered (Metamask, Email OTP, auto-login, social/OAuth), markReturningUser deletion is safe (zero remaining references), and CI is fully green.


Reviewed by Jarvis 🤖 · Requested by Juan Manuel Hidalgo via Slack

*/
function trackCheckpoint(params: CheckpointParams): void {
const analytics = getAnalytics()
if (!analytics) return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P2] Minor: getAnalytics() is called here and again inside safeAnonymousId() (line 22). You could extract it once and pass it through to avoid the double call:

function trackCheckpoint(params: CheckpointParams): void {
  const analytics = getAnalytics()
  if (!analytics) return

  const anonymousId = safeAnonymousId(analytics) // pass it in
  // ...
}

const userIdentifier = params.userIdentifier ?? anonymousId
const identifierType = params.identifierType ?? (anonymousId ? 'anon' : undefined)

if (!userIdentifier || !identifierType) return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P2] Suggestion: Silent drops here could make it harder to debug funnel gaps. Consider adding a console.debug('[trackCheckpoint] dropped — no identifier available', params) in development builds so dropped events are visible during testing.

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.

2 participants