Skip to content

Migrate BrandDesignUpdatePageViewModel onto LinearOnboardingOrchestrator#8845

Merged
LukasPaczos merged 4 commits into
developfrom
feature/lpaczos/brand-design-view-model-orchestrator
Jun 17, 2026
Merged

Migrate BrandDesignUpdatePageViewModel onto LinearOnboardingOrchestrator#8845
LukasPaczos merged 4 commits into
developfrom
feature/lpaczos/brand-design-view-model-orchestrator

Conversation

@LukasPaczos

@LukasPaczos LukasPaczos commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Task/Issue URL: https://app.asana.com/1/137249556945/project/1208671518894266/task/1215622484916139?focus=true
Tech Design URL (if applicable): https://app.asana.com/1/137249556945/project/481882893211075/task/1214772963645835

Description

Stacked on #8840. This change moves the new-user linear onboarding flow onto that orchestrator, behind a kill switch, with no behaviour change.

It composes the new-user flow as a plan in NewUserOnboardingPlanProvider (the ordered steps, their preconditions, side effects, and the skip / quick-setup side plans), starts it from LaunchViewModel via NewUserOnboardingPlanBootstrapper, and routes BrandDesignUpdatePageViewModel through it. The page ViewModel picks one of two flow strategies at construction:

  • OrchestratorFlow translates fragment callbacks into orchestrator events and renders the current step.
  • LegacyFlow keeps the existing in-ViewModel state machine unchanged.

The linearOnboardingOrchestrator feature flag selects between them (enabled by default). Intra-dialog interactions (quick-setup rows, widgets, notification callbacks) stay in the ViewModel in both modes.

Pixels were verified to match across the base, skip, and skip-then-resume, quick-setup, and sync restore scenarios for three builds: the pre-change baseline, flag off, and flag on.

Steps to test this PR

Base flow via orchestrator

  • Clean install
  • Launch the app
  • Go through linear onboarding
    • Verify all steps are present and transition correctly
  • Reach the browser
    • Verify the in-context CTAs show

Skip->return flow via orchestrator

  • Clean install
  • Launch the app
  • Click "I've been here before"
  • Click "Show Tutorial"
    • Verify browser comparison step is shown
  • Go through linear onboarding
    • Verify all steps are present and transition correctly.
  • Reach the browser
    • Verify the in-context CTAs show

Skip flow via orchestrator

  • Clean install
  • Launch the app
  • Click "I've been here before"
  • Click "Start browsing"
  • Reach the browser
    • Verify the native input box is visible and focused

Restore sync flow via orchestrator

  • Enable sync on the device
  • Uninstall the app (don't clear data)
  • Launch the app
    • Verify you see the "Let's pick up where you left off" screen

Quick setup flow via orchestrator
Apply below diff:

diff --git a/app/src/main/java/com/duckduckgo/app/onboardingquicksetup/OnboardingQuickSetupExperimentManager.kt b/app/src/main/java/com/duckduckgo/app/onboardingquicksetup/OnboardingQuickSetupExperimentManager.kt
index b4ddba2126..a70907c829 100644
--- a/app/src/main/java/com/duckduckgo/app/onboardingquicksetup/OnboardingQuickSetupExperimentManager.kt
+++ b/app/src/main/java/com/duckduckgo/app/onboardingquicksetup/OnboardingQuickSetupExperimentManager.kt
@@ -45,6 +45,7 @@ class OnboardingQuickSetupExperimentManagerImpl @Inject constructor(
 ) : OnboardingQuickSetupExperimentManager {
 
     override suspend fun enroll(): QuickSetupExperimentVariant? = withContext(dispatcherProvider.io()) {
+        return@withContext QuickSetupExperimentVariant.TREATMENT
         toggles.onboardingQuickSetupExperimentJun3().enroll()
         when {
             toggles.onboardingQuickSetupExperimentJun3().isEnrolledAndEnabled(QuickSetupCohorts.TREATMENT) -> {
  • Clear data (to get rid of sync restore)
  • Launch the app
  • Click "I've been here before"
    • Verify Quick setup page is visible
  • Change address bar position to bottom and start browsing
    • Verify the bottom option is applied

Apply below diff:

diff --git a/app/src/main/java/com/duckduckgo/app/onboarding/LinearOnboardingOrchestratorFeature.kt b/app/src/main/java/com/duckduckgo/app/onboarding/LinearOnboardingOrchestratorFeature.kt
index 89716bbfda..1eb455e745 100644
--- a/app/src/main/java/com/duckduckgo/app/onboarding/LinearOnboardingOrchestratorFeature.kt
+++ b/app/src/main/java/com/duckduckgo/app/onboarding/LinearOnboardingOrchestratorFeature.kt
@@ -31,6 +31,6 @@ import com.duckduckgo.feature.toggles.api.Toggle.DefaultFeatureValue
     featureName = "linearOnboardingOrchestrator",
 )
 interface LinearOnboardingOrchestratorFeature {
-    @Toggle.DefaultValue(DefaultFeatureValue.TRUE)
+    @Toggle.DefaultValue(DefaultFeatureValue.FALSE)
     fun self(): Toggle
 }

Base flow via VM

  • Clean install
  • Launch the app
  • Go through linear onboarding
    • Verify all steps are present and transition correctly
  • Reach the browser
    • Verify the in-context CTAs show

Skip->return flow via VM

  • Clean install
  • Launch the app
  • Click "I've been here before"
  • Click "Show Tutorial"
    • Verify browser comparison step is shown
  • Go through linear onboarding
    • Verify all steps are present and transition correctly.
  • Reach the browser
    • Verify the in-context CTAs show

Skip flow via VM

  • Clean install
  • Launch the app
  • Click "I've been here before"
  • Click "Start browsing"
  • Reach the browser
    • Verify the native input box is visible and focused

Restore sync flow via VM

  • Enable sync on the device
  • Uninstall the app (don't clear data)
  • Launch the app
    • Verify you see the "Let's pick up where you left off" screen

Quick setup flow via VM
Apply below diff:

diff --git a/app/src/main/java/com/duckduckgo/app/onboardingquicksetup/OnboardingQuickSetupExperimentManager.kt b/app/src/main/java/com/duckduckgo/app/onboardingquicksetup/OnboardingQuickSetupExperimentManager.kt
index b4ddba2126..a70907c829 100644
--- a/app/src/main/java/com/duckduckgo/app/onboardingquicksetup/OnboardingQuickSetupExperimentManager.kt
+++ b/app/src/main/java/com/duckduckgo/app/onboardingquicksetup/OnboardingQuickSetupExperimentManager.kt
@@ -45,6 +45,7 @@ class OnboardingQuickSetupExperimentManagerImpl @Inject constructor(
 ) : OnboardingQuickSetupExperimentManager {
 
     override suspend fun enroll(): QuickSetupExperimentVariant? = withContext(dispatcherProvider.io()) {
+        return@withContext QuickSetupExperimentVariant.TREATMENT
         toggles.onboardingQuickSetupExperimentJun3().enroll()
         when {
             toggles.onboardingQuickSetupExperimentJun3().isEnrolledAndEnabled(QuickSetupCohorts.TREATMENT) -> {
  • Clear data (to get rid of sync restore)
  • Launch the app
  • Click "I've been here before"
    • Verify Quick setup page is visible
  • Change address bar position to bottom and start browsing
    • Verify the bottom option is applied

UI changes

No changes expected.


Note

Medium Risk
First-run onboarding is a critical path with a large dual-path refactor; rollback relies on the remote flag, and orchestrator mode intentionally differs from legacy in at least one default-browser edge case covered by tests.

Overview
New-user onboarding is wired to the shared LinearOnboardingOrchestrator behind remote toggle linearOnboardingOrchestrator (on by default). When enabled, LaunchViewModel starts the run via NewUserOnboardingPlanBootstrapper before opening OnboardingActivity; when disabled, behavior stays on the existing in-VM flow.

The PR adds NewUserOnboardingPlanProvider, which defines the full linear plan (intro, notifications, sync restore / reinstall / initial forks, comparison chart, default browser, address bar, input mode + preview, skip and quick-setup side plans), step events, pixels, and completion results (launch search/chat). BrandDesignUpdatePageViewModel picks OrchestratorFlow or LegacyFlow at construction: orchestrator mode maps UI callbacks to events and renders resolveDialog() output; legacy mode keeps the prior state machine unchanged.

OnboardingViewModel and OnboardingActivity defer terminal AppStage / skip writes and dev-skip navigation when the orchestrator is driving the run. onboarding-api / onboarding-impl are added as app dependencies; lint baselines record temporary *-impl imports in tests and orchestrator code.

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

@LukasPaczos

Copy link
Copy Markdown
Contributor Author

bugbot run

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit c5865a4. Configure here.

@LukasPaczos LukasPaczos force-pushed the feature/lpaczos/brand-design-view-model-orchestrator branch from c5865a4 to 5d1f335 Compare June 11, 2026 11:23
@LukasPaczos LukasPaczos marked this pull request as ready for review June 11, 2026 11:23

@catalinradoiu catalinradoiu 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.

Looks good and works as expected! 👍

Base automatically changed from feature/lpaczos/linear-onboarding-orchestrator to develop June 15, 2026 11:12
@LukasPaczos LukasPaczos force-pushed the feature/lpaczos/brand-design-view-model-orchestrator branch from 3db4691 to 396d5c0 Compare June 15, 2026 11:17
@LukasPaczos LukasPaczos force-pushed the feature/lpaczos/brand-design-view-model-orchestrator branch from 396d5c0 to d92688f Compare June 15, 2026 12:08
@LukasPaczos LukasPaczos force-pushed the feature/lpaczos/brand-design-view-model-orchestrator branch from 0b3d336 to aaa4c4f Compare June 15, 2026 18:54

@cursor cursor 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.

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

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 aaa4c4f. Configure here.

…and-design-view-model-orchestrator

# Conflicts:
#	app/src/main/java/com/duckduckgo/app/launch/LaunchViewModel.kt
#	app/src/main/java/com/duckduckgo/app/onboarding/ui/OnboardingViewModel.kt
#	app/src/test/java/com/duckduckgo/app/onboarding/ui/OnboardingViewModelTest.kt
@LukasPaczos LukasPaczos merged commit 02ce923 into develop Jun 17, 2026
14 checks passed
@LukasPaczos LukasPaczos deleted the feature/lpaczos/brand-design-view-model-orchestrator branch June 17, 2026 07:59
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.

3 participants