From 07eb0156ea7668fb2b9bc86320e9d68e2cb89bfa Mon Sep 17 00:00:00 2001 From: Ondrej Ruttkay Date: Mon, 5 Feb 2024 15:33:40 +0100 Subject: [PATCH 1/4] Add ad destination boilerplate view model, fragment and screen --- ...zeCampaignCreationAdDestinationFragment.kt | 44 +++++++ ...lazeCampaignCreationAdDestinationScreen.kt | 115 ++++++++++++++++++ ...eCampaignCreationAdDestinationViewModel.kt | 43 +++++++ 3 files changed, 202 insertions(+) create mode 100644 WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/destination/BlazeCampaignCreationAdDestinationFragment.kt create mode 100644 WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/destination/BlazeCampaignCreationAdDestinationScreen.kt create mode 100644 WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/destination/BlazeCampaignCreationAdDestinationViewModel.kt diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/destination/BlazeCampaignCreationAdDestinationFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/destination/BlazeCampaignCreationAdDestinationFragment.kt new file mode 100644 index 00000000000..d9bee415559 --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/destination/BlazeCampaignCreationAdDestinationFragment.kt @@ -0,0 +1,44 @@ +package com.woocommerce.android.ui.blaze.creation.destination + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController +import com.woocommerce.android.ui.base.BaseFragment +import com.woocommerce.android.ui.compose.composeView +import com.woocommerce.android.ui.main.AppBarStatus +import com.woocommerce.android.viewmodel.MultiLiveEvent +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class BlazeCampaignCreationAdDestinationFragment : BaseFragment() { + private val viewModel: BlazeCampaignCreationAdDestinationViewModel by viewModels() + + override val activityAppBarStatus: AppBarStatus + get() = AppBarStatus.Hidden + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + return composeView { + BlazeCampaignCreationAdDestinationScreen(viewModel) + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + handleEvents() + handleResults() + } + + private fun handleEvents() { + viewModel.event.observe(viewLifecycleOwner) { event -> + when (event) { + is MultiLiveEvent.Event.Exit -> findNavController().navigateUp() + } + } + } + + private fun handleResults() { + /* TODO */ + } +} diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/destination/BlazeCampaignCreationAdDestinationScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/destination/BlazeCampaignCreationAdDestinationScreen.kt new file mode 100644 index 00000000000..b17c3ff64c4 --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/destination/BlazeCampaignCreationAdDestinationScreen.kt @@ -0,0 +1,115 @@ +package com.woocommerce.android.ui.blaze.creation.destination + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Divider +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Scaffold +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons.Filled +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.runtime.Composable +import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import com.woocommerce.android.R +import com.woocommerce.android.ui.blaze.creation.destination.BlazeCampaignCreationAdDestinationViewModel.ViewState +import com.woocommerce.android.ui.compose.component.Toolbar +import com.woocommerce.android.ui.compose.preview.LightDarkThemePreviews +import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground + +@Composable +fun BlazeCampaignCreationAdDestinationScreen(viewModel: BlazeCampaignCreationAdDestinationViewModel) { + viewModel.viewState.observeAsState().value?.let { previewState -> + AdDestinationScreen( + previewState, + viewModel::onBackPressed, + viewModel::onUrlPropertyTapped, + viewModel::onParameterPropertyTapped + ) + } +} + +@Composable +fun AdDestinationScreen( + viewState: ViewState, + onBackPressed: () -> Unit, + onUrlPropertyTapped: () -> Unit, + onParametersPropertyTapped: () -> Unit +) { + Scaffold( + topBar = { + Toolbar( + title = stringResource(id = R.string.blaze_campaign_preview_details_destination_url), + onNavigationButtonClick = onBackPressed, + navigationIcon = Filled.ArrowBack + ) + }, + modifier = Modifier.background(MaterialTheme.colors.surface) + ) { paddingValues -> + Column( + Modifier + .background(MaterialTheme.colors.surface) + .padding(paddingValues) + .fillMaxSize() + ) { + AdDestinationProperty( + title = stringResource(id = R.string.blaze_campaign_edit_ad_destination_url_property_title), + value = viewState.destinationUrl, + onPropertyTapped = onUrlPropertyTapped + ) + Divider() + AdDestinationProperty( + title = stringResource(id = R.string.blaze_campaign_edit_ad_destination_parameters_property_title), + value = viewState.parameters, + onPropertyTapped = onParametersPropertyTapped + ) + } + } +} + +@Composable +fun AdDestinationProperty(title: String, value: String, onPropertyTapped: () -> Unit) { + Column( + modifier = Modifier + .clickable { onPropertyTapped() } + .padding(dimensionResource(id = R.dimen.major_100)) + .fillMaxWidth(1f) + ) { + Text( + text = title, + style = MaterialTheme.typography.subtitle1, + color = colorResource(id = R.color.color_on_surface_high) + ) + Text( + text = value, + style = MaterialTheme.typography.body2, + color = colorResource(id = R.color.color_on_surface_medium), + maxLines = 3, + overflow = TextOverflow.Ellipsis + ) + } +} + +@LightDarkThemePreviews +@Composable +fun PreviewAdDestinationScreen() { + WooThemeWithBackground { + AdDestinationScreen( + viewState = ViewState( + destinationUrl = "https://woocommerce.com", + parameters = "utm_source=woocommerce\nutm_medium=android\nutm_campaign=blaze" + ), + onBackPressed = {}, + onUrlPropertyTapped = {}, + onParametersPropertyTapped = {} + ) + } +} diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/destination/BlazeCampaignCreationAdDestinationViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/destination/BlazeCampaignCreationAdDestinationViewModel.kt new file mode 100644 index 00000000000..191a24879a6 --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/destination/BlazeCampaignCreationAdDestinationViewModel.kt @@ -0,0 +1,43 @@ +package com.woocommerce.android.ui.blaze.creation.destination + +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.asLiveData +import com.woocommerce.android.R +import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.Exit +import com.woocommerce.android.viewmodel.ResourceProvider +import com.woocommerce.android.viewmodel.ScopedViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import javax.inject.Inject + +@HiltViewModel +class BlazeCampaignCreationAdDestinationViewModel @Inject constructor( + savedStateHandle: SavedStateHandle, + resourceProvider: ResourceProvider +) : ScopedViewModel(savedStateHandle) { + private val _viewState = MutableStateFlow( + ViewState( + destinationUrl = resourceProvider.getString(R.string.blaze_campaign_edit_ad_destination_empty_url_message), + parameters = resourceProvider.getString(R.string.blaze_campaign_edit_ad_destination_empty_parameters_message) + ) + ) + + val viewState = _viewState.asLiveData() + + fun onBackPressed() { + triggerEvent(Exit) + } + + fun onUrlPropertyTapped() { + /* TODO */ + } + + fun onParameterPropertyTapped() { + /* TODO */ + } + + data class ViewState( + val destinationUrl: String, + val parameters: String + ) +} From c2547ced7a2ae28cd1c3bec4bc65924318b2d8bb Mon Sep 17 00:00:00 2001 From: Ondrej Ruttkay Date: Mon, 5 Feb 2024 15:34:05 +0100 Subject: [PATCH 2/4] Add navigation to ad destination from ad preview --- .../BlazeCampaignCreationPreviewFragment.kt | 5 +++++ .../BlazeCampaignCreationPreviewViewModel.kt | 21 ++++++++++++------- .../nav_graph_blaze_campaign_creation.xml | 7 +++++++ 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/preview/BlazeCampaignCreationPreviewFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/preview/BlazeCampaignCreationPreviewFragment.kt index b708dc42a64..efdf1c7df19 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/preview/BlazeCampaignCreationPreviewFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/preview/BlazeCampaignCreationPreviewFragment.kt @@ -11,6 +11,7 @@ import com.woocommerce.android.extensions.navigateSafely import com.woocommerce.android.ui.base.BaseFragment import com.woocommerce.android.ui.blaze.creation.ad.BlazeCampaignCreationEditAdFragment import com.woocommerce.android.ui.blaze.creation.ad.BlazeCampaignCreationEditAdViewModel.EditAdResult +import com.woocommerce.android.ui.blaze.creation.preview.BlazeCampaignCreationPreviewViewModel.NavigateToAdDestinationScreen import com.woocommerce.android.ui.blaze.creation.preview.BlazeCampaignCreationPreviewViewModel.NavigateToBudgetScreen import com.woocommerce.android.ui.blaze.creation.preview.BlazeCampaignCreationPreviewViewModel.NavigateToEditAdScreen import com.woocommerce.android.ui.blaze.creation.preview.BlazeCampaignCreationPreviewViewModel.NavigateToTargetSelectionScreen @@ -64,6 +65,10 @@ class BlazeCampaignCreationPreviewFragment : BaseFragment() { event.selectedIds.toTypedArray() ) ) + is NavigateToAdDestinationScreen -> findNavController().navigateSafely( + BlazeCampaignCreationPreviewFragmentDirections + .actionBlazeCampaignCreationPreviewFragmentToBlazeCampaignCreationAdDestinationFragment() + ) } } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/preview/BlazeCampaignCreationPreviewViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/preview/BlazeCampaignCreationPreviewViewModel.kt index ba679268669..c47771eb2d6 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/preview/BlazeCampaignCreationPreviewViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/preview/BlazeCampaignCreationPreviewViewModel.kt @@ -196,8 +196,10 @@ class BlazeCampaignCreationPreviewViewModel @Inject constructor( destinationUrl = CampaignDetailItemUi( displayTitle = resourceProvider.getString(string.blaze_campaign_preview_details_destination_url), displayValue = targetUrl, - onItemSelected = { /* TODO Add destination url selection */ }, maxLinesValue = 1, + onItemSelected = { + triggerEvent(NavigateToAdDestinationScreen) + } ) ) @@ -222,13 +224,6 @@ class BlazeCampaignCreationPreviewViewModel @Inject constructor( startDate = Date().apply { time += BlazeRepository.ONE_DAY_IN_MILLIS }, // By default start tomorrow ) - data class NavigateToEditAdScreen( - val productId: Long, - val tagLine: String, - val description: String, - val campaignImageUrl: String? - ) : MultiLiveEvent.Event() - data class CampaignPreviewUiState( val adDetails: AdDetailsUi, val campaignDetails: CampaignDetailsUi, @@ -261,8 +256,18 @@ class BlazeCampaignCreationPreviewViewModel @Inject constructor( ) object NavigateToBudgetScreen : MultiLiveEvent.Event() + + object NavigateToAdDestinationScreen : MultiLiveEvent.Event() + data class NavigateToTargetSelectionScreen( val targetType: BlazeTargetType, val selectedIds: List ) : MultiLiveEvent.Event() + + data class NavigateToEditAdScreen( + val productId: Long, + val tagLine: String, + val description: String, + val campaignImageUrl: String? + ) : MultiLiveEvent.Event() } diff --git a/WooCommerce/src/main/res/navigation/nav_graph_blaze_campaign_creation.xml b/WooCommerce/src/main/res/navigation/nav_graph_blaze_campaign_creation.xml index 22ed9b6623b..61ace75e218 100644 --- a/WooCommerce/src/main/res/navigation/nav_graph_blaze_campaign_creation.xml +++ b/WooCommerce/src/main/res/navigation/nav_graph_blaze_campaign_creation.xml @@ -59,6 +59,9 @@ + + From 135db48b05f283abec499e7e64a31c20996c36e2 Mon Sep 17 00:00:00 2001 From: Ondrej Ruttkay Date: Mon, 5 Feb 2024 15:34:16 +0100 Subject: [PATCH 3/4] Ad string resources --- WooCommerce/src/main/res/values/strings.xml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/WooCommerce/src/main/res/values/strings.xml b/WooCommerce/src/main/res/values/strings.xml index 7ef7a5426aa..e704ffa6970 100644 --- a/WooCommerce/src/main/res/values/strings.xml +++ b/WooCommerce/src/main/res/values/strings.xml @@ -3879,7 +3879,13 @@ Promote with Blaze Ready to promote Promote - + + Ad link destination + Enter manually + Destination URL + URL parameters From 19ca63dec65245a08f6be331188f603f5f08b9e2 Mon Sep 17 00:00:00 2001 From: Ondrej Ruttkay Date: Mon, 5 Feb 2024 18:31:34 +0100 Subject: [PATCH 4/4] Fix ktlint issue --- .../destination/BlazeCampaignCreationAdDestinationViewModel.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/destination/BlazeCampaignCreationAdDestinationViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/destination/BlazeCampaignCreationAdDestinationViewModel.kt index 191a24879a6..e48fe2fd905 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/destination/BlazeCampaignCreationAdDestinationViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/creation/destination/BlazeCampaignCreationAdDestinationViewModel.kt @@ -18,7 +18,8 @@ class BlazeCampaignCreationAdDestinationViewModel @Inject constructor( private val _viewState = MutableStateFlow( ViewState( destinationUrl = resourceProvider.getString(R.string.blaze_campaign_edit_ad_destination_empty_url_message), - parameters = resourceProvider.getString(R.string.blaze_campaign_edit_ad_destination_empty_parameters_message) + parameters = resourceProvider + .getString(R.string.blaze_campaign_edit_ad_destination_empty_parameters_message) ) )