Skip to content

[Config-driven linear onboarding dialogs] Port existing screens (forward flows) - #9403

Open
LukasPaczos wants to merge 19 commits into
developfrom
refactor/lpaczos/config-driven-linear-onboarding-dialogs/welcome-input-screens
Open

[Config-driven linear onboarding dialogs] Port existing screens (forward flows)#9403
LukasPaczos wants to merge 19 commits into
developfrom
refactor/lpaczos/config-driven-linear-onboarding-dialogs/welcome-input-screens

Conversation

@LukasPaczos

@LukasPaczos LukasPaczos commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Task/Issue URL: https://app.asana.com/1/137249556945/project/1208671518894266/task/1217183142109149?focus=true
Tech Design URL (if applicable): https://app.asana.com/1/137249556945/project/481882893211075/task/1216854264994244
API Proposals URL(s) (if applicable):

Description

Step 3 of the tech design: ports the remaining screens to the config-driven onboarding renderer. This PR covers below binders:

  • Welcome
  • Input selection
  • Input preview

Divergences from the legacy implementation (BrandDesignUpdateWelcomePage.kt):

  • The input screen's "with AI" Lottie flourish always starts after the 2s initial delay. Legacy's snapped render (rotation, process restore) started the loop immediately. This fixes an unintentional divergence.
  • The input preview's card resizes (mode switch, suggestion reveal) go through the render engine. Same visual, but the tween is suppressed while a snapped or skipped entrance is still landing the screen, where legacy always ran it:
input_preview_snap

Divergences from the tech design:

  • Shown pixels are a 1:1 port used only by the new view model, not the shared utility the TD proposed for both arms. Parity is guarded by the exhaustive mapping and tests instead; the legacy path is untouched, which keeps this PR flag-off safe. We can look into the alternative approach of moving these pixels into plan provider in a follow up, since we'd need to make changes to both old and new code paths.
  • BindScope gained animateCardBounds, which the TD didn't have. A binder only reaches its own layout, so it had no way to animate a card resize, if one was needed. The binder requests the tween but the engine owns it, so the request is safely ignored during snapped transitions.
  • DialogConfig gained a CardEntry param (Immediate / AfterBackgroundTransition). The welcome dialogs arrive while the intro background is still crossing over, and without it the card faded in over the moving background. The timing precisely matches legacy.

Steps to test this PR

  • Apply below patch:
diff --git a/app/src/main/java/com/duckduckgo/app/onboardingbranddesignupdate/OnboardingBrandDesignUpdateToggles.kt b/app/src/main/java/com/duckduckgo/app/onboardingbranddesignupdate/OnboardingBrandDesignUpdateToggles.kt
index b6a1e4d352..41a8d85ad4 100644
--- a/app/src/main/java/com/duckduckgo/app/onboardingbranddesignupdate/OnboardingBrandDesignUpdateToggles.kt
+++ b/app/src/main/java/com/duckduckgo/app/onboardingbranddesignupdate/OnboardingBrandDesignUpdateToggles.kt
@@ -58,6 +58,6 @@ interface OnboardingBrandDesignUpdateToggles {
     /**
      * Selects the config-driven renderer for the brand-design onboarding dialogs.
      */
-    @Toggle.DefaultValue(DefaultFeatureValue.FALSE)
+    @Toggle.DefaultValue(DefaultFeatureValue.TRUE)
     fun configDrivenDialogs(): Toggle
 }

base flow

  • Clean install.
  • Walk the full flow: welcome, comparison chart, address bar, input screen, input preview, — each renders with its background, Dax embellishment, card arrow and CTAs.
  • Rotate on each screen — the snapped re-render keeps in-progress state (address bar selection, switches, input mode).
  • Tap during a transition to skip animations.
  • Input preview: switch the search/AI tabs (card resizes smoothly), submit a typed query and a suggestion.

sync restore flow

  • Finish onboarding, go to settings, and enable sync on the device.
  • Uninstall the app (do not clear caches).
  • Reinstall the app.
  • Verify restore content is displayed on the welcome step.

Custom AI flow

  • Additionally apply this diff:
diff --git a/app/src/main/java/com/duckduckgo/app/onboarding/CustomAiOnboardingStore.kt b/app/src/main/java/com/duckduckgo/app/onboarding/CustomAiOnboardingStore.kt
index fe3752cbb2..af220bb09d 100644
--- a/app/src/main/java/com/duckduckgo/app/onboarding/CustomAiOnboardingStore.kt
+++ b/app/src/main/java/com/duckduckgo/app/onboarding/CustomAiOnboardingStore.kt
@@ -100,6 +100,7 @@ class CustomAiOnboardingStoreImpl @Inject constructor(
     }
 
     override suspend fun resolve() = resolveMutex.withLock {
+        return@withLock true
         withContext(dispatcherProvider.io()) {
             // Ensure the install referrer (and therefore the processing function) has resolved before reading.
             withTimeoutOrNull(MAX_REFERRER_WAIT_TIME_MS) { referrerStateListener.get().waitForReferrerCode() }
@@ -116,6 +117,7 @@ class CustomAiOnboardingStoreImpl @Inject constructor(
     }
 
     override suspend fun isEnabled(): Boolean = resolveMutex.withLock {
+        return@withLock true
         withContext(dispatcherProvider.io()) {
             preferences.getBoolean(PREFS_KEY_ENABLED, false)
         }
  • Clean install.
  • Walk the full flow: welcome, AI comparison, input preview, Duck.ai demo, browser comparison, address bar

Note

Medium Risk
Touches first-run onboarding UI, orchestrator events, and analytics pixels; behavior is flag-gated but mistakes would affect every new install when the config-driven toggle is on.

Overview
Ports welcome, input selection, and input preview onto the config-driven onboarding renderer, alongside the comparison/address-bar work already in that pipeline.

Welcome and flow entry: DialogConfigResolver now builds configs for initial, reinstall, and sync-restore steps (restore/skip CTAs, custom-AI copy). Welcome uses CardEntry.AfterBackgroundTransition so the card fade waits for the intro background crossover. OnboardingDialogShownPixels fires unique “shown” pixels when those dialogs render; sync restore is shown in the UI instead of auto-skipping.

Input screens: New ContentConfig types and binders wire AI/search picker (submit → InputModeConfirmed) and the preview demo (tabs, suggestions, keyboard). Preview queries go through ContentInteraction.SubmitInputPreviewInputDemoQuerySubmitted. BindScope.animateCardBounds and CardStage.beginBoundsTransition smooth card growth when preview mode or suggestions change; tweens are suppressed while a snapped/skipped entrance is still landing.

Engine / VM: DialogRenderEngine passes animateCardBounds into bind scope and honors cardEntry reveal delay. ConfigDrivenOnboardingPageViewModel no longer auto-advances past the ported dialogs on activity start.

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

@LukasPaczos
LukasPaczos force-pushed the refactor/lpaczos/config-driven-linear-onboarding-dialogs/welcome-input-screens branch from 4faa9d3 to a359793 Compare August 5, 2026 15:02

@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 0b0f84a. Configure here.

Base automatically changed from refactor/lpaczos/config-driven-linear-onboarding-dialogs/intro-animation to develop August 6, 2026 11:14
The welcome, sync restore and input screens render their own cards now, so the
unrendered-dialog fallback must not keep skipping them. Only add to dock, the
widget prompt and quick setup still need advancing past.
The binder's mode-toggle preselect duplicated what applyMode already does. The
pixels test now also pins that quick setup fires nothing.
A dialog whose card entry follows the background waits EXIT_DURATION before
starting its one-time reveal, so it no longer fades in over a background
that is still crossing over. A snapped background has nothing to wait for,
so the delay is gated on the background actually animating.

(cherry picked from commit f2d7a72)
The initial, reinstall, and sync-restore dialogs are the ones that can
arrive while the intro visuals are still on screen, and they share one
config builder.

(cherry picked from commit 8590f0d)
The input screen preview reveals its suggestion buttons from gone, and swaps
the field between one and three lines on a mode switch. Both resize the card,
which had no way to animate: a binder only reaches its own include, so each
change landed in a single layout pass.

The engine now hands binders a card-bounds transition through BindScope,
gated by the render's animation policy so a snapped entrance and tap-to-skip
still snap.

(cherry picked from commit df138bb)
The render's animate flag is off for every re-draw of a step that has already
been presented, so gating on it alone left a rotated or re-entered screen
unable to animate its own resizes for the rest of its life.

The policy now only covers the entrance itself: an interaction the screen
drives once it is on stage animates either way.

(cherry picked from commit 86d14fe)
The render flag and the settled flag encoded one three-state fact, and
the render flag stayed stale-true after settling. A single nullable
policy carries the entrance's animate flag and expires to null once the
screen is on stage, which is the shape the previous commit described.

Also swaps the input preview's hand-rolled mode dedup for
distinctUntilChanged, so same-mode re-emissions no longer re-apply the
mode, and makes BindScope's card-bounds hook a required parameter so a
missed wiring cannot no-op silently.

(cherry picked from commit 20603a9)
The flag guarded copy that does not exist: no welcome variant carries
raw line breaks, so decoding the plain copy is a no-op and the
markup-capable variants (sync restore, custom AI) decode like the
title view already does. Mirrors the title machinery's approach and
keeps markup handling out of the config.

(cherry picked from commit fd10ab4)
No welcome body copy has ever carried markup, in any language: the
legacy decode of the sync-restore and custom-AI variants was decoding
plain sentences since the strings were introduced. All three variants
now take the same plain preventWidows path as the other body texts.

(cherry picked from commit c59c95c)
@LukasPaczos
LukasPaczos force-pushed the refactor/lpaczos/config-driven-linear-onboarding-dialogs/welcome-input-screens branch from 72f6a25 to 3f97516 Compare August 6, 2026 11:14
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