iOS: Fix duck.ai "You've got this" completion dialog persistence and dismissal in UTI mode#5231
iOS: Fix duck.ai "You've got this" completion dialog persistence and dismissal in UTI mode#5231mallexxx wants to merge 1 commit into
Conversation
…dismissal in UTI mode Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ 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 3b454d6. Configure here.
| } | ||
| // Dismiss the UTI completion dialog when the NTP leaves the screen (tab switch, | ||
| // navigation). The dialog is marked as seen so the subscription promo surfaces cleanly. | ||
| dismissDuckAICompletionDialogIfNeededOnEditingEnd() |
There was a problem hiding this comment.
Safety-net dismissal in viewWillDisappear is dead code
Medium Severity
The new dismissDuckAICompletionDialogIfNeededOnEditingEnd() call at line 159 can never independently fire in viewWillDisappear. The UTI completion dialog's hosting controller is parented to mainVC (line 602), so the pre-existing check at line 154 (hc.parent !== self) is always true and dismissHostingController(didFinishNTPOnboarding: false) runs first, setting isShowingDuckAICompletionDialog = false. The guard inside dismissDuckAICompletionDialogIfNeededOnEditingEnd then always returns early. This means setFinalOnboardingDialogSeen() is never called through this path — the intended safety net for "tab switch, navigation" cleanup doesn't work. In practice, external triggers (onEditingEnd, dismiss()) always precede viewWillDisappear, masking the issue, but the defensive fallback is silently broken.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 3b454d6. Configure here.


Task/Issue URL: https://app.asana.com/1/137249556945/project/1202406491309510/task/1215009192158950
Tech Design URL: N/A
CC: N/A
Description
Testing Steps
Prerequisites
iOS/Core/FeatureFlag.swiftchange.unifiedToggleInputfrom:Config(source: .remoteReleasable(AIChatSubfeature.unifiedToggleInput))to:
Config(defaultValue: .enabled, source: .remoteReleasable(AIChatSubfeature.unifiedToggleInput))iOS/DuckDuckGo/OnboardingFlow/LinearOnboarding/OnboardingIntroViewModel.swiftinresolveDuckAIQueryExperimentCohortID()addreturn .treatmentBas the first line to force Treatment B enrollmentDevice→Erase all content and settings…)App Language→EnglishandApp Region→United StatesReaching the completion dialog
5. Fresh install. Go through onboarding to the "Search experience" screen.
6. Toggle to "Search + Duck.ai" → "Next".
7. Select Duck.ai and choose a suggested query option.
8. Complete the onboarding until the "You've got this!" dialog appears with the address bar active.
Address bar activate/deactivate
9. While the dialog is visible, activate and deactivate the address bar multiple times — confirm the dialog remains visible throughout
High-five → Subscription upsell
10. Tap "High five" — confirm the dialog dismisses and the subscription upsell appears
Navigate away while address bar is active → upsell on next new tab
11. Reach the dialog again (steps 5–8), activate the address bar, type a URL and navigate to a page — confirm the dialog is dismissed, then open a new tab and verify the subscription upsell appears
Burn all → upsell on next new tab
12. Reach the dialog again, trigger "Burn all" — confirm the dialog is dismissed and the subscription upsell appears on the next new tab
Open duck.ai → upsell on next new tab
13. Reach the dialog again, open the duck.ai chat — confirm the dialog is dismissed and the subscription upsell appears on the next new tab
Impact and Risks
Impact Level: Low
What could go wrong?
dismissDuckAICompletionDialogIfNeededOnEditingEndis now also called fromviewWillDisappear. It is idempotent (guarded byisShowingDuckAICompletionDialog), so double-calls from overlapping triggers are safe.setUnifiedInputContentOverlaySuppressed(true)is now called during UTI completion dialog setup.dismissHostingControlleralready callssetUnifiedInputContentOverlaySuppressed(false)on all teardown paths, so the suppression state is always restored.Quality Considerations
setFinalOnboardingDialogSeen()before checkingsubscriptionPromotionPending, ensuring the subscription promo state machine behaves the same regardless of how the completion dialog exits.Made with Cursor
Note
Low Risk
Onboarding-only UI in
NewTabPageViewController; dismissal is guarded and overlay suppression is cleared on existing teardown paths.Overview
Fixes the Duck.ai "You've got this!" completion dialog in Unified Toggle Input (UTI) mode so it stays visible while the address bar opens and closes, and so leaving the new tab page still advances onboarding correctly.
Presentation: UTI completion now matches other Dax overlays: it calls
setUnifiedInputContentOverlaySuppressed(true)so the NTP shows through while the bar is active, and hosts a single SwiftUI overlay on the plaincontentContainerancestor (full NTP bounds) instead of insideunifiedInputContentContainer, avoiding nestedUIHostingControllerwarnings and the old bar-relative layout.Lifecycle:
viewWillDisappearinvokesdismissDuckAICompletionDialogIfNeededOnEditingEnd()so tab switches and navigation tear down the dialog and mark end-of-journey seen.setFinalOnboardingDialogSeen()runs before readingsubscriptionPromotionPendingin that dismiss path and innotifyDuckAICompletionDismissedIfNeeded(), so the subscription upsell can appear on the next NTP whether the user taps through or navigates away.Reviewed by Cursor Bugbot for commit 3b454d6. Bugbot is set up for automated code reviews on this repo. Configure here.