Skip to content

Commit

Permalink
Merge pull request woocommerce#10820 from woocommerce/issue/10784-bla…
Browse files Browse the repository at this point in the history
…ze-analytics-part-II

Issue/10784 blaze analytics part ii
  • Loading branch information
JorgeMucientes authored Feb 26, 2024
2 parents c9b9256 + 7b5dbb8 commit 24ffc7e
Show file tree
Hide file tree
Showing 16 changed files with 192 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1011,6 +1011,22 @@ enum class AnalyticsEvent(val siteless: Boolean = false) {
BLAZE_CAMPAIGN_LIST_ENTRY_POINT_SELECTED,
BLAZE_INTRO_DISPLAYED,
BLAZE_VIEW_DISMISSED,
BLAZE_INTRO_LEARN_MORE_TAPPED,
BLAZE_CREATION_FORM_DISPLAYED,
BLAZE_CREATION_EDIT_AD_TAPPED,
BLAZE_CREATION_CONFIRM_DETAILS_TAPPED,
BLAZE_CREATION_PAYMENT_SUBMIT_CAMPAIGN_TAPPED,
BLAZE_CREATION_ADD_PAYMENT_METHOD_WEB_VIEW_DISPLAYED,
BLAZE_CREATION_ADD_PAYMENT_METHOD_SUCCESS,
BLAZE_CREATION_EDIT_AD_AI_SUGGESTION_TAPPED,
BLAZE_CREATION_EDIT_AD_SAVE_TAPPED,
BLAZE_CREATION_EDIT_BUDGET_SAVE_TAPPED,
BLAZE_CREATION_EDIT_BUDGET_SET_DURATION_APPLIED,
BLAZE_CREATION_EDIT_LANGUAGE_SAVE_TAPPED,
BLAZE_CREATION_EDIT_DEVICE_SAVE_TAPPED,
BLAZE_CREATION_EDIT_INTEREST_SAVE_TAPPED,
BLAZE_CREATION_EDIT_LOCATION_SAVE_TAPPED,
BLAZE_CREATION_EDIT_DESTINATION_SAVE_TAPPED,

// Hazmat Shipping Declaration
CONTAINS_HAZMAT_CHECKED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,9 @@ class AnalyticsTracker private constructor(
// -- Blaze
const val KEY_BLAZE_SOURCE = "source"
const val KEY_BLAZE_STEP = "step"
const val KEY_BLAZE_DURATION = "duration"
const val KEY_BLAZE_TOTAL_BUDGET = "total_budget"
const val KEY_BLAZE_IS_AI_CONTENT = "is_ai_suggested_ad_content"

const val PRODUCT_TYPES = "product_types"
const val HAS_ADDONS = "has_addons"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ class BlazeRepository @Inject constructor(
val campaignImage: BlazeCampaignImage,
val budget: Budget,
val targetingParameters: TargetingParameters,
val destinationParameters: DestinationParameters
val destinationParameters: DestinationParameters,
) : Parcelable

sealed interface BlazeCampaignImage : Parcelable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ fun CampaignBudgetScreen(viewModel: BlazeCampaignBudgetViewModel) {
onEditDurationTapped = viewModel::onEditDurationTapped,
onImpressionsInfoTapped = viewModel::onImpressionsInfoTapped,
onBudgetUpdated = viewModel::onBudgetUpdated,
onCampaignDurationUpdated = viewModel::onCampaignDurationUpdated,
onStartDateChanged = viewModel::onStartDateChanged,
onBudgetChangeFinished = viewModel::onBudgetChangeFinished,
onUpdateTapped = viewModel::onUpdateTapped
onUpdateTapped = viewModel::onUpdateTapped,
onApplyDurationTapped = viewModel::onApplyDurationTapped
)
}
}
Expand All @@ -81,10 +81,10 @@ private fun CampaignBudgetScreen(
onEditDurationTapped: () -> Unit,
onImpressionsInfoTapped: () -> Unit,
onBudgetUpdated: (Float) -> Unit,
onCampaignDurationUpdated: (Int) -> Unit,
onStartDateChanged: (Long) -> Unit,
onBudgetChangeFinished: () -> Unit,
onUpdateTapped: () -> Unit
onUpdateTapped: () -> Unit,
onApplyDurationTapped: (Int) -> Unit
) {
val coroutineScope = rememberCoroutineScope()
val modalSheetState = rememberModalBottomSheetState(
Expand Down Expand Up @@ -115,9 +115,11 @@ private fun CampaignBudgetScreen(

state.showCampaignDurationBottomSheet -> EditDurationBottomSheet(
budgetUiState = state,
onDurationChanged = { onCampaignDurationUpdated(it) },
onStartDateChanged = { onStartDateChanged(it) },
onApplyTapped = { coroutineScope.launch { modalSheetState.hide() } }
onApplyTapped = {
onApplyDurationTapped(it)
coroutineScope.launch { modalSheetState.hide() }
}
)
}
}
Expand Down Expand Up @@ -326,15 +328,14 @@ private fun ImpressionsInfoBottomSheet(
@Composable
private fun EditDurationBottomSheet(
budgetUiState: BlazeCampaignBudgetViewModel.BudgetUiState,
onDurationChanged: (Int) -> Unit,
onStartDateChanged: (Long) -> Unit,
onApplyTapped: () -> Unit,
onApplyTapped: (Int) -> Unit,
modifier: Modifier = Modifier,
) {
var showDatePicker by remember { mutableStateOf(false) }
if (showDatePicker) {
DatePickerDialog(
currentDate = Date(budgetUiState.campaignStartDateMillis),
currentDate = Date(budgetUiState.confirmedCampaignStartDateMillis),
onDateSelected = {
onStartDateChanged(it.time)
showDatePicker = false
Expand Down Expand Up @@ -369,7 +370,6 @@ private fun EditDurationBottomSheet(
value = sliderPosition,
valueRange = budgetUiState.durationRangeMin..budgetUiState.durationRangeMax,
onValueChange = { sliderPosition = it },
onValueChangeFinished = { onDurationChanged(sliderPosition.toInt()) },
colors = SliderDefaults.colors(
inactiveTrackColor = colorResource(id = color.divider_color)
)
Expand All @@ -389,7 +389,7 @@ private fun EditDurationBottomSheet(
.clip(RoundedCornerShape(4.dp))
.background(colorResource(id = color.divider_color))
.padding(8.dp),
text = Date(budgetUiState.campaignStartDateMillis).formatToMMMddYYYY(),
text = Date(budgetUiState.bottomSheetCampaignStartDateMillis).formatToMMMddYYYY(),
style = MaterialTheme.typography.body1,
)
}
Expand All @@ -400,7 +400,7 @@ private fun EditDurationBottomSheet(
bottom = 16.dp
)
.fillMaxWidth(),
onClick = onApplyTapped,
onClick = { onApplyTapped(sliderPosition.toInt()) },
text = stringResource(id = R.string.blaze_campaign_budget_duration_bottom_sheet_apply_button)
)
}
Expand All @@ -425,7 +425,8 @@ private fun CampaignBudgetScreenPreview() {
impressionsMax = 0,
isError = false
),
campaignStartDateMillis = Date().time,
confirmedCampaignStartDateMillis = Date().time,
bottomSheetCampaignStartDateMillis = Date().time,
campaignDurationDates = "Dec 13 - Dec 20, 2023",
showImpressionsBottomSheet = false,
showCampaignDurationBottomSheet = false
Expand All @@ -434,10 +435,10 @@ private fun CampaignBudgetScreenPreview() {
onEditDurationTapped = {},
onImpressionsInfoTapped = {},
onBudgetUpdated = {},
onCampaignDurationUpdated = {},
onStartDateChanged = {},
onUpdateTapped = {},
onBudgetChangeFinished = {}
onBudgetChangeFinished = {},
onApplyDurationTapped = {}
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import android.os.Parcelable
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import com.woocommerce.android.analytics.AnalyticsEvent.BLAZE_CREATION_EDIT_BUDGET_SAVE_TAPPED
import com.woocommerce.android.analytics.AnalyticsEvent.BLAZE_CREATION_EDIT_BUDGET_SET_DURATION_APPLIED
import com.woocommerce.android.analytics.AnalyticsTracker
import com.woocommerce.android.analytics.AnalyticsTrackerWrapper
import com.woocommerce.android.extensions.formatToMMMdd
import com.woocommerce.android.extensions.formatToMMMddYYYY
import com.woocommerce.android.ui.blaze.BlazeRepository
Expand All @@ -23,14 +27,14 @@ import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
import java.util.Date
import javax.inject.Inject
import kotlin.math.roundToInt
import kotlin.time.Duration.Companion.days

@HiltViewModel
class BlazeCampaignBudgetViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val currencyFormatter: CurrencyFormatter,
private val repository: BlazeRepository
private val repository: BlazeRepository,
private val analyticsTrackerWrapper: AnalyticsTrackerWrapper
) : ScopedViewModel(savedStateHandle) {
private val navArgs: BlazeCampaignBudgetFragmentArgs by savedStateHandle.navArgs()

Expand All @@ -48,7 +52,8 @@ class BlazeCampaignBudgetViewModel @Inject constructor(
durationInDays = navArgs.budget.durationInDays,
durationRangeMin = 1f,
durationRangeMax = CAMPAIGN_MAX_DURATION.toFloat(),
campaignStartDateMillis = navArgs.budget.startDate.time,
confirmedCampaignStartDateMillis = navArgs.budget.startDate.time,
bottomSheetCampaignStartDateMillis = navArgs.budget.startDate.time,
campaignDurationDates = getCampaignDurationDisplayDate(
navArgs.budget.startDate.time, navArgs.budget.durationInDays
),
Expand Down Expand Up @@ -80,11 +85,18 @@ class BlazeCampaignBudgetViewModel @Inject constructor(
totalBudget = budgetUiState.value.totalBudget,
spentBudget = 0f,
durationInDays = budgetUiState.value.durationInDays,
startDate = Date(budgetUiState.value.campaignStartDateMillis),
startDate = Date(budgetUiState.value.confirmedCampaignStartDateMillis),
currencyCode = budgetUiState.value.currencyCode,
)
)
)
analyticsTrackerWrapper.track(
stat = BLAZE_CREATION_EDIT_BUDGET_SAVE_TAPPED,
properties = mapOf(
AnalyticsTracker.KEY_BLAZE_DURATION to budgetUiState.value.durationInDays,
AnalyticsTracker.KEY_BLAZE_TOTAL_BUDGET to budgetUiState.value.totalBudget,
)
)
}

fun onEditDurationTapped() {
Expand Down Expand Up @@ -114,26 +126,35 @@ class BlazeCampaignBudgetViewModel @Inject constructor(
}
}

fun onCampaignDurationUpdated(duration: Int) {
val currentDailyExpend = budgetUiState.value.totalBudget / budgetUiState.value.durationInDays
val newTotalBudget = duration * currentDailyExpend
fun onApplyDurationTapped(newDuration: Int) {
val currentDailySpend = calculateDailySpending(newDuration)
budgetUiState.update {
it.copy(
durationInDays = duration,
budgetRangeMin = duration * CAMPAIGN_MINIMUM_DAILY_SPEND,
budgetRangeMax = duration * CAMPAIGN_MAXIMUM_DAILY_SPEND,
dailySpending = formatDailySpend(currentDailyExpend),
totalBudget = newTotalBudget,
campaignDurationDates = getCampaignDurationDisplayDate(it.campaignStartDateMillis, duration)
durationInDays = newDuration,
budgetRangeMin = newDuration * CAMPAIGN_MINIMUM_DAILY_SPEND,
budgetRangeMax = newDuration * CAMPAIGN_MAXIMUM_DAILY_SPEND,
dailySpending = formatDailySpend(currentDailySpend),
totalBudget = newDuration * currentDailySpend,
confirmedCampaignStartDateMillis = it.bottomSheetCampaignStartDateMillis,
campaignDurationDates = getCampaignDurationDisplayDate(
it.bottomSheetCampaignStartDateMillis,
newDuration
)
)
}
fetchAdForecast()
analyticsTrackerWrapper.track(
stat = BLAZE_CREATION_EDIT_BUDGET_SET_DURATION_APPLIED,
properties = mapOf(
AnalyticsTracker.KEY_BLAZE_DURATION to budgetUiState.value.durationInDays,
)
)
}

fun onStartDateChanged(newStartDateMillis: Long) {
budgetUiState.update {
it.copy(
campaignStartDateMillis = newStartDateMillis,
bottomSheetCampaignStartDateMillis = newStartDateMillis,
campaignDurationDates = getCampaignDurationDisplayDate(
newStartDateMillis,
it.durationInDays
Expand All @@ -145,18 +166,22 @@ class BlazeCampaignBudgetViewModel @Inject constructor(
fun onBudgetChangeFinished() {
fetchAdForecast()
val roundedBudgetToDurationMultiple =
(budgetUiState.value.totalBudget / budgetUiState.value.durationInDays).roundToInt() *
budgetUiState.value.durationInDays
calculateDailySpending(budgetUiState.value.durationInDays) * budgetUiState.value.durationInDays
budgetUiState.update {
it.copy(totalBudget = roundedBudgetToDurationMultiple.toFloat())
it.copy(totalBudget = roundedBudgetToDurationMultiple)
}
}

private fun calculateDailySpending(duration: Int): Float {
val dailySpend = budgetUiState.value.totalBudget / duration
return dailySpend.coerceIn(CAMPAIGN_MINIMUM_DAILY_SPEND, CAMPAIGN_MAXIMUM_DAILY_SPEND)
}

private fun fetchAdForecast() {
campaignForecastState = campaignForecastState.copy(isLoading = true)
launch {
repository.fetchAdForecast(
startDate = Date(budgetUiState.value.campaignStartDateMillis),
startDate = Date(budgetUiState.value.confirmedCampaignStartDateMillis),
campaignDurationDays = budgetUiState.value.durationInDays,
totalBudget = budgetUiState.value.totalBudget,
targetingParameters = navArgs.targetingParameters
Expand Down Expand Up @@ -205,7 +230,8 @@ class BlazeCampaignBudgetViewModel @Inject constructor(
val durationRangeMax: Float,
val showImpressionsBottomSheet: Boolean,
val showCampaignDurationBottomSheet: Boolean,
val campaignStartDateMillis: Long,
val confirmedCampaignStartDateMillis: Long,
val bottomSheetCampaignStartDateMillis: Long,
val campaignDurationDates: String,
) : Parcelable

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package com.woocommerce.android.ui.blaze.creation.destination

import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.asLiveData
import com.woocommerce.android.analytics.AnalyticsEvent.BLAZE_CREATION_EDIT_DESTINATION_SAVE_TAPPED
import com.woocommerce.android.analytics.AnalyticsTrackerWrapper
import com.woocommerce.android.tools.SelectedSite
import com.woocommerce.android.ui.blaze.BlazeRepository.DestinationParameters
import com.woocommerce.android.ui.products.ProductDetailRepository
Expand All @@ -18,10 +20,12 @@ import javax.inject.Inject
class BlazeCampaignCreationAdDestinationViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
selectedSite: SelectedSite,
productDetailRepository: ProductDetailRepository
productDetailRepository: ProductDetailRepository,
private val analyticsTrackerWrapper: AnalyticsTrackerWrapper,
) : ScopedViewModel(savedStateHandle) {
private val navArgs: BlazeCampaignCreationAdDestinationFragmentArgs by savedStateHandle.navArgs()
private val productUrl = requireNotNull(productDetailRepository.getProduct(navArgs.productId)).permalink
private val initialDestinationValues: ViewState

private val _viewState = MutableStateFlow(
ViewState(
Expand All @@ -35,7 +39,14 @@ class BlazeCampaignCreationAdDestinationViewModel @Inject constructor(

val viewState = _viewState.asLiveData()

init {
initialDestinationValues = _viewState.value
}

fun onBackPressed() {
if (initialDestinationValues != _viewState.value) {
analyticsTrackerWrapper.track(stat = BLAZE_CREATION_EDIT_DESTINATION_SAVE_TAPPED)
}
triggerEvent(ExitWithResult(DestinationParameters(_viewState.value.targetUrl, _viewState.value.parameters)))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ fun BlazeCampaignCreationIntroScreen(
WooThemeWithBackground {
BlazeCampaignCreationIntroScreen(
onContinueClick = viewModel::onContinueClick,
onDismissClick = viewModel::onDismissClick
onDismissClick = viewModel::onDismissClick,
onLearnMoreClick = viewModel::onLearnMoreClick
)
}
}
Expand All @@ -74,7 +75,8 @@ fun BlazeCampaignCreationIntroScreen(
@Composable
fun BlazeCampaignCreationIntroScreen(
onContinueClick: () -> Unit,
onDismissClick: () -> Unit
onDismissClick: () -> Unit,
onLearnMoreClick: () -> Unit,
) {
Scaffold(
topBar = {
Expand Down Expand Up @@ -109,6 +111,7 @@ fun BlazeCampaignCreationIntroScreen(
onContinueClick = onContinueClick,
onLearnMoreClick = {
coroutineScope.launch { modalSheetState.show() }
onLearnMoreClick()
}
)
}
Expand Down Expand Up @@ -336,10 +339,12 @@ fun StepItem(currentStep: Int, steps: Int, text: AnnotatedString) {
topStart = dimensionResource(id = R.dimen.minor_100),
topEnd = dimensionResource(id = R.dimen.minor_100)
)

currentStep + 1 == steps -> RoundedCornerShape(
bottomStart = dimensionResource(id = R.dimen.minor_100),
bottomEnd = dimensionResource(id = R.dimen.minor_100)
)

else -> RoundedCornerShape(0.dp)
}

Expand Down Expand Up @@ -369,7 +374,8 @@ private fun BlazeCampaignCreationIntroScreenPreview() {
WooThemeWithBackground {
BlazeCampaignCreationIntroScreen(
onContinueClick = {},
onDismissClick = {}
onDismissClick = {},
onLearnMoreClick = {}
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.woocommerce.android.ui.blaze.creation.intro
import androidx.lifecycle.SavedStateHandle
import com.woocommerce.android.analytics.AnalyticsEvent.BLAZE_ENTRY_POINT_TAPPED
import com.woocommerce.android.analytics.AnalyticsEvent.BLAZE_INTRO_DISPLAYED
import com.woocommerce.android.analytics.AnalyticsEvent.BLAZE_INTRO_LEARN_MORE_TAPPED
import com.woocommerce.android.analytics.AnalyticsTracker
import com.woocommerce.android.analytics.AnalyticsTrackerWrapper
import com.woocommerce.android.ui.blaze.BlazeUrlsHelper.BlazeFlowSource
Expand Down Expand Up @@ -78,6 +79,10 @@ class BlazeCampaignCreationIntroViewModel @Inject constructor(
triggerEvent(ShowCampaignCreationForm(productId, BlazeFlowSource.INTRO_VIEW))
}

fun onLearnMoreClick() {
analyticsTracker.track(stat = BLAZE_INTRO_LEARN_MORE_TAPPED)
}

object ShowProductSelector : MultiLiveEvent.Event()
data class ShowCampaignCreationForm(val productId: Long, val source: BlazeFlowSource) : MultiLiveEvent.Event()
}
Loading

0 comments on commit 24ffc7e

Please sign in to comment.