diff --git a/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt b/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt index 546e27a37e36..55dcdfaacb3d 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt @@ -2450,24 +2450,6 @@ class BrowserTabViewModelTest { verify(mockPixel).fire(cta.shownPixel!!, cta.pixelShownParameters()) } - @Test - fun whenRegisterDaxBubbleCtaDismissedThenRegisterInDatabase() = runTest { - val cta = DaxBubbleCta.DaxIntroSearchOptionsCta(mockOnboardingStore, mockAppInstallStore) - testee.ctaViewState.value = CtaViewState(cta = cta) - - testee.registerDaxBubbleCtaDismissed() - verify(mockDismissedCtaDao).insert(DismissedCta(cta.ctaId)) - } - - @Test - fun whenRegisterDaxBubbleCtaDismissedThenCtaChangedToNull() = runTest { - val cta = DaxBubbleCta.DaxIntroSearchOptionsCta(mockOnboardingStore, mockAppInstallStore) - testee.ctaViewState.value = CtaViewState(cta = cta) - - testee.registerDaxBubbleCtaDismissed() - assertNull(testee.ctaViewState.value!!.cta) - } - @Test fun whenRefreshCtaIfCtaAlreadyShownForCurrentPageThenReturnNull() = runTest { setBrowserShowing(isBrowsing = true) diff --git a/app/src/androidTest/java/com/duckduckgo/app/cta/ui/CtaViewModelTest.kt b/app/src/androidTest/java/com/duckduckgo/app/cta/ui/CtaViewModelTest.kt index dbc90079d67d..bf69f981bba4 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/cta/ui/CtaViewModelTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/cta/ui/CtaViewModelTest.kt @@ -241,19 +241,19 @@ class CtaViewModelTest { @Test fun whenRegisterDaxBubbleIntroCtaThenDatabaseNotified() = runTest { - testee.registerDaxBubbleCtaDismissed(DaxBubbleCta.DaxIntroSearchOptionsCta(mockOnboardingStore, mockAppInstallStore)) + testee.onUserDismissedCta(DaxBubbleCta.DaxIntroSearchOptionsCta(mockOnboardingStore, mockAppInstallStore)) verify(mockDismissedCtaDao).insert(DismissedCta(CtaId.DAX_INTRO)) } @Test fun whenRegisterDaxBubbleIntroVisitSiteCtaThenDatabaseNotified() = runTest { - testee.registerDaxBubbleCtaDismissed(DaxBubbleCta.DaxIntroVisitSiteOptionsCta(mockOnboardingStore, mockAppInstallStore)) + testee.onUserDismissedCta(DaxBubbleCta.DaxIntroVisitSiteOptionsCta(mockOnboardingStore, mockAppInstallStore)) verify(mockDismissedCtaDao).insert(DismissedCta(CtaId.DAX_INTRO_VISIT_SITE)) } @Test fun whenRegisterDaxBubbleEndCtaThenDatabaseNotified() = runTest { - testee.registerDaxBubbleCtaDismissed(DaxBubbleCta.DaxEndCta(mockOnboardingStore, mockAppInstallStore)) + testee.onUserDismissedCta(DaxBubbleCta.DaxEndCta(mockOnboardingStore, mockAppInstallStore)) verify(mockDismissedCtaDao).insert(DismissedCta(CtaId.DAX_END)) } @@ -261,7 +261,7 @@ class CtaViewModelTest { fun whenRegisterCtaAndUserHasPendingOnboardingCtasThenStageNotCompleted() = runTest { givenDaxOnboardingActive() givenShownDaxOnboardingCtas(emptyList()) - testee.registerDaxBubbleCtaDismissed(DaxBubbleCta.DaxEndCta(mockOnboardingStore, mockAppInstallStore)) + testee.onUserDismissedCta(DaxBubbleCta.DaxEndCta(mockOnboardingStore, mockAppInstallStore)) verify(mockUserStageStore, times(0)).stageCompleted(any()) } @@ -269,7 +269,7 @@ class CtaViewModelTest { fun whenRegisterCtaAndAllDaxOnboardingCtasShownThenStageCompleted() = runTest { givenDaxOnboardingActive() givenShownDaxOnboardingCtas(requiredDaxOnboardingCtas) - testee.registerDaxBubbleCtaDismissed(DaxBubbleCta.DaxEndCta(mockOnboardingStore, mockAppInstallStore)) + testee.onUserDismissedCta(DaxBubbleCta.DaxEndCta(mockOnboardingStore, mockAppInstallStore)) verify(mockUserStageStore).stageCompleted(AppStage.DAX_ONBOARDING) } @@ -662,7 +662,7 @@ class CtaViewModelTest { @Test fun whenRegisterDismissedDaxIntroVisitSiteCtaThenDatabaseNotified() = runTest { - testee.registerDaxBubbleCtaDismissed( + testee.onUserDismissedCta( DaxBubbleCta.DaxIntroVisitSiteOptionsCta( mockOnboardingStore, mockAppInstallStore, diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt index 0a91f8b8b0ac..b198082c72c7 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt @@ -1515,7 +1515,6 @@ class BrowserTabFragment : clientBrandHintProvider.setOn(webView?.safeSettings, url) hideKeyboard() omnibar.hideFindInPage() - viewModel.registerDaxBubbleCtaDismissed() webView?.loadUrl(url, headers) } diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt index f55044459275..9a131bfb1fa4 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt @@ -2704,14 +2704,6 @@ class BrowserTabViewModel @Inject constructor( command.value = if (shouldHideKeyboard) HideKeyboard else ShowKeyboard } - fun registerDaxBubbleCtaDismissed() { - viewModelScope.launch { - val cta = ctaViewState.value?.cta ?: return@launch - ctaViewModel.registerDaxBubbleCtaDismissed(cta) - ctaViewState.value = currentCtaViewState().copy(cta = null) - } - } - fun onUserClickCtaOkButton(cta: Cta) { viewModelScope.launch { ctaViewModel.onUserClickCtaOkButton(cta) diff --git a/app/src/main/java/com/duckduckgo/app/cta/ui/Cta.kt b/app/src/main/java/com/duckduckgo/app/cta/ui/Cta.kt index b7434d07d57c..5481200b9b94 100644 --- a/app/src/main/java/com/duckduckgo/app/cta/ui/Cta.kt +++ b/app/src/main/java/com/duckduckgo/app/cta/ui/Cta.kt @@ -61,6 +61,8 @@ interface DaxCta { val onboardingStore: OnboardingStore val appInstallStore: AppInstallStore var ctaPixelParam: String + val markAsReadOnShow: Boolean + get() = false companion object { const val MAX_DAYS_ALLOWED = 3 @@ -79,9 +81,6 @@ interface Cta { } interface OnboardingDaxCta { - val markAsReadOnShow: Boolean - get() = false - fun showOnboardingCta( binding: FragmentBrowserTabBinding, onPrimaryCtaClicked: () -> Unit, @@ -510,6 +509,7 @@ sealed class DaxBubbleCta( onTypingAnimationFinished: () -> Unit, ) { ctaView = view + clearDialog() val daxTitle = view.context.getString(title) val daxText = view.context.getString(description) val optionsViews: List = listOf( @@ -523,33 +523,27 @@ sealed class DaxBubbleCta( view.findViewById(R.id.primaryCta).show() view.findViewById(R.id.primaryCta).alpha = 0f view.findViewById(R.id.primaryCta).text = view.context.getString(it) - } ?: view.findViewById(R.id.primaryCta).gone() + } secondaryCta?.let { view.findViewById(R.id.secondaryCta).show() view.findViewById(R.id.secondaryCta).alpha = 0f view.findViewById(R.id.secondaryCta).text = view.context.getString(it) - } ?: view.findViewById(R.id.secondaryCta).gone() + } placeholder?.let { view.findViewById(R.id.placeholder).show() view.findViewById(R.id.placeholder).alpha = 0f view.findViewById(R.id.placeholder).setImageResource(it) - } ?: view.findViewById(R.id.placeholder).gone() - - if (options.isNullOrEmpty()) { - view.findViewById(R.id.daxDialogOption1).gone() - view.findViewById(R.id.daxDialogOption2).gone() - view.findViewById(R.id.daxDialogOption3).gone() - view.findViewById(R.id.daxDialogOption4).gone() - } else { - options?.let { - optionsViews.forEachIndexed { index, buttonView -> - if (it.size > index) { - it[index].setOptionView(buttonView) - } else { - buttonView.gone() - } + } + + options?.let { + optionsViews.forEachIndexed { index, buttonView -> + buttonView.show() + if (it.size > index) { + it[index].setOptionView(buttonView) + } else { + buttonView.gone() } } } @@ -588,6 +582,23 @@ sealed class DaxBubbleCta( view.findViewById(R.id.cardContainer).setOnClickListener { afterAnimation() } } + private fun clearDialog() { + ctaView?.findViewById(R.id.primaryCta)?.alpha = 0f + ctaView?.findViewById(R.id.primaryCta)?.gone() + ctaView?.findViewById(R.id.secondaryCta)?.alpha = 0f + ctaView?.findViewById(R.id.secondaryCta)?.gone() + ctaView?.findViewById(R.id.placeholder)?.alpha = 0f + ctaView?.findViewById(R.id.placeholder)?.gone() + ctaView?.findViewById(R.id.daxDialogOption1)?.alpha = 0f + ctaView?.findViewById(R.id.daxDialogOption1)?.gone() + ctaView?.findViewById(R.id.daxDialogOption2)?.alpha = 0f + ctaView?.findViewById(R.id.daxDialogOption2)?.gone() + ctaView?.findViewById(R.id.daxDialogOption3)?.alpha = 0f + ctaView?.findViewById(R.id.daxDialogOption3)?.gone() + ctaView?.findViewById(R.id.daxDialogOption4)?.alpha = 0f + ctaView?.findViewById(R.id.daxDialogOption4)?.gone() + } + fun setOnPrimaryCtaClicked(onButtonClicked: () -> Unit) { ctaView?.findViewById(R.id.primaryCta)?.setOnClickListener { onButtonClicked.invoke() @@ -612,6 +623,8 @@ sealed class DaxBubbleCta( } } + override val markAsReadOnShow: Boolean = true + override fun pixelCancelParameters(): Map = mapOf(Pixel.PixelParameter.CTA_SHOWN to ctaPixelParam) override fun pixelOkParameters(): Map = mapOf(Pixel.PixelParameter.CTA_SHOWN to ctaPixelParam) diff --git a/app/src/main/java/com/duckduckgo/app/cta/ui/CtaViewModel.kt b/app/src/main/java/com/duckduckgo/app/cta/ui/CtaViewModel.kt index 0f41f2ef3b64..6ffa6d6b6dc5 100644 --- a/app/src/main/java/com/duckduckgo/app/cta/ui/CtaViewModel.kt +++ b/app/src/main/java/com/duckduckgo/app/cta/ui/CtaViewModel.kt @@ -136,7 +136,7 @@ class CtaViewModel @Inject constructor( pixel.fire(it, cta.pixelShownParameters()) } } - if (cta is OnboardingDaxDialogCta && cta.markAsReadOnShow) { + if (cta is DaxCta && cta.markAsReadOnShow) { dismissedCtaDao.insert(DismissedCta(cta.ctaId)) } if (cta is BrokenSitePromptDialogCta) { @@ -145,15 +145,6 @@ class CtaViewModel @Inject constructor( } } - suspend fun registerDaxBubbleCtaDismissed(cta: Cta) { - withContext(dispatchers.io()) { - if (cta is DaxBubbleCta) { - dismissedCtaDao.insert(DismissedCta(cta.ctaId)) - completeStageIfDaxOnboardingCompleted() - } - } - } - private suspend fun completeStageIfDaxOnboardingCompleted() { if (daxOnboardingActive() && allOnboardingCtasShown()) { Timber.d("Completing DAX ONBOARDING") @@ -267,19 +258,14 @@ class CtaViewModel @Inject constructor( @WorkerThread private suspend fun canShowDaxIntroVisitSiteCta(): Boolean = daxOnboardingActive() && daxDialogIntroShown() && !hideTips() && - !(daxDialogNetworkShown() || daxDialogOtherShown() || daxDialogTrackersFoundShown()) + !(daxDialogIntroVisitSiteShown() || daxDialogNetworkShown() || daxDialogOtherShown() || daxDialogTrackersFoundShown()) @WorkerThread - private suspend fun canShowDaxCtaEndOfJourney(): Boolean = daxOnboardingActive() && - !daxDialogEndShown() && - daxDialogIntroShown() && - !hideTips() && - (daxDialogNetworkShown() || daxDialogOtherShown() || daxDialogSerpShown() || daxDialogTrackersFoundShown()) - - private suspend fun canShowPrivacyProCta(): Boolean { - return daxOnboardingActive() && !hideTips() && !daxDialogPrivacyProShown() && - subscriptions.isEligible() && extendedOnboardingFeatureToggles.privacyProCta().isEnabled() - } + private suspend fun canShowDaxCtaEndOfJourney(): Boolean = daxOnboardingActive() && !daxDialogEndShown() && daxDialogIntroShown() && !hideTips() + + @WorkerThread + private suspend fun canShowPrivacyProCta(): Boolean = daxOnboardingActive() && !hideTips() && !daxDialogPrivacyProShown() && + subscriptions.isEligible() && extendedOnboardingFeatureToggles.privacyProCta().isEnabled() @WorkerThread private suspend fun getBrowserCta(site: Site?): Cta? { @@ -360,13 +346,14 @@ class CtaViewModel @Inject constructor( private fun daxDialogIntroShown(): Boolean = dismissedCtaDao.exists(CtaId.DAX_INTRO) + private fun daxDialogIntroVisitSiteShown(): Boolean = dismissedCtaDao.exists(CtaId.DAX_INTRO_VISIT_SITE) + // We only want to show New Tab when the Home CTAs from Onboarding has finished // https://app.asana.com/0/1157893581871903/1207769731595075/f suspend fun areBubbleDaxDialogsCompleted(): Boolean { return withContext(dispatchers.io()) { val noBrowserCtaExperiment = extendedOnboardingFeatureToggles.noBrowserCtas().isEnabled() - val bubbleCtasShown = daxDialogEndShown() && (daxDialogNetworkShown() || daxDialogOtherShown() || daxDialogTrackersFoundShown()) - noBrowserCtaExperiment || bubbleCtasShown || hideTips() || !userStageStore.daxOnboardingActive() + noBrowserCtaExperiment || daxDialogEndShown() || hideTips() } } @@ -374,7 +361,7 @@ class CtaViewModel @Inject constructor( return withContext(dispatchers.io()) { val noBrowserCtaExperiment = extendedOnboardingFeatureToggles.noBrowserCtas().isEnabled() val inContextDaxCtasShown = daxDialogSerpShown() && daxDialogTrackersFoundShown() && daxDialogFireEducationShown() && daxDialogEndShown() - noBrowserCtaExperiment || inContextDaxCtasShown || hideTips() || !userStageStore.daxOnboardingActive() + noBrowserCtaExperiment || inContextDaxCtasShown || hideTips() } } diff --git a/app/src/test/java/com/duckduckgo/app/cta/ui/OnboardingDaxDialogTests.kt b/app/src/test/java/com/duckduckgo/app/cta/ui/OnboardingDaxDialogTests.kt index cf04add7679e..163a66479c64 100644 --- a/app/src/test/java/com/duckduckgo/app/cta/ui/OnboardingDaxDialogTests.kt +++ b/app/src/test/java/com/duckduckgo/app/cta/ui/OnboardingDaxDialogTests.kt @@ -166,16 +166,12 @@ class OnboardingDaxDialogTests { } @Test - fun whenDaxDialogEndShownButOtherDialogsNotShownThenOnboardingNotComplete() = runTest { - whenever(userStageStore.getUserAppStage()).thenReturn(AppStage.DAX_ONBOARDING) - whenever(settingsDataStore.hideTips).thenReturn(false) - whenever(dismissedCtaDao.exists(DAX_END)).thenReturn(true) - whenever(dismissedCtaDao.exists(DAX_DIALOG_OTHER)).thenReturn(false) - whenever(dismissedCtaDao.exists(DAX_DIALOG_TRACKERS_FOUND)).thenReturn(false) - whenever(dismissedCtaDao.exists(DAX_DIALOG_NETWORK)).thenReturn(false) + fun whenOnboardingDismissedThenOnboardingComplete() = runTest { + whenever(settingsDataStore.hideTips).thenReturn(true) + whenever(dismissedCtaDao.exists(DAX_END)).thenReturn(false) val onboardingComplete = testee.areBubbleDaxDialogsCompleted() - assertFalse(onboardingComplete) + assertTrue(onboardingComplete) } @Test @@ -221,17 +217,4 @@ class OnboardingDaxDialogTests { val inContextDaxDialogsComplete = testee.areInContextDaxDialogsCompleted() assertTrue(inContextDaxDialogsComplete) } - - @Test - fun whenOnboardingCompleteThenAreInContextDialogsCompletedIsTrue() = runTest { - whenever(userStageStore.getUserAppStage()).thenReturn(AppStage.ESTABLISHED) - whenever(settingsDataStore.hideTips).thenReturn(false) - whenever(dismissedCtaDao.exists(DAX_DIALOG_SERP)).thenReturn(false) - whenever(dismissedCtaDao.exists(DAX_DIALOG_TRACKERS_FOUND)).thenReturn(false) - whenever(dismissedCtaDao.exists(DAX_FIRE_BUTTON)).thenReturn(false) - whenever(dismissedCtaDao.exists(DAX_END)).thenReturn(false) - - val inContextDaxDialogsComplete = testee.areInContextDaxDialogsCompleted() - assertTrue(inContextDaxDialogsComplete) - } }