From 4d653850bd15020a02c17c957604040d4dfddf10 Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 01:28:29 -0300 Subject: [PATCH 01/24] Introduce Analytics call to action UI xml --- .../layout/analytics_call_to_action_view.xml | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 WooCommerce/src/main/res/layout/analytics_call_to_action_view.xml diff --git a/WooCommerce/src/main/res/layout/analytics_call_to_action_view.xml b/WooCommerce/src/main/res/layout/analytics_call_to_action_view.xml new file mode 100644 index 00000000000..09a5fbc704a --- /dev/null +++ b/WooCommerce/src/main/res/layout/analytics_call_to_action_view.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + From 01a0ba21df6843aeec5b29652081ace062b60dff Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 01:29:22 -0300 Subject: [PATCH 02/24] Introduce new VIewStates to support the CTA visibility control --- .../ui/analytics/hub/AnalyticsHubCardState.kt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubCardState.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubCardState.kt index 235254ccdd4..fa1783068cd 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubCardState.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubCardState.kt @@ -56,10 +56,12 @@ sealed class AnalyticsHubListViewState : AnalyticsCardViewState { sealed class AnalyticsHubCustomSelectionListViewState : AnalyticsCardViewState { data class LoadingAdsViewState(override val card: AnalyticsCards) : AnalyticsHubCustomSelectionListViewState() + data class NoAdsState( override val card: AnalyticsCards, val message: String ) : AnalyticsHubCustomSelectionListViewState() + data class CustomListViewState( override val card: AnalyticsCards, val title: String, @@ -82,4 +84,26 @@ sealed class AnalyticsHubCustomSelectionListViewState : AnalyticsCardViewState { else -> "-" } } + + data class HiddenState( + override val card: AnalyticsCards + ) : AnalyticsHubCustomSelectionListViewState() +} + +data class AnalyticsHubUserCallToActionViewState( + val title: String, + val description: String, + val callToActionText: String, + val isVisible: Boolean, + val onCallToActionClickListener: () -> Unit +) { + companion object { + val EMPTY = AnalyticsHubUserCallToActionViewState( + title = "", + description = "", + callToActionText = "", + isVisible = false, + onCallToActionClickListener = {} + ) + } } From 659fbfb926cb1b8124e09f843b6473cd9cda1e4e Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 01:29:32 -0300 Subject: [PATCH 03/24] Define AnalyticsHubCustomSelectionCardView --- .../cta/AnalyticsHubUserCallToActionView.kt | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/cta/AnalyticsHubUserCallToActionView.kt diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/cta/AnalyticsHubUserCallToActionView.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/cta/AnalyticsHubUserCallToActionView.kt new file mode 100644 index 00000000000..5dfa694ae9f --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/cta/AnalyticsHubUserCallToActionView.kt @@ -0,0 +1,30 @@ +package com.woocommerce.android.ui.analytics.hub.cta + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import com.google.android.material.card.MaterialCardView +import com.woocommerce.android.databinding.AnalyticsCallToActionViewBinding +import com.woocommerce.android.ui.analytics.hub.AnalyticsHubUserCallToActionViewState + +class AnalyticsHubUserCallToActionView @JvmOverloads constructor( + ctx: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : MaterialCardView(ctx, attrs, defStyleAttr) { + val binding = AnalyticsCallToActionViewBinding.inflate(LayoutInflater.from(ctx), this) + + fun updateInformation(viewState: AnalyticsHubUserCallToActionViewState) { + if (viewState.isVisible) { + binding.ctaTitle.text = viewState.title + binding.ctaDescription.text = viewState.description + binding.buttonCtaAction.text = viewState.callToActionText + binding.buttonCtaAction.setOnClickListener { + viewState.onCallToActionClickListener() + } + binding.analyticsCallToActionCard.visibility = VISIBLE + } else { + binding.analyticsCallToActionCard.visibility = GONE + } + } +} From a4403d8e7156f96a38e70dead6d8e9ce31419c60 Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 01:29:56 -0300 Subject: [PATCH 04/24] Add the AnalyticsHubUserCallToActionView directly to the Analytics Hub UI xml --- .../src/main/res/layout/fragment_analytics.xml | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/WooCommerce/src/main/res/layout/fragment_analytics.xml b/WooCommerce/src/main/res/layout/fragment_analytics.xml index 72967982f7e..cf01e402aca 100644 --- a/WooCommerce/src/main/res/layout/fragment_analytics.xml +++ b/WooCommerce/src/main/res/layout/fragment_analytics.xml @@ -50,14 +50,26 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/divider" /> + + + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/analyticsCallToActionCard" /> + + + From f62b470d7a2e91963865205f328178dd0e5d0ac3 Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 01:38:12 -0300 Subject: [PATCH 05/24] Control whether a GoogleAdsStat was fetched or not --- .../com/woocommerce/android/model/GoogleAdsStat.kt | 9 +++++++-- .../android/ui/analytics/hub/sync/AnalyticsRepository.kt | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/model/GoogleAdsStat.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/model/GoogleAdsStat.kt index 78cabb80ab2..87b5f0b5e4d 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/model/GoogleAdsStat.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/model/GoogleAdsStat.kt @@ -3,8 +3,12 @@ package com.woocommerce.android.model data class GoogleAdsStat( val googleAdsCampaigns: List, val totals: GoogleAdsTotals, - val totalsDeltaPercentage: GoogleAdsTotalsDeltaPercentage + val totalsDeltaPercentage: GoogleAdsTotalsDeltaPercentage, + val isFetchedData: Boolean ) { + val noCampaignsAvailable + get() = googleAdsCampaigns.isEmpty() && isFetchedData + companion object { val EMPTY = GoogleAdsStat( googleAdsCampaigns = emptyList(), @@ -21,7 +25,8 @@ data class GoogleAdsStat( clicksDelta = DeltaPercentage.NotExist, impressionsDelta = DeltaPercentage.NotExist, conversionsDelta = DeltaPercentage.NotExist - ) + ), + isFetchedData = false ) } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/sync/AnalyticsRepository.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/sync/AnalyticsRepository.kt index b6c09bf9024..e6ac3ddaa72 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/sync/AnalyticsRepository.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/sync/AnalyticsRepository.kt @@ -519,7 +519,8 @@ class AnalyticsRepository @Inject constructor( totalsDeltaPercentage = mapGoogleAdsDeltaPercentages( previousRangeResponse, currentRangeResponse - ) + ), + isFetchedData = true ) ) From b29e734a251edefa5f9183ed7743589c4129c01e Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 01:38:32 -0300 Subject: [PATCH 06/24] Add Google Ads CTA string resources --- WooCommerce/src/main/res/values/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/WooCommerce/src/main/res/values/strings.xml b/WooCommerce/src/main/res/values/strings.xml index 3938e78cc00..652fe2d956e 100644 --- a/WooCommerce/src/main/res/values/strings.xml +++ b/WooCommerce/src/main/res/values/strings.xml @@ -540,6 +540,9 @@ item items %1$s, %2$s, %3$s, %4$s sold + Google Campaigns + Drive sales and generate more traffic with Google Ads. + Add paid campaign See your stats, revenue and more from your device! We\'ve been working on making it possible to display significant store information from your device! Could it be better? Please help us improve this feature by sharing your feedback with us From bbcba5e8ef96d937290044ab5d17d840fd4d7b12 Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 01:51:11 -0300 Subject: [PATCH 07/24] Define OpenGoogleAdsCreation event for Fragment --- .../android/ui/analytics/hub/AnalyticsHubViewState.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewState.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewState.kt index 544cc53077c..3a48da178c5 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewState.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewState.kt @@ -7,6 +7,7 @@ data class AnalyticsViewState( val refreshIndicator: RefreshIndicator, val analyticsDateRangeSelectorState: AnalyticsHubDateRangeSelectorViewState, val cards: AnalyticsHubCardViewState, + val ctaState: AnalyticsHubUserCallToActionViewState, val showFeedBackBanner: Boolean, val lastUpdateTimestamp: String ) @@ -18,6 +19,11 @@ sealed class AnalyticsViewEvent : MultiLiveEvent.Event() { object OpenDateRangeSelector : AnalyticsViewEvent() object SendFeedback : AnalyticsViewEvent() object OpenSettings : AnalyticsViewEvent() + data class OpenGoogleAdsCreation( + val url: String, + val isCreationFlow: Boolean, + val title: String + ) : AnalyticsViewEvent() } sealed class RefreshIndicator { From 10ea32b51f78a2be86c3c4091e0705e82b0965df Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 01:53:52 -0300 Subject: [PATCH 08/24] Update AnalyticsHubFragment to start and observe the Google Ads WebView --- .../ui/analytics/hub/AnalyticsHubFragment.kt | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubFragment.kt index 26405f3e08a..58498e31240 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubFragment.kt @@ -17,6 +17,7 @@ import com.woocommerce.android.NavGraphMainDirections import com.woocommerce.android.R import com.woocommerce.android.databinding.FragmentAnalyticsBinding import com.woocommerce.android.extensions.handleDialogResult +import com.woocommerce.android.extensions.handleNotice import com.woocommerce.android.extensions.navigateSafely import com.woocommerce.android.extensions.scrollStartEvents import com.woocommerce.android.extensions.showDateRangePicker @@ -26,6 +27,8 @@ import com.woocommerce.android.ui.analytics.ranges.StatsTimeRangeSelection.Selec import com.woocommerce.android.ui.base.BaseFragment import com.woocommerce.android.ui.common.MarginBottomItemDecoration import com.woocommerce.android.ui.feedback.SurveyType +import com.woocommerce.android.ui.google.webview.GoogleAdsWebViewFragment.Companion.WEBVIEW_RESULT +import com.woocommerce.android.ui.google.webview.GoogleAdsWebViewViewModel import com.woocommerce.android.util.ChromeCustomTabUtils import com.woocommerce.android.viewmodel.MultiLiveEvent import dagger.hilt.android.AndroidEntryPoint @@ -81,6 +84,12 @@ class AnalyticsHubFragment : BaseFragment(R.layout.fragment_analytics) { private fun handleEvent(event: MultiLiveEvent.Event) { when (event) { + is AnalyticsViewEvent.OpenGoogleAdsCreation -> startGoogleAdsWebView( + url = event.url, + title = event.title, + isCreationFlow = event.isCreationFlow + ) + is AnalyticsViewEvent.OpenUrl -> ChromeCustomTabUtils.launchUrl(requireContext(), event.url) is AnalyticsViewEvent.OpenWPComWebView -> findNavController() @@ -124,6 +133,12 @@ class AnalyticsHubFragment : BaseFragment(R.layout.fragment_analytics) { ?.let { viewModel.onNewRangeSelection(it) } ?: viewModel.onCustomDateRangeClicked() } + handleNotice(WEBVIEW_RESULT) { + viewModel.onGoogleAdsCreationSuccess() + findNavController().navigateSafely( + NavGraphMainDirections.actionGlobalGoogleAdsCampaignSuccessBottomSheet() + ) + } } private fun bind(view: View) { @@ -145,6 +160,7 @@ class AnalyticsHubFragment : BaseFragment(R.layout.fragment_analytics) { binding.analyticsDateSelectorCard.updatePreviousRange(viewState.analyticsDateRangeSelectorState.previousRange) binding.analyticsDateSelectorCard.updateCurrentRange(viewState.analyticsDateRangeSelectorState.currentRange) binding.analyticsDateSelectorCard.updateLastUpdateTimestamp(viewState.lastUpdateTimestamp) + binding.analyticsCallToActionCard.updateInformation(viewState.ctaState) when (viewState.cards) { is AnalyticsHubCardViewState.CardsState -> { (binding.cards.adapter as AnalyticsHubCardsAdapter).cardList = viewState.cards.cardsState @@ -193,4 +209,20 @@ class AnalyticsHubFragment : BaseFragment(R.layout.fragment_analytics) { Lifecycle.State.RESUMED ) } + + private fun startGoogleAdsWebView( + url: String, + title: String, + isCreationFlow: Boolean + ) { + val direction = NavGraphMainDirections.actionGlobalGoogleAdsWebViewFragment( + urlToLoad = url, + title = title, + urlComparisonMode = GoogleAdsWebViewViewModel.UrlComparisonMode.PARTIAL, + isCreationFlow = isCreationFlow, + entryPointSource = GoogleAdsWebViewViewModel.EntryPointSource.MYSTORE + ) + + findNavController().navigateSafely(direction) + } } From 12e8a7758890c9b21b3dded95685438a1baf5366 Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 01:54:22 -0300 Subject: [PATCH 09/24] Handle the HiddenState scenario inside the AnalyticsHubCustomSelectionCardView --- .../customlistcard/AnalyticsHubCustomSelectionCardView.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/customlistcard/AnalyticsHubCustomSelectionCardView.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/customlistcard/AnalyticsHubCustomSelectionCardView.kt index 7107fabc5ed..e563419f94c 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/customlistcard/AnalyticsHubCustomSelectionCardView.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/customlistcard/AnalyticsHubCustomSelectionCardView.kt @@ -13,6 +13,7 @@ import com.woocommerce.android.R import com.woocommerce.android.databinding.AnalyticsCustomSelectionCardViewBinding import com.woocommerce.android.ui.analytics.hub.AnalyticsHubCustomSelectionListViewState import com.woocommerce.android.ui.analytics.hub.AnalyticsHubCustomSelectionListViewState.CustomListViewState +import com.woocommerce.android.ui.analytics.hub.AnalyticsHubCustomSelectionListViewState.HiddenState import com.woocommerce.android.ui.analytics.hub.AnalyticsHubCustomSelectionListViewState.LoadingAdsViewState import com.woocommerce.android.ui.analytics.hub.AnalyticsHubCustomSelectionListViewState.NoAdsState import com.woocommerce.android.ui.analytics.hub.informationcard.SeeReportClickListener @@ -36,9 +37,15 @@ class AnalyticsHubCustomSelectionCardView @JvmOverloads constructor( is LoadingAdsViewState -> setSkeleton() is NoAdsState -> setNoAdsViewState(viewState) is CustomListViewState -> setDataViewState(viewState) + is HiddenState -> setHiddenState() } } + private fun setHiddenState() { + skeletonView.hide() + binding.analyticsCustomCardListContainer.visibility = GONE + } + private fun setDataViewState(viewState: CustomListViewState) { skeletonView.hide() binding.analyticsCardTitle.text = viewState.title From ed58d3cb3762941947813222ddd31a4a8e83d971 Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 01:54:53 -0300 Subject: [PATCH 10/24] Support the CTA scenarios after fetching the Google Campaigns data --- .../ui/analytics/hub/AnalyticsHubViewModel.kt | 74 ++++++++++++++----- 1 file changed, 56 insertions(+), 18 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt index 523e131917d..df1c00d7b0d 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt @@ -78,6 +78,10 @@ import javax.inject.Inject import com.woocommerce.android.ui.analytics.hub.AnalyticsHubListViewState as ListViewState import com.woocommerce.android.ui.analytics.hub.AnalyticsHubListViewState.LoadingViewState as LoadingListViewState import com.woocommerce.android.ui.analytics.hub.AnalyticsHubListViewState.NoDataState as ListNoDataState +import com.woocommerce.android.AppUrls.GOOGLE_ADMIN_CAMPAIGN_CREATION_SUFFIX +import com.woocommerce.android.extensions.adminUrlOrDefault +import com.woocommerce.android.ui.analytics.hub.AnalyticsHubCustomSelectionListViewState.HiddenState +import com.woocommerce.android.ui.analytics.hub.AnalyticsViewEvent.OpenGoogleAdsCreation @HiltViewModel @SuppressWarnings("LargeClass") @@ -112,6 +116,7 @@ class AnalyticsHubViewModel @Inject constructor( refreshIndicator = NotShowIndicator, analyticsDateRangeSelectorState = AnalyticsHubDateRangeSelectorViewState.EMPTY, cards = AnalyticsHubCardViewState.LoadingCardsConfiguration, + ctaState = AnalyticsHubUserCallToActionViewState.EMPTY, showFeedBackBanner = false, lastUpdateTimestamp = "" ) @@ -315,6 +320,23 @@ class AnalyticsHubViewModel @Inject constructor( } } + private fun updateCardStatus(card: AnalyticsCards, newState: AnalyticsCardViewState) { + val cardsInformation = mutableState.value.cards + if (cardsInformation is AnalyticsHubCardViewState.CardsState) { + val updatedCardState = cardsInformation.cardsState.toMutableList().map { state -> + if (state.card == card) { + newState + } else { + state + } + } + val updatedInfo = cardsInformation.copy(cardsState = updatedCardState) + mutableState.update { viewState -> + viewState.copy(cards = updatedInfo) + } + } + } + private fun observeOrdersStatChanges() { ordersObservationJob = updateStats.ordersState.onEach { state -> when (state) { @@ -464,7 +486,14 @@ class AnalyticsHubViewModel @Inject constructor( googleAdsObservationJob = updateStats.googleAdsState.onEach { state -> when (state) { is GoogleAdsState.Available -> { - updateCardStatus(AnalyticsCards.GoogleAds, buildGoogleAdsDataViewState(state.googleAdsStat)) + val stats = state.googleAdsStat + if (stats.noCampaignsAvailable) { + updateGoogleAdsCTAVisibility(isVisible = true) + updateCardStatus(AnalyticsCards.GoogleAds, HiddenState(AnalyticsCards.GoogleAds)) + } else { + updateGoogleAdsCTAVisibility(isVisible = false) + updateCardStatus(AnalyticsCards.GoogleAds, buildGoogleAdsDataViewState(stats)) + } } is GoogleAdsState.Error -> { @@ -480,23 +509,6 @@ class AnalyticsHubViewModel @Inject constructor( }.launchIn(viewModelScope) } - private fun updateCardStatus(card: AnalyticsCards, newState: AnalyticsCardViewState) { - val cardsInformation = mutableState.value.cards - if (cardsInformation is AnalyticsHubCardViewState.CardsState) { - val updatedCardState = cardsInformation.cardsState.toMutableList().map { state -> - if (state.card == card) { - newState - } else { - state - } - } - val updatedInfo = cardsInformation.copy(cardsState = updatedCardState) - mutableState.update { viewState -> - viewState.copy(cards = updatedInfo) - } - } - } - @OptIn(ExperimentalCoroutinesApi::class) private fun observeLastUpdateTimestamp() { lastUpdateObservationJob?.cancel() @@ -790,10 +802,36 @@ class AnalyticsHubViewModel @Inject constructor( giftCardsObservationJob?.cancel() } + private fun updateGoogleAdsCTAVisibility(isVisible: Boolean) { + val newState = AnalyticsHubUserCallToActionViewState( + title = resourceProvider.getString(R.string.analytics_google_ads_cta_title), + description = resourceProvider.getString(R.string.analytics_google_ads_cta_description), + callToActionText = resourceProvider.getString(R.string.analytics_google_ads_cta_action), + isVisible = isVisible, + onCallToActionClickListener = { + selectedSite.getOrNull()?.let { + it.adminUrlOrDefault + GOOGLE_ADMIN_CAMPAIGN_CREATION_SUFFIX + }?.let { url -> + triggerEvent(OpenGoogleAdsCreation( + url = url, + isCreationFlow = true, + title = resourceProvider.getString(R.string.analytics_google_ads_cta_action) + )) + } + + } + ) + mutableState.update { it.copy(ctaState = newState) } + } + fun onOpenSettings() { tracker.track(AnalyticsEvent.ANALYTICS_HUB_SETTINGS_OPENED) triggerEvent(AnalyticsViewEvent.OpenSettings) } + + fun onGoogleAdsCreationSuccess() { + TODO("Not yet implemented") + } } enum class ReportCard { Revenue, Orders, Products, Bundles, GiftCard, GoogleCampaigns } From 55c94fa614bdade08b040e1293b35297d02263e7 Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 02:02:55 -0300 Subject: [PATCH 11/24] Add Analytics Hub entry point source to the GoogleAdsWebViewViewModel --- .../woocommerce/android/analytics/AnalyticsTracker.kt | 1 + .../android/ui/analytics/hub/AnalyticsHubFragment.kt | 7 ++++--- .../ui/google/webview/GoogleAdsWebViewViewModel.kt | 10 +++++----- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/analytics/AnalyticsTracker.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/analytics/AnalyticsTracker.kt index 93c1ea3f706..c63d971d2e6 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/analytics/AnalyticsTracker.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/analytics/AnalyticsTracker.kt @@ -678,6 +678,7 @@ class AnalyticsTracker private constructor( const val VALUE_GOOGLEADS_ENTRY_POINT_SOURCE_MOREMENU = "more_menu" const val VALUE_GOOGLEADS_ENTRY_POINT_TYPE_CREATION = "creation" const val VALUE_GOOGLEADS_ENTRY_POINT_TYPE_DASHBOARD = "dashboard" + const val VALUE_GOOGLEADS_ENTRY_POINT_TYPE_ANALYTICS_HUB = "analytics_hub" var sendUsageStats: Boolean = true set(value) { diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubFragment.kt index 58498e31240..9100e0ce115 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubFragment.kt @@ -28,7 +28,8 @@ import com.woocommerce.android.ui.base.BaseFragment import com.woocommerce.android.ui.common.MarginBottomItemDecoration import com.woocommerce.android.ui.feedback.SurveyType import com.woocommerce.android.ui.google.webview.GoogleAdsWebViewFragment.Companion.WEBVIEW_RESULT -import com.woocommerce.android.ui.google.webview.GoogleAdsWebViewViewModel +import com.woocommerce.android.ui.google.webview.GoogleAdsWebViewViewModel.EntryPointSource.ANALYTICS_HUB +import com.woocommerce.android.ui.google.webview.GoogleAdsWebViewViewModel.UrlComparisonMode.PARTIAL import com.woocommerce.android.util.ChromeCustomTabUtils import com.woocommerce.android.viewmodel.MultiLiveEvent import dagger.hilt.android.AndroidEntryPoint @@ -218,9 +219,9 @@ class AnalyticsHubFragment : BaseFragment(R.layout.fragment_analytics) { val direction = NavGraphMainDirections.actionGlobalGoogleAdsWebViewFragment( urlToLoad = url, title = title, - urlComparisonMode = GoogleAdsWebViewViewModel.UrlComparisonMode.PARTIAL, + urlComparisonMode = PARTIAL, isCreationFlow = isCreationFlow, - entryPointSource = GoogleAdsWebViewViewModel.EntryPointSource.MYSTORE + entryPointSource = ANALYTICS_HUB ) findNavController().navigateSafely(direction) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/google/webview/GoogleAdsWebViewViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/google/webview/GoogleAdsWebViewViewModel.kt index a2fd15456fd..a02a6d11703 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/google/webview/GoogleAdsWebViewViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/google/webview/GoogleAdsWebViewViewModel.kt @@ -56,10 +56,10 @@ class GoogleAdsWebViewViewModel @Inject constructor( ) } - private val sourceValue = if (viewState.source == EntryPointSource.MORE_MENU) { - AnalyticsTracker.VALUE_GOOGLEADS_ENTRY_POINT_SOURCE_MOREMENU - } else { - AnalyticsTracker.VALUE_GOOGLEADS_ENTRY_POINT_SOURCE_MYSTORE + private val sourceValue = when (viewState.source) { + EntryPointSource.MORE_MENU -> AnalyticsTracker.VALUE_GOOGLEADS_ENTRY_POINT_SOURCE_MOREMENU + EntryPointSource.MYSTORE -> AnalyticsTracker.VALUE_GOOGLEADS_ENTRY_POINT_SOURCE_MYSTORE + EntryPointSource.ANALYTICS_HUB -> AnalyticsTracker.VALUE_GOOGLEADS_ENTRY_POINT_TYPE_ANALYTICS_HUB } fun onUrlLoaded(url: String) { @@ -148,6 +148,6 @@ class GoogleAdsWebViewViewModel @Inject constructor( } enum class EntryPointSource { - MORE_MENU, MYSTORE + MORE_MENU, MYSTORE, ANALYTICS_HUB } } From 3b07a1f97aa1b5aaebec7817ba8a2f497393934a Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 02:03:53 -0300 Subject: [PATCH 12/24] Introduce remaining track event when starting the Google Ads WebView --- .../ui/analytics/hub/AnalyticsHubViewModel.kt | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt index df1c00d7b0d..a531bc2f572 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt @@ -79,6 +79,8 @@ import com.woocommerce.android.ui.analytics.hub.AnalyticsHubListViewState as Lis import com.woocommerce.android.ui.analytics.hub.AnalyticsHubListViewState.LoadingViewState as LoadingListViewState import com.woocommerce.android.ui.analytics.hub.AnalyticsHubListViewState.NoDataState as ListNoDataState import com.woocommerce.android.AppUrls.GOOGLE_ADMIN_CAMPAIGN_CREATION_SUFFIX +import com.woocommerce.android.analytics.AnalyticsTracker.Companion.KEY_GOOGLEADS_SOURCE +import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_GOOGLEADS_ENTRY_POINT_TYPE_ANALYTICS_HUB import com.woocommerce.android.extensions.adminUrlOrDefault import com.woocommerce.android.ui.analytics.hub.AnalyticsHubCustomSelectionListViewState.HiddenState import com.woocommerce.android.ui.analytics.hub.AnalyticsViewEvent.OpenGoogleAdsCreation @@ -803,25 +805,34 @@ class AnalyticsHubViewModel @Inject constructor( } private fun updateGoogleAdsCTAVisibility(isVisible: Boolean) { - val newState = AnalyticsHubUserCallToActionViewState( + AnalyticsHubUserCallToActionViewState( title = resourceProvider.getString(R.string.analytics_google_ads_cta_title), description = resourceProvider.getString(R.string.analytics_google_ads_cta_description), callToActionText = resourceProvider.getString(R.string.analytics_google_ads_cta_action), isVisible = isVisible, - onCallToActionClickListener = { - selectedSite.getOrNull()?.let { - it.adminUrlOrDefault + GOOGLE_ADMIN_CAMPAIGN_CREATION_SUFFIX - }?.let { url -> - triggerEvent(OpenGoogleAdsCreation( - url = url, - isCreationFlow = true, - title = resourceProvider.getString(R.string.analytics_google_ads_cta_action) - )) - } + onCallToActionClickListener = { onGoogleAdsCTAClicked() } + ).let { newState -> + mutableState.update { it.copy(ctaState = newState) } + } + } - } - ) - mutableState.update { it.copy(ctaState = newState) } + private fun onGoogleAdsCTAClicked() { + selectedSite.getOrNull()?.let { + it.adminUrlOrDefault + GOOGLE_ADMIN_CAMPAIGN_CREATION_SUFFIX + }?.let { url -> + triggerEvent(OpenGoogleAdsCreation( + url = url, + isCreationFlow = true, + title = resourceProvider.getString(R.string.analytics_google_ads_cta_action) + )) + + tracker.track( + stat = AnalyticsEvent.GOOGLEADS_ENTRY_POINT_DISPLAYED, + properties = mapOf( + KEY_GOOGLEADS_SOURCE to VALUE_GOOGLEADS_ENTRY_POINT_TYPE_ANALYTICS_HUB + ) + ) + } } fun onOpenSettings() { From 6134eeb8c41a0eda55a539bae4501431f076449f Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 02:06:35 -0300 Subject: [PATCH 13/24] Fix incorrect track triggers for the GoogleAds AnalyticsHub entry point --- .../android/ui/analytics/hub/AnalyticsHubViewModel.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt index a531bc2f572..bc3b9895a53 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt @@ -814,6 +814,15 @@ class AnalyticsHubViewModel @Inject constructor( ).let { newState -> mutableState.update { it.copy(ctaState = newState) } } + + if (isVisible) { + tracker.track( + stat = AnalyticsEvent.GOOGLEADS_ENTRY_POINT_DISPLAYED, + properties = mapOf( + KEY_GOOGLEADS_SOURCE to VALUE_GOOGLEADS_ENTRY_POINT_TYPE_ANALYTICS_HUB + ) + ) + } } private fun onGoogleAdsCTAClicked() { @@ -827,7 +836,7 @@ class AnalyticsHubViewModel @Inject constructor( )) tracker.track( - stat = AnalyticsEvent.GOOGLEADS_ENTRY_POINT_DISPLAYED, + stat = AnalyticsEvent.GOOGLEADS_ENTRY_POINT_TAPPED, properties = mapOf( KEY_GOOGLEADS_SOURCE to VALUE_GOOGLEADS_ENTRY_POINT_TYPE_ANALYTICS_HUB ) From 55643f8876f2ee3d4305b99888bb77be3fca537b Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 02:07:03 -0300 Subject: [PATCH 14/24] Remove unused AnalyticsHubViewModel method --- .../android/ui/analytics/hub/AnalyticsHubFragment.kt | 2 +- .../android/ui/analytics/hub/AnalyticsHubViewModel.kt | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubFragment.kt index 9100e0ce115..f4de8b42a6a 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubFragment.kt @@ -134,8 +134,8 @@ class AnalyticsHubFragment : BaseFragment(R.layout.fragment_analytics) { ?.let { viewModel.onNewRangeSelection(it) } ?: viewModel.onCustomDateRangeClicked() } + handleNotice(WEBVIEW_RESULT) { - viewModel.onGoogleAdsCreationSuccess() findNavController().navigateSafely( NavGraphMainDirections.actionGlobalGoogleAdsCampaignSuccessBottomSheet() ) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt index bc3b9895a53..3c8b3fa20cf 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt @@ -848,10 +848,6 @@ class AnalyticsHubViewModel @Inject constructor( tracker.track(AnalyticsEvent.ANALYTICS_HUB_SETTINGS_OPENED) triggerEvent(AnalyticsViewEvent.OpenSettings) } - - fun onGoogleAdsCreationSuccess() { - TODO("Not yet implemented") - } } enum class ReportCard { Revenue, Orders, Products, Bundles, GiftCard, GoogleCampaigns } From d58683d65315661d2c5ee598f29836fb754e8451 Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 02:08:29 -0300 Subject: [PATCH 15/24] Fix lint issues --- .../ui/analytics/hub/AnalyticsHubViewModel.kt | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt index 3c8b3fa20cf..5d99b7d73fe 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt @@ -4,10 +4,14 @@ import androidx.annotation.VisibleForTesting import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope +import com.woocommerce.android.AppUrls.GOOGLE_ADMIN_CAMPAIGN_CREATION_SUFFIX import com.woocommerce.android.R import com.woocommerce.android.analytics.AnalyticsEvent import com.woocommerce.android.analytics.AnalyticsTracker +import com.woocommerce.android.analytics.AnalyticsTracker.Companion.KEY_GOOGLEADS_SOURCE +import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_GOOGLEADS_ENTRY_POINT_TYPE_ANALYTICS_HUB import com.woocommerce.android.analytics.AnalyticsTrackerWrapper +import com.woocommerce.android.extensions.adminUrlOrDefault import com.woocommerce.android.model.AnalyticCardConfiguration import com.woocommerce.android.model.AnalyticsCards import com.woocommerce.android.model.BundleStat @@ -24,11 +28,13 @@ import com.woocommerce.android.model.SessionStat import com.woocommerce.android.model.StatType import com.woocommerce.android.tools.SelectedSite import com.woocommerce.android.ui.analytics.hub.AnalyticsHubCustomSelectionListViewState.CustomListViewState +import com.woocommerce.android.ui.analytics.hub.AnalyticsHubCustomSelectionListViewState.HiddenState import com.woocommerce.android.ui.analytics.hub.AnalyticsHubCustomSelectionListViewState.LoadingAdsViewState import com.woocommerce.android.ui.analytics.hub.AnalyticsHubInformationViewState.DataViewState import com.woocommerce.android.ui.analytics.hub.AnalyticsHubInformationViewState.LoadingViewState import com.woocommerce.android.ui.analytics.hub.AnalyticsHubInformationViewState.NoDataState import com.woocommerce.android.ui.analytics.hub.AnalyticsHubInformationViewState.NoSupportedState +import com.woocommerce.android.ui.analytics.hub.AnalyticsViewEvent.OpenGoogleAdsCreation import com.woocommerce.android.ui.analytics.hub.RefreshIndicator.NotShowIndicator import com.woocommerce.android.ui.analytics.hub.RefreshIndicator.ShowIndicator import com.woocommerce.android.ui.analytics.hub.daterangeselector.AnalyticsHubDateRangeSelectorViewState @@ -78,12 +84,6 @@ import javax.inject.Inject import com.woocommerce.android.ui.analytics.hub.AnalyticsHubListViewState as ListViewState import com.woocommerce.android.ui.analytics.hub.AnalyticsHubListViewState.LoadingViewState as LoadingListViewState import com.woocommerce.android.ui.analytics.hub.AnalyticsHubListViewState.NoDataState as ListNoDataState -import com.woocommerce.android.AppUrls.GOOGLE_ADMIN_CAMPAIGN_CREATION_SUFFIX -import com.woocommerce.android.analytics.AnalyticsTracker.Companion.KEY_GOOGLEADS_SOURCE -import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_GOOGLEADS_ENTRY_POINT_TYPE_ANALYTICS_HUB -import com.woocommerce.android.extensions.adminUrlOrDefault -import com.woocommerce.android.ui.analytics.hub.AnalyticsHubCustomSelectionListViewState.HiddenState -import com.woocommerce.android.ui.analytics.hub.AnalyticsViewEvent.OpenGoogleAdsCreation @HiltViewModel @SuppressWarnings("LargeClass") @@ -829,11 +829,13 @@ class AnalyticsHubViewModel @Inject constructor( selectedSite.getOrNull()?.let { it.adminUrlOrDefault + GOOGLE_ADMIN_CAMPAIGN_CREATION_SUFFIX }?.let { url -> - triggerEvent(OpenGoogleAdsCreation( - url = url, - isCreationFlow = true, - title = resourceProvider.getString(R.string.analytics_google_ads_cta_action) - )) + triggerEvent( + OpenGoogleAdsCreation( + url = url, + isCreationFlow = true, + title = resourceProvider.getString(R.string.analytics_google_ads_cta_action) + ) + ) tracker.track( stat = AnalyticsEvent.GOOGLEADS_ENTRY_POINT_TAPPED, From d6b2779de268b26a26bf151efb4432e5e5733945 Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 02:47:40 -0300 Subject: [PATCH 16/24] Fix the loading feedback issue inside the Analytics Hub --- .../hub/AnalyticsHubCardViewState.kt | 22 ++++++++++++++++--- .../ui/analytics/hub/AnalyticsHubFragment.kt | 11 ++++------ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubCardViewState.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubCardViewState.kt index a2d588da006..6b4d9acbfd5 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubCardViewState.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubCardViewState.kt @@ -1,6 +1,22 @@ package com.woocommerce.android.ui.analytics.hub -sealed class AnalyticsHubCardViewState { - data object LoadingCardsConfiguration : AnalyticsHubCardViewState() - data class CardsState(val cardsState: List) : AnalyticsHubCardViewState() +import com.woocommerce.android.model.AnalyticsCards + +sealed class AnalyticsHubCardViewState( + open val cardsState: List +) { + /** + A default Loading configuration for when the Analytics Hub has started + but the AnalyticCardConfiguration list is still unknown. + */ + data object LoadingCardsConfiguration : AnalyticsHubCardViewState( + cardsState = listOf( + AnalyticsHubInformationViewState.LoadingViewState(AnalyticsCards.Revenue), + AnalyticsHubInformationViewState.LoadingViewState(AnalyticsCards.Orders), + AnalyticsHubListViewState.LoadingViewState(AnalyticsCards.Products), + ) + ) + data class CardsState( + override val cardsState: List + ) : AnalyticsHubCardViewState(cardsState) } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubFragment.kt index f4de8b42a6a..fc303b72e14 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubFragment.kt @@ -162,15 +162,12 @@ class AnalyticsHubFragment : BaseFragment(R.layout.fragment_analytics) { binding.analyticsDateSelectorCard.updateCurrentRange(viewState.analyticsDateRangeSelectorState.currentRange) binding.analyticsDateSelectorCard.updateLastUpdateTimestamp(viewState.lastUpdateTimestamp) binding.analyticsCallToActionCard.updateInformation(viewState.ctaState) - when (viewState.cards) { - is AnalyticsHubCardViewState.CardsState -> { - (binding.cards.adapter as AnalyticsHubCardsAdapter).cardList = viewState.cards.cardsState - } - - else -> {} - } binding.analyticsRefreshLayout.isRefreshing = viewState.refreshIndicator == ShowIndicator displayFeedbackBanner(viewState.showFeedBackBanner) + + binding.cards.adapter + .run { this as? AnalyticsHubCardsAdapter } + ?.cardList = viewState.cards.cardsState } private fun getDateRangeSelectorViewState() = viewModel.viewState.value.analyticsDateRangeSelectorState From c50ec64c6873f8762a2ebeb136f060b58df4ee81 Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 03:07:16 -0300 Subject: [PATCH 17/24] Fix CTA using vertical space even when hidden --- .../ui/analytics/hub/cta/AnalyticsHubUserCallToActionView.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/cta/AnalyticsHubUserCallToActionView.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/cta/AnalyticsHubUserCallToActionView.kt index 5dfa694ae9f..1aadbe874bb 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/cta/AnalyticsHubUserCallToActionView.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/cta/AnalyticsHubUserCallToActionView.kt @@ -22,9 +22,9 @@ class AnalyticsHubUserCallToActionView @JvmOverloads constructor( binding.buttonCtaAction.setOnClickListener { viewState.onCallToActionClickListener() } - binding.analyticsCallToActionCard.visibility = VISIBLE + binding.root.visibility = VISIBLE } else { - binding.analyticsCallToActionCard.visibility = GONE + binding.root.visibility = GONE } } } From 7fa2bc90497f3f3d4439ff1fd089371e6e63ca9b Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 03:21:06 -0300 Subject: [PATCH 18/24] Use an empty viewState for the GoogleAds stats card to better control when to display the CTA --- .../ui/analytics/hub/AnalyticsHubViewModel.kt | 16 ++++++++-------- .../hub/sync/UpdateAnalyticsHubStats.kt | 9 +++++++-- .../ui/analytics/hub/sync/UpdateStates.kt | 1 + 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt index 5d99b7d73fe..de51b25508b 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt @@ -486,16 +486,16 @@ class AnalyticsHubViewModel @Inject constructor( private fun observeGoogleAdsChanges() { googleAdsObservationJob = updateStats.googleAdsState.onEach { state -> + updateGoogleAdsCTAVisibility(isVisible = false) + when (state) { is GoogleAdsState.Available -> { - val stats = state.googleAdsStat - if (stats.noCampaignsAvailable) { - updateGoogleAdsCTAVisibility(isVisible = true) - updateCardStatus(AnalyticsCards.GoogleAds, HiddenState(AnalyticsCards.GoogleAds)) - } else { - updateGoogleAdsCTAVisibility(isVisible = false) - updateCardStatus(AnalyticsCards.GoogleAds, buildGoogleAdsDataViewState(stats)) - } + updateCardStatus(AnalyticsCards.GoogleAds, buildGoogleAdsDataViewState(state.googleAdsStat)) + } + + is GoogleAdsState.Empty -> { + updateGoogleAdsCTAVisibility(isVisible = true) + updateCardStatus(AnalyticsCards.GoogleAds, HiddenState(AnalyticsCards.GoogleAds)) } is GoogleAdsState.Error -> { diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/sync/UpdateAnalyticsHubStats.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/sync/UpdateAnalyticsHubStats.kt index f6c9aaee169..91bdd7d9b94 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/sync/UpdateAnalyticsHubStats.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/sync/UpdateAnalyticsHubStats.kt @@ -229,7 +229,12 @@ class UpdateAnalyticsHubStats @Inject constructor( ) = async { analyticsRepository.fetchGoogleAdsStats(rangeSelection) .run { this as? AnalyticsRepository.GoogleAdsResult.GoogleAdsData } - ?.let { _googleAdsState.value = GoogleAdsState.Available(it.googleAdsStat) } - ?: _googleAdsState.update { GoogleAdsState.Error } + ?.let { + if (it.googleAdsStat.noCampaignsAvailable) { + _googleAdsState.value = GoogleAdsState.Empty + } else { + _googleAdsState.value = GoogleAdsState.Available(it.googleAdsStat) + } + } ?: _googleAdsState.update { GoogleAdsState.Error } } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/sync/UpdateStates.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/sync/UpdateStates.kt index 00aee723ccc..337377ee670 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/sync/UpdateStates.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/sync/UpdateStates.kt @@ -68,6 +68,7 @@ sealed class GiftCardsState { sealed class GoogleAdsState { data class Available(val googleAdsStat: GoogleAdsStat) : GoogleAdsState() + data object Empty : GoogleAdsState() data object Loading : GoogleAdsState() data object Error : GoogleAdsState() val isIdle get() = this !is Loading From 45b6c0b7587a57c2d794c770249fb2bf032ed0ca Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 03:21:41 -0300 Subject: [PATCH 19/24] Simplify the GoogleAdsStat state control --- .../android/model/GoogleAdsStat.kt | 12 +++++------ .../android/model/GoogleAdsStatUIData.kt | 20 +++++++++---------- .../analytics/hub/sync/AnalyticsRepository.kt | 3 +-- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/model/GoogleAdsStat.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/model/GoogleAdsStat.kt index 87b5f0b5e4d..8b6fac0bbc1 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/model/GoogleAdsStat.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/model/GoogleAdsStat.kt @@ -1,17 +1,16 @@ package com.woocommerce.android.model data class GoogleAdsStat( - val googleAdsCampaigns: List, + val googleAdsCampaigns: List?, val totals: GoogleAdsTotals, - val totalsDeltaPercentage: GoogleAdsTotalsDeltaPercentage, - val isFetchedData: Boolean + val totalsDeltaPercentage: GoogleAdsTotalsDeltaPercentage ) { val noCampaignsAvailable - get() = googleAdsCampaigns.isEmpty() && isFetchedData + get() = googleAdsCampaigns?.isEmpty() ?: false companion object { val EMPTY = GoogleAdsStat( - googleAdsCampaigns = emptyList(), + googleAdsCampaigns = null, totals = GoogleAdsTotals( sales = 0.0, spend = 0.0, @@ -25,8 +24,7 @@ data class GoogleAdsStat( clicksDelta = DeltaPercentage.NotExist, impressionsDelta = DeltaPercentage.NotExist, conversionsDelta = DeltaPercentage.NotExist - ), - isFetchedData = false + ) ) } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/model/GoogleAdsStatUIData.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/model/GoogleAdsStatUIData.kt index e4fef12ac80..d03d01c824f 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/model/GoogleAdsStatUIData.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/model/GoogleAdsStatUIData.kt @@ -23,7 +23,7 @@ class GoogleAdsStatUIData( StatType.TOTAL_SALES -> { mainTotalStatTitle = resourceProvider.getString(R.string.analytics_google_ads_filter_total_sales) mainTotalStat = currencyFormatter.formatCurrency(rawStats.totals.sales.toString()) - statItems = rawStats.googleAdsCampaigns.map { campaign -> + statItems = rawStats.googleAdsCampaigns?.map { campaign -> GoogleAdsStatUIDataItem( name = campaign.name, mainStat = currencyFormatter.formatCurrency(campaign.subtotal.sales.toString()), @@ -32,13 +32,13 @@ class GoogleAdsStatUIData( currencyFormatter.formatCurrency(campaign.subtotal.spend.toString()) ) ) - } + }.orEmpty() } StatType.SPEND -> { mainTotalStatTitle = resourceProvider.getString(R.string.analytics_google_ads_filter_spend) mainTotalStat = currencyFormatter.formatCurrency(rawStats.totals.spend.toString()) - statItems = rawStats.googleAdsCampaigns.map { campaign -> + statItems = rawStats.googleAdsCampaigns?.map { campaign -> GoogleAdsStatUIDataItem( name = campaign.name, mainStat = currencyFormatter.formatCurrency(campaign.subtotal.spend.toString()), @@ -47,13 +47,13 @@ class GoogleAdsStatUIData( currencyFormatter.formatCurrency(campaign.subtotal.sales.toString()) ) ) - } + }.orEmpty() } StatType.CLICKS -> { mainTotalStatTitle = resourceProvider.getString(R.string.analytics_google_ads_filter_clicks) mainTotalStat = rawStats.totals.clicks.toString() - statItems = rawStats.googleAdsCampaigns.map { campaign -> + statItems = rawStats.googleAdsCampaigns?.map { campaign -> GoogleAdsStatUIDataItem( name = campaign.name, mainStat = campaign.subtotal.clicks.toString(), @@ -62,13 +62,13 @@ class GoogleAdsStatUIData( currencyFormatter.formatCurrency(campaign.subtotal.spend.toString()) ) ) - } + }.orEmpty() } StatType.IMPRESSIONS -> { mainTotalStatTitle = resourceProvider.getString(R.string.analytics_google_ads_filter_impressions) mainTotalStat = rawStats.totals.impressions.toString() - statItems = rawStats.googleAdsCampaigns.map { campaign -> + statItems = rawStats.googleAdsCampaigns?.map { campaign -> GoogleAdsStatUIDataItem( name = campaign.name, mainStat = campaign.subtotal.impressions.toString(), @@ -77,13 +77,13 @@ class GoogleAdsStatUIData( currencyFormatter.formatCurrency(campaign.subtotal.spend.toString()) ) ) - } + }.orEmpty() } StatType.CONVERSIONS -> { mainTotalStatTitle = resourceProvider.getString(R.string.analytics_google_ads_filter_conversion) mainTotalStat = rawStats.totals.conversions.toString() - statItems = rawStats.googleAdsCampaigns.map { campaign -> + statItems = rawStats.googleAdsCampaigns?.map { campaign -> GoogleAdsStatUIDataItem( name = campaign.name, mainStat = campaign.subtotal.conversions.toString(), @@ -92,7 +92,7 @@ class GoogleAdsStatUIData( currencyFormatter.formatCurrency(campaign.subtotal.spend.toString()) ) ) - } + }.orEmpty() } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/sync/AnalyticsRepository.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/sync/AnalyticsRepository.kt index e6ac3ddaa72..b6c09bf9024 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/sync/AnalyticsRepository.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/sync/AnalyticsRepository.kt @@ -519,8 +519,7 @@ class AnalyticsRepository @Inject constructor( totalsDeltaPercentage = mapGoogleAdsDeltaPercentages( previousRangeResponse, currentRangeResponse - ), - isFetchedData = true + ) ) ) From 915ffc870a7c16ef0cbab92613a8d5477d365f0e Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 03:21:52 -0300 Subject: [PATCH 20/24] Animate the Google Ads CTA presentation --- .../ui/analytics/hub/cta/AnalyticsHubUserCallToActionView.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/cta/AnalyticsHubUserCallToActionView.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/cta/AnalyticsHubUserCallToActionView.kt index 1aadbe874bb..d699995adb8 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/cta/AnalyticsHubUserCallToActionView.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/cta/AnalyticsHubUserCallToActionView.kt @@ -5,6 +5,7 @@ import android.util.AttributeSet import android.view.LayoutInflater import com.google.android.material.card.MaterialCardView import com.woocommerce.android.databinding.AnalyticsCallToActionViewBinding +import com.woocommerce.android.extensions.expand import com.woocommerce.android.ui.analytics.hub.AnalyticsHubUserCallToActionViewState class AnalyticsHubUserCallToActionView @JvmOverloads constructor( @@ -22,7 +23,7 @@ class AnalyticsHubUserCallToActionView @JvmOverloads constructor( binding.buttonCtaAction.setOnClickListener { viewState.onCallToActionClickListener() } - binding.root.visibility = VISIBLE + binding.root.expand() } else { binding.root.visibility = GONE } From 4d82023214a3824654d9c61692061928ca94b790 Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 03:36:19 -0300 Subject: [PATCH 21/24] Refresh the Analytics Hub after creating the first Google Campaign --- .../woocommerce/android/ui/analytics/hub/AnalyticsHubFragment.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubFragment.kt index fc303b72e14..46cda4682fb 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubFragment.kt @@ -136,6 +136,7 @@ class AnalyticsHubFragment : BaseFragment(R.layout.fragment_analytics) { } handleNotice(WEBVIEW_RESULT) { + viewModel.onRefreshRequested() findNavController().navigateSafely( NavGraphMainDirections.actionGlobalGoogleAdsCampaignSuccessBottomSheet() ) From ec92726a8b6c1b42adb3df11e2d9160302dd9c8e Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 03:37:41 -0300 Subject: [PATCH 22/24] Fix lint issues --- .../android/ui/analytics/hub/AnalyticsHubCardViewState.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubCardViewState.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubCardViewState.kt index 6b4d9acbfd5..8b82487f093 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubCardViewState.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubCardViewState.kt @@ -6,8 +6,8 @@ sealed class AnalyticsHubCardViewState( open val cardsState: List ) { /** - A default Loading configuration for when the Analytics Hub has started - but the AnalyticCardConfiguration list is still unknown. + A default Loading configuration for when the Analytics Hub has started + but the AnalyticCardConfiguration list is still unknown. */ data object LoadingCardsConfiguration : AnalyticsHubCardViewState( cardsState = listOf( From 0c317a62cfa1f86d9bfe460e2329f132588196ad Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 03:39:22 -0300 Subject: [PATCH 23/24] Update release notes --- RELEASE-NOTES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index e92be64c15a..67980f4eb0a 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -2,7 +2,7 @@ *** For entries which are touching the Android Wear app's, start entry with `[WEAR]` too. 19.8 ----- - +- [*] Stats: The Google Campaign analytics card now has a call to action to create a paid campaign if there are no campaign analytics for the selected time period. [https://github.com/woocommerce/woocommerce-android/pull/12161] 19.7 ----- From a16f8f107d1cabcd8636946cf468e52a2672b86e Mon Sep 17 00:00:00 2001 From: ThomazFB Date: Mon, 29 Jul 2024 04:05:10 -0300 Subject: [PATCH 24/24] Update Google CTA Web View title --- .../android/ui/analytics/hub/AnalyticsHubViewModel.kt | 2 +- WooCommerce/src/main/res/values/strings.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt index de51b25508b..5d6e2efe47f 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/analytics/hub/AnalyticsHubViewModel.kt @@ -833,7 +833,7 @@ class AnalyticsHubViewModel @Inject constructor( OpenGoogleAdsCreation( url = url, isCreationFlow = true, - title = resourceProvider.getString(R.string.analytics_google_ads_cta_action) + title = resourceProvider.getString(R.string.analytics_google_ads_cta_web_view_title) ) ) diff --git a/WooCommerce/src/main/res/values/strings.xml b/WooCommerce/src/main/res/values/strings.xml index 652fe2d956e..0a50b6088b4 100644 --- a/WooCommerce/src/main/res/values/strings.xml +++ b/WooCommerce/src/main/res/values/strings.xml @@ -543,6 +543,7 @@ Google Campaigns Drive sales and generate more traffic with Google Ads. Add paid campaign + Google for WooCommerce See your stats, revenue and more from your device! We\'ve been working on making it possible to display significant store information from your device! Could it be better? Please help us improve this feature by sharing your feedback with us