Skip to content

Commit f112b78

Browse files
nalcalagjoshliebe
authored andcommitted
Onboarding: Dismiss contextual onboarding dialogs after 1 view (#5747)
Task/Issue URL: https://app.asana.com/0/1176956903599313/1209163229346625 ### Description Show context onboarding Dax dialogs when they are shown ### Steps to test this PR _Opening a New Tab_ - [x] Fresh install - [x] When browser is shown, 'Try a search' onboarding dialog will appear - [x] Open a new tab - [x] Check dialog is replaced for 'Visit a site' dialog - [x] Open a new tab - [x] Check dialog is replaced for 'End' dialog _Visit a site dialog not appearing twice_ - [x] Fresh install - [x] When browser is shown, 'Try a search' onboarding dialog will appear - [x] Visit a site without opening a new tab - [x] Open a new tab - [x] Check 'Visit a site' dialog is not shown - [x] Check 'End' dialog is shown _Close and open the app again_ - [x] Fresh install - [x] When browser is shown, 'Try a search' onboarding dialog will appear - [x] Background or close the app - [x] Open it again - [x] Check dialog is replaced for 'Visit a site' dialog - [x] Close or background the app - [x] Open it again - [x] Check dialog is replaced for 'End' dialog ### No UI changes
1 parent 2c34dc0 commit f112b78

File tree

7 files changed

+54
-98
lines changed

7 files changed

+54
-98
lines changed

app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2451,24 +2451,6 @@ class BrowserTabViewModelTest {
24512451
verify(mockPixel).fire(cta.shownPixel!!, cta.pixelShownParameters())
24522452
}
24532453

2454-
@Test
2455-
fun whenRegisterDaxBubbleCtaDismissedThenRegisterInDatabase() = runTest {
2456-
val cta = DaxBubbleCta.DaxIntroSearchOptionsCta(mockOnboardingStore, mockAppInstallStore)
2457-
testee.ctaViewState.value = CtaViewState(cta = cta)
2458-
2459-
testee.registerDaxBubbleCtaDismissed()
2460-
verify(mockDismissedCtaDao).insert(DismissedCta(cta.ctaId))
2461-
}
2462-
2463-
@Test
2464-
fun whenRegisterDaxBubbleCtaDismissedThenCtaChangedToNull() = runTest {
2465-
val cta = DaxBubbleCta.DaxIntroSearchOptionsCta(mockOnboardingStore, mockAppInstallStore)
2466-
testee.ctaViewState.value = CtaViewState(cta = cta)
2467-
2468-
testee.registerDaxBubbleCtaDismissed()
2469-
assertNull(testee.ctaViewState.value!!.cta)
2470-
}
2471-
24722454
@Test
24732455
fun whenRefreshCtaIfCtaAlreadyShownForCurrentPageThenReturnNull() = runTest {
24742456
setBrowserShowing(isBrowsing = true)

app/src/androidTest/java/com/duckduckgo/app/cta/ui/CtaViewModelTest.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -245,35 +245,35 @@ class CtaViewModelTest {
245245

246246
@Test
247247
fun whenRegisterDaxBubbleIntroCtaThenDatabaseNotified() = runTest {
248-
testee.registerDaxBubbleCtaDismissed(DaxBubbleCta.DaxIntroSearchOptionsCta(mockOnboardingStore, mockAppInstallStore))
248+
testee.onUserDismissedCta(DaxBubbleCta.DaxIntroSearchOptionsCta(mockOnboardingStore, mockAppInstallStore))
249249
verify(mockDismissedCtaDao).insert(DismissedCta(CtaId.DAX_INTRO))
250250
}
251251

252252
@Test
253253
fun whenRegisterDaxBubbleIntroVisitSiteCtaThenDatabaseNotified() = runTest {
254-
testee.registerDaxBubbleCtaDismissed(DaxBubbleCta.DaxIntroVisitSiteOptionsCta(mockOnboardingStore, mockAppInstallStore))
254+
testee.onUserDismissedCta(DaxBubbleCta.DaxIntroVisitSiteOptionsCta(mockOnboardingStore, mockAppInstallStore))
255255
verify(mockDismissedCtaDao).insert(DismissedCta(CtaId.DAX_INTRO_VISIT_SITE))
256256
}
257257

258258
@Test
259259
fun whenRegisterDaxBubbleEndCtaThenDatabaseNotified() = runTest {
260-
testee.registerDaxBubbleCtaDismissed(DaxBubbleCta.DaxEndCta(mockOnboardingStore, mockAppInstallStore))
260+
testee.onUserDismissedCta(DaxBubbleCta.DaxEndCta(mockOnboardingStore, mockAppInstallStore))
261261
verify(mockDismissedCtaDao).insert(DismissedCta(CtaId.DAX_END))
262262
}
263263

264264
@Test
265265
fun whenRegisterCtaAndUserHasPendingOnboardingCtasThenStageNotCompleted() = runTest {
266266
givenDaxOnboardingActive()
267267
givenShownDaxOnboardingCtas(emptyList())
268-
testee.registerDaxBubbleCtaDismissed(DaxBubbleCta.DaxEndCta(mockOnboardingStore, mockAppInstallStore))
268+
testee.onUserDismissedCta(DaxBubbleCta.DaxEndCta(mockOnboardingStore, mockAppInstallStore))
269269
verify(mockUserStageStore, times(0)).stageCompleted(any())
270270
}
271271

272272
@Test
273273
fun whenRegisterCtaAndAllDaxOnboardingCtasShownThenStageCompleted() = runTest {
274274
givenDaxOnboardingActive()
275275
givenShownDaxOnboardingCtas(requiredDaxOnboardingCtas)
276-
testee.registerDaxBubbleCtaDismissed(DaxBubbleCta.DaxEndCta(mockOnboardingStore, mockAppInstallStore))
276+
testee.onUserDismissedCta(DaxBubbleCta.DaxEndCta(mockOnboardingStore, mockAppInstallStore))
277277
verify(mockUserStageStore).stageCompleted(AppStage.DAX_ONBOARDING)
278278
}
279279

@@ -666,7 +666,7 @@ class CtaViewModelTest {
666666

667667
@Test
668668
fun whenRegisterDismissedDaxIntroVisitSiteCtaThenDatabaseNotified() = runTest {
669-
testee.registerDaxBubbleCtaDismissed(
669+
testee.onUserDismissedCta(
670670
DaxBubbleCta.DaxIntroVisitSiteOptionsCta(
671671
mockOnboardingStore,
672672
mockAppInstallStore,

app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1515,7 +1515,6 @@ class BrowserTabFragment :
15151515
clientBrandHintProvider.setOn(webView?.safeSettings, url)
15161516
hideKeyboard()
15171517
omnibar.hideFindInPage()
1518-
viewModel.registerDaxBubbleCtaDismissed()
15191518
webView?.loadUrl(url, headers)
15201519
}
15211520

app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2704,14 +2704,6 @@ class BrowserTabViewModel @Inject constructor(
27042704
command.value = if (shouldHideKeyboard) HideKeyboard else ShowKeyboard
27052705
}
27062706

2707-
fun registerDaxBubbleCtaDismissed() {
2708-
viewModelScope.launch {
2709-
val cta = ctaViewState.value?.cta ?: return@launch
2710-
ctaViewModel.registerDaxBubbleCtaDismissed(cta)
2711-
ctaViewState.value = currentCtaViewState().copy(cta = null)
2712-
}
2713-
}
2714-
27152707
fun onUserClickCtaOkButton(cta: Cta) {
27162708
viewModelScope.launch {
27172709
ctaViewModel.onUserClickCtaOkButton(cta)

app/src/main/java/com/duckduckgo/app/cta/ui/Cta.kt

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ interface DaxCta {
6161
val onboardingStore: OnboardingStore
6262
val appInstallStore: AppInstallStore
6363
var ctaPixelParam: String
64+
val markAsReadOnShow: Boolean
65+
get() = false
6466

6567
companion object {
6668
const val MAX_DAYS_ALLOWED = 3
@@ -79,9 +81,6 @@ interface Cta {
7981
}
8082

8183
interface OnboardingDaxCta {
82-
val markAsReadOnShow: Boolean
83-
get() = false
84-
8584
fun showOnboardingCta(
8685
binding: FragmentBrowserTabBinding,
8786
onPrimaryCtaClicked: () -> Unit,
@@ -510,6 +509,7 @@ sealed class DaxBubbleCta(
510509
onTypingAnimationFinished: () -> Unit,
511510
) {
512511
ctaView = view
512+
clearDialog()
513513
val daxTitle = view.context.getString(title)
514514
val daxText = view.context.getString(description)
515515
val optionsViews: List<DaxButton> = listOf(
@@ -523,33 +523,27 @@ sealed class DaxBubbleCta(
523523
view.findViewById<DaxButton>(R.id.primaryCta).show()
524524
view.findViewById<DaxButton>(R.id.primaryCta).alpha = 0f
525525
view.findViewById<DaxButton>(R.id.primaryCta).text = view.context.getString(it)
526-
} ?: view.findViewById<DaxButton>(R.id.primaryCta).gone()
526+
}
527527

528528
secondaryCta?.let {
529529
view.findViewById<DaxButton>(R.id.secondaryCta).show()
530530
view.findViewById<DaxButton>(R.id.secondaryCta).alpha = 0f
531531
view.findViewById<DaxButton>(R.id.secondaryCta).text = view.context.getString(it)
532-
} ?: view.findViewById<DaxButton>(R.id.secondaryCta).gone()
532+
}
533533

534534
placeholder?.let {
535535
view.findViewById<ImageView>(R.id.placeholder).show()
536536
view.findViewById<ImageView>(R.id.placeholder).alpha = 0f
537537
view.findViewById<ImageView>(R.id.placeholder).setImageResource(it)
538-
} ?: view.findViewById<ImageView>(R.id.placeholder).gone()
539-
540-
if (options.isNullOrEmpty()) {
541-
view.findViewById<DaxButton>(R.id.daxDialogOption1).gone()
542-
view.findViewById<DaxButton>(R.id.daxDialogOption2).gone()
543-
view.findViewById<DaxButton>(R.id.daxDialogOption3).gone()
544-
view.findViewById<DaxButton>(R.id.daxDialogOption4).gone()
545-
} else {
546-
options?.let {
547-
optionsViews.forEachIndexed { index, buttonView ->
548-
if (it.size > index) {
549-
it[index].setOptionView(buttonView)
550-
} else {
551-
buttonView.gone()
552-
}
538+
}
539+
540+
options?.let {
541+
optionsViews.forEachIndexed { index, buttonView ->
542+
buttonView.show()
543+
if (it.size > index) {
544+
it[index].setOptionView(buttonView)
545+
} else {
546+
buttonView.gone()
553547
}
554548
}
555549
}
@@ -588,6 +582,23 @@ sealed class DaxBubbleCta(
588582
view.findViewById<View>(R.id.cardContainer).setOnClickListener { afterAnimation() }
589583
}
590584

585+
private fun clearDialog() {
586+
ctaView?.findViewById<DaxButton>(R.id.primaryCta)?.alpha = 0f
587+
ctaView?.findViewById<DaxButton>(R.id.primaryCta)?.gone()
588+
ctaView?.findViewById<DaxButton>(R.id.secondaryCta)?.alpha = 0f
589+
ctaView?.findViewById<DaxButton>(R.id.secondaryCta)?.gone()
590+
ctaView?.findViewById<ImageView>(R.id.placeholder)?.alpha = 0f
591+
ctaView?.findViewById<ImageView>(R.id.placeholder)?.gone()
592+
ctaView?.findViewById<DaxButton>(R.id.daxDialogOption1)?.alpha = 0f
593+
ctaView?.findViewById<DaxButton>(R.id.daxDialogOption1)?.gone()
594+
ctaView?.findViewById<DaxButton>(R.id.daxDialogOption2)?.alpha = 0f
595+
ctaView?.findViewById<DaxButton>(R.id.daxDialogOption2)?.gone()
596+
ctaView?.findViewById<DaxButton>(R.id.daxDialogOption3)?.alpha = 0f
597+
ctaView?.findViewById<DaxButton>(R.id.daxDialogOption3)?.gone()
598+
ctaView?.findViewById<DaxButton>(R.id.daxDialogOption4)?.alpha = 0f
599+
ctaView?.findViewById<DaxButton>(R.id.daxDialogOption4)?.gone()
600+
}
601+
591602
fun setOnPrimaryCtaClicked(onButtonClicked: () -> Unit) {
592603
ctaView?.findViewById<DaxButton>(R.id.primaryCta)?.setOnClickListener {
593604
onButtonClicked.invoke()
@@ -612,6 +623,8 @@ sealed class DaxBubbleCta(
612623
}
613624
}
614625

626+
override val markAsReadOnShow: Boolean = true
627+
615628
override fun pixelCancelParameters(): Map<String, String> = mapOf(Pixel.PixelParameter.CTA_SHOWN to ctaPixelParam)
616629

617630
override fun pixelOkParameters(): Map<String, String> = mapOf(Pixel.PixelParameter.CTA_SHOWN to ctaPixelParam)

app/src/main/java/com/duckduckgo/app/cta/ui/CtaViewModel.kt

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ class CtaViewModel @Inject constructor(
139139
pixel.fire(it, cta.pixelShownParameters())
140140
}
141141
}
142-
if (cta is OnboardingDaxDialogCta && cta.markAsReadOnShow) {
142+
if (cta is DaxCta && cta.markAsReadOnShow) {
143143
dismissedCtaDao.insert(DismissedCta(cta.ctaId))
144144
}
145145
if (cta is BrokenSitePromptDialogCta) {
@@ -160,15 +160,6 @@ class CtaViewModel @Inject constructor(
160160
}
161161
}
162162

163-
suspend fun registerDaxBubbleCtaDismissed(cta: Cta) {
164-
withContext(dispatchers.io()) {
165-
if (cta is DaxBubbleCta) {
166-
dismissedCtaDao.insert(DismissedCta(cta.ctaId))
167-
completeStageIfDaxOnboardingCompleted()
168-
}
169-
}
170-
}
171-
172163
private suspend fun completeStageIfDaxOnboardingCompleted() {
173164
if (daxOnboardingActive() && allOnboardingCtasShown()) {
174165
Timber.d("Completing DAX ONBOARDING")
@@ -282,19 +273,14 @@ class CtaViewModel @Inject constructor(
282273
@WorkerThread
283274
private suspend fun canShowDaxIntroVisitSiteCta(): Boolean =
284275
daxOnboardingActive() && daxDialogIntroShown() && !hideTips() &&
285-
!(daxDialogNetworkShown() || daxDialogOtherShown() || daxDialogTrackersFoundShown())
276+
!(daxDialogIntroVisitSiteShown() || daxDialogNetworkShown() || daxDialogOtherShown() || daxDialogTrackersFoundShown())
286277

287278
@WorkerThread
288-
private suspend fun canShowDaxCtaEndOfJourney(): Boolean = daxOnboardingActive() &&
289-
!daxDialogEndShown() &&
290-
daxDialogIntroShown() &&
291-
!hideTips() &&
292-
(daxDialogNetworkShown() || daxDialogOtherShown() || daxDialogSerpShown() || daxDialogTrackersFoundShown())
293-
294-
private suspend fun canShowPrivacyProCta(): Boolean {
295-
return daxOnboardingActive() && !hideTips() && !daxDialogPrivacyProShown() &&
296-
subscriptions.isEligible() && extendedOnboardingFeatureToggles.privacyProCta().isEnabled()
297-
}
279+
private suspend fun canShowDaxCtaEndOfJourney(): Boolean = daxOnboardingActive() && !daxDialogEndShown() && daxDialogIntroShown() && !hideTips()
280+
281+
@WorkerThread
282+
private suspend fun canShowPrivacyProCta(): Boolean = daxOnboardingActive() && !hideTips() && !daxDialogPrivacyProShown() &&
283+
subscriptions.isEligible() && extendedOnboardingFeatureToggles.privacyProCta().isEnabled()
298284

299285
@WorkerThread
300286
private suspend fun getBrowserCta(site: Site?): Cta? {
@@ -375,21 +361,22 @@ class CtaViewModel @Inject constructor(
375361

376362
private fun daxDialogIntroShown(): Boolean = dismissedCtaDao.exists(CtaId.DAX_INTRO)
377363

364+
private fun daxDialogIntroVisitSiteShown(): Boolean = dismissedCtaDao.exists(CtaId.DAX_INTRO_VISIT_SITE)
365+
378366
// We only want to show New Tab when the Home CTAs from Onboarding has finished
379367
// https://app.asana.com/0/1157893581871903/1207769731595075/f
380368
suspend fun areBubbleDaxDialogsCompleted(): Boolean {
381369
return withContext(dispatchers.io()) {
382370
val noBrowserCtaExperiment = extendedOnboardingFeatureToggles.noBrowserCtas().isEnabled()
383-
val bubbleCtasShown = daxDialogEndShown() && (daxDialogNetworkShown() || daxDialogOtherShown() || daxDialogTrackersFoundShown())
384-
noBrowserCtaExperiment || bubbleCtasShown || hideTips() || !userStageStore.daxOnboardingActive()
371+
noBrowserCtaExperiment || daxDialogEndShown() || hideTips()
385372
}
386373
}
387374

388375
suspend fun areInContextDaxDialogsCompleted(): Boolean {
389376
return withContext(dispatchers.io()) {
390377
val noBrowserCtaExperiment = extendedOnboardingFeatureToggles.noBrowserCtas().isEnabled()
391378
val inContextDaxCtasShown = daxDialogSerpShown() && daxDialogTrackersFoundShown() && daxDialogFireEducationShown() && daxDialogEndShown()
392-
noBrowserCtaExperiment || inContextDaxCtasShown || hideTips() || !userStageStore.daxOnboardingActive()
379+
noBrowserCtaExperiment || inContextDaxCtasShown || hideTips()
393380
}
394381
}
395382

app/src/test/java/com/duckduckgo/app/cta/ui/OnboardingDaxDialogTests.kt

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -169,16 +169,12 @@ class OnboardingDaxDialogTests {
169169
}
170170

171171
@Test
172-
fun whenDaxDialogEndShownButOtherDialogsNotShownThenOnboardingNotComplete() = runTest {
173-
whenever(userStageStore.getUserAppStage()).thenReturn(AppStage.DAX_ONBOARDING)
174-
whenever(settingsDataStore.hideTips).thenReturn(false)
175-
whenever(dismissedCtaDao.exists(DAX_END)).thenReturn(true)
176-
whenever(dismissedCtaDao.exists(DAX_DIALOG_OTHER)).thenReturn(false)
177-
whenever(dismissedCtaDao.exists(DAX_DIALOG_TRACKERS_FOUND)).thenReturn(false)
178-
whenever(dismissedCtaDao.exists(DAX_DIALOG_NETWORK)).thenReturn(false)
172+
fun whenOnboardingDismissedThenOnboardingComplete() = runTest {
173+
whenever(settingsDataStore.hideTips).thenReturn(true)
174+
whenever(dismissedCtaDao.exists(DAX_END)).thenReturn(false)
179175

180176
val onboardingComplete = testee.areBubbleDaxDialogsCompleted()
181-
assertFalse(onboardingComplete)
177+
assertTrue(onboardingComplete)
182178
}
183179

184180
@Test
@@ -224,17 +220,4 @@ class OnboardingDaxDialogTests {
224220
val inContextDaxDialogsComplete = testee.areInContextDaxDialogsCompleted()
225221
assertTrue(inContextDaxDialogsComplete)
226222
}
227-
228-
@Test
229-
fun whenOnboardingCompleteThenAreInContextDialogsCompletedIsTrue() = runTest {
230-
whenever(userStageStore.getUserAppStage()).thenReturn(AppStage.ESTABLISHED)
231-
whenever(settingsDataStore.hideTips).thenReturn(false)
232-
whenever(dismissedCtaDao.exists(DAX_DIALOG_SERP)).thenReturn(false)
233-
whenever(dismissedCtaDao.exists(DAX_DIALOG_TRACKERS_FOUND)).thenReturn(false)
234-
whenever(dismissedCtaDao.exists(DAX_FIRE_BUTTON)).thenReturn(false)
235-
whenever(dismissedCtaDao.exists(DAX_END)).thenReturn(false)
236-
237-
val inContextDaxDialogsComplete = testee.areInContextDaxDialogsCompleted()
238-
assertTrue(inContextDaxDialogsComplete)
239-
}
240223
}

0 commit comments

Comments
 (0)