From 9ee26c10cd357ac56d55c9eaed557fb2a640b286 Mon Sep 17 00:00:00 2001 From: Hicham Boushaba Date: Mon, 16 Oct 2023 11:15:25 +0100 Subject: [PATCH 01/13] Bump fluxc version --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 01835a8bdaf..2a35ca6e921 100644 --- a/build.gradle +++ b/build.gradle @@ -95,7 +95,7 @@ tasks.register("installGitHooks", Copy) { } ext { - fluxCVersion = '2.50.0' + fluxCVersion = '2871-0eff506ba6c0aea40d51c3e295a5d6dc1c636336' glideVersion = '4.13.2' coilVersion = '2.1.0' constraintLayoutVersion = '1.2.0' From 4da442031dc0a4921ddf010a462df48c341c87fb Mon Sep 17 00:00:00 2001 From: Hicham Boushaba Date: Mon, 16 Oct 2023 11:16:33 +0100 Subject: [PATCH 02/13] Logic for using the appropriate state depending on last ca --- .../android/ui/blaze/MyStoreBlazeViewModel.kt | 52 ++++++++++++------- .../blaze/ObserveMostRecentBlazeCampaign.kt | 18 +++++++ 2 files changed, 52 insertions(+), 18 deletions(-) create mode 100644 WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/ObserveMostRecentBlazeCampaign.kt diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt index 51bb8b190c7..7ff8a5055f8 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt @@ -5,41 +5,57 @@ import androidx.annotation.ColorRes import androidx.annotation.StringRes import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.asLiveData -import androidx.lifecycle.viewModelScope import com.woocommerce.android.R import com.woocommerce.android.util.FeatureFlag import com.woocommerce.android.viewmodel.ScopedViewModel -import com.woocommerce.android.viewmodel.getStateFlow import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.map import kotlinx.parcelize.Parcelize +import org.wordpress.android.fluxc.model.blaze.BlazeCampaignModel import javax.inject.Inject @HiltViewModel class MyStoreBlazeViewModel @Inject constructor( savedStateHandle: SavedStateHandle, + observeMostRecentBlazeCampaign: ObserveMostRecentBlazeCampaign ) : ScopedViewModel(savedStateHandle) { - private val _blazeCampaignState = - savedStateHandle.getStateFlow( - scope = viewModelScope, - initialValue = MyStoreBlazeUi( - isVisible = FeatureFlag.BLAZE_ITERATION_2.isEnabled(), + val blazeCampaignState = observeMostRecentBlazeCampaign().map { + when (it) { + null -> prepareUiForNoCampaign() + else -> prepareUiForCampaign(it) + } + }.asLiveData() + + private fun prepareUiForNoCampaign(): MyStoreBlazeUi { + return MyStoreBlazeUi( + isVisible = FeatureFlag.BLAZE_ITERATION_2.isEnabled(), + product = BlazeProductUi( + name = "Product name", + imgUrl = "https://hips.hearstapps.com/hmg-prod/images/gh-082420-ghi-best-sofas-1598293488.png", + ), + blazeActiveCampaign = null + ) + } + + private fun prepareUiForCampaign(campaign: BlazeCampaignModel): MyStoreBlazeUi { + return MyStoreBlazeUi( + isVisible = FeatureFlag.BLAZE_ITERATION_2.isEnabled(), + product = BlazeProductUi( + name = "Product name", + imgUrl = "https://hips.hearstapps.com/hmg-prod/images/gh-082420-ghi-best-sofas-1598293488.png", + ), + blazeActiveCampaign = BlazeCampaignUi( product = BlazeProductUi( name = "Product name", imgUrl = "https://hips.hearstapps.com/hmg-prod/images/gh-082420-ghi-best-sofas-1598293488.png", ), - blazeActiveCampaign = BlazeCampaignUi( - product = BlazeProductUi( - name = "Product name", - imgUrl = "https://hips.hearstapps.com/hmg-prod/images/gh-082420-ghi-best-sofas-1598293488.png", - ), - status = CampaignStatusUi.Active, - impressions = 100, - clicks = 10, - budget = 1000 - ) + status = CampaignStatusUi.Active, + impressions = 100, + clicks = 10, + budget = 1000 ) ) - val blazeCampaignState = _blazeCampaignState.asLiveData() + } @Parcelize data class MyStoreBlazeUi( diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/ObserveMostRecentBlazeCampaign.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/ObserveMostRecentBlazeCampaign.kt new file mode 100644 index 00000000000..f03dbddab41 --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/ObserveMostRecentBlazeCampaign.kt @@ -0,0 +1,18 @@ +package com.woocommerce.android.ui.blaze + +import com.woocommerce.android.tools.SelectedSite +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.onStart +import org.wordpress.android.fluxc.model.blaze.BlazeCampaignModel +import org.wordpress.android.fluxc.store.blaze.BlazeCampaignsStore +import javax.inject.Inject + +class ObserveMostRecentBlazeCampaign @Inject constructor( + private val selectedSite: SelectedSite, + private val blazeCampaignsStore: BlazeCampaignsStore +) { + operator fun invoke(): Flow = + blazeCampaignsStore.observeMostRecentBlazeCampaign(selectedSite.get()).onStart { + blazeCampaignsStore.fetchBlazeCampaigns(selectedSite.get()) + } +} From 8330553daa39881c7b3b9ea7ed458f05824ea016 Mon Sep 17 00:00:00 2001 From: Hicham Boushaba Date: Mon, 16 Oct 2023 11:51:08 +0100 Subject: [PATCH 03/13] Refactor campaign state modeling --- .../android/ui/blaze/MyStoreBlazeView.kt | 26 ++++----- .../android/ui/blaze/MyStoreBlazeViewModel.kt | 55 +++++++++---------- .../android/ui/mystore/MyStoreFragment.kt | 3 +- 3 files changed, 41 insertions(+), 43 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeView.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeView.kt index df47d4a30f4..86e8c44169f 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeView.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeView.kt @@ -35,14 +35,14 @@ import com.woocommerce.android.R import com.woocommerce.android.ui.blaze.MyStoreBlazeViewModel.BlazeCampaignUi import com.woocommerce.android.ui.blaze.MyStoreBlazeViewModel.BlazeProductUi import com.woocommerce.android.ui.blaze.MyStoreBlazeViewModel.CampaignStatusUi -import com.woocommerce.android.ui.blaze.MyStoreBlazeViewModel.MyStoreBlazeUi +import com.woocommerce.android.ui.blaze.MyStoreBlazeViewModel.MyStoreBlazeCampaignState import com.woocommerce.android.ui.compose.component.ListItemImage import com.woocommerce.android.ui.compose.component.WCTag import com.woocommerce.android.ui.compose.component.WCTextButton @Composable fun MyStoreBlazeView( - state: MyStoreBlazeUi, + state: MyStoreBlazeCampaignState, onCreateCampaignClicked: () -> Unit, onShowAllClicked: () -> Unit ) { @@ -61,14 +61,14 @@ fun MyStoreBlazeView( ) ) { BlazeCampaignHeader() - when { - state.blazeActiveCampaign != null -> BlazeCampaignItem( - campaign = state.blazeActiveCampaign, + when (state) { + is MyStoreBlazeCampaignState.Campaign -> BlazeCampaignItem( + campaign = state.campaign, onCampaignClicked = {}, modifier = Modifier.padding(top = dimensionResource(id = R.dimen.major_100)) ) - else -> { + is MyStoreBlazeCampaignState.NoCampaign -> { Text( modifier = Modifier.padding( top = dimensionResource(id = R.dimen.major_100), @@ -83,15 +83,17 @@ fun MyStoreBlazeView( modifier = Modifier.padding(top = dimensionResource(id = R.dimen.major_100)) ) } + else -> error("Invalid state") } } - when { - state.blazeActiveCampaign != null -> ShowAllOrCreateCampaignFooter( + when (state) { + is MyStoreBlazeCampaignState.Campaign -> ShowAllOrCreateCampaignFooter( onShowAllClicked, onCreateCampaignClicked ) - else -> CreateCampaignFooter(onCreateCampaignClicked) + is MyStoreBlazeCampaignState.NoCampaign -> CreateCampaignFooter(onCreateCampaignClicked) + else -> error("Invalid state") } } } @@ -284,10 +286,8 @@ fun MyStoreBlazeViewPreview() { imgUrl = "", ) MyStoreBlazeView( - state = MyStoreBlazeUi( - isVisible = true, - product = product, - blazeActiveCampaign = BlazeCampaignUi( + state = MyStoreBlazeCampaignState.Campaign( + campaign = BlazeCampaignUi( product = product, status = CampaignStatusUi.Active, impressions = 100, diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt index 7ff8a5055f8..c37a4221c96 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt @@ -1,6 +1,5 @@ package com.woocommerce.android.ui.blaze -import android.os.Parcelable import androidx.annotation.ColorRes import androidx.annotation.StringRes import androidx.lifecycle.SavedStateHandle @@ -9,8 +8,9 @@ import com.woocommerce.android.R import com.woocommerce.android.util.FeatureFlag import com.woocommerce.android.viewmodel.ScopedViewModel import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.emitAll +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map -import kotlinx.parcelize.Parcelize import org.wordpress.android.fluxc.model.blaze.BlazeCampaignModel import javax.inject.Inject @@ -19,32 +19,32 @@ class MyStoreBlazeViewModel @Inject constructor( savedStateHandle: SavedStateHandle, observeMostRecentBlazeCampaign: ObserveMostRecentBlazeCampaign ) : ScopedViewModel(savedStateHandle) { - val blazeCampaignState = observeMostRecentBlazeCampaign().map { - when (it) { - null -> prepareUiForNoCampaign() - else -> prepareUiForCampaign(it) + val blazeCampaignState = flow { + if (!FeatureFlag.BLAZE_ITERATION_2.isEnabled()) emit(MyStoreBlazeCampaignState.Hidden) + else { + emitAll( + observeMostRecentBlazeCampaign().map { + when (it) { + null -> prepareUiForNoCampaign() + else -> prepareUiForCampaign(it) + } + } + ) } }.asLiveData() - private fun prepareUiForNoCampaign(): MyStoreBlazeUi { - return MyStoreBlazeUi( - isVisible = FeatureFlag.BLAZE_ITERATION_2.isEnabled(), + private fun prepareUiForNoCampaign(): MyStoreBlazeCampaignState { + return MyStoreBlazeCampaignState.NoCampaign( product = BlazeProductUi( name = "Product name", imgUrl = "https://hips.hearstapps.com/hmg-prod/images/gh-082420-ghi-best-sofas-1598293488.png", - ), - blazeActiveCampaign = null + ) ) } - private fun prepareUiForCampaign(campaign: BlazeCampaignModel): MyStoreBlazeUi { - return MyStoreBlazeUi( - isVisible = FeatureFlag.BLAZE_ITERATION_2.isEnabled(), - product = BlazeProductUi( - name = "Product name", - imgUrl = "https://hips.hearstapps.com/hmg-prod/images/gh-082420-ghi-best-sofas-1598293488.png", - ), - blazeActiveCampaign = BlazeCampaignUi( + private fun prepareUiForCampaign(campaign: BlazeCampaignModel): MyStoreBlazeCampaignState { + return MyStoreBlazeCampaignState.Campaign( + campaign = BlazeCampaignUi( product = BlazeProductUi( name = "Product name", imgUrl = "https://hips.hearstapps.com/hmg-prod/images/gh-082420-ghi-best-sofas-1598293488.png", @@ -57,27 +57,24 @@ class MyStoreBlazeViewModel @Inject constructor( ) } - @Parcelize - data class MyStoreBlazeUi( - val isVisible: Boolean, - val product: BlazeProductUi, - val blazeActiveCampaign: BlazeCampaignUi? - ) : Parcelable + sealed interface MyStoreBlazeCampaignState { + object Hidden : MyStoreBlazeCampaignState + data class NoCampaign(val product: BlazeProductUi) : MyStoreBlazeCampaignState + data class Campaign(val campaign: BlazeCampaignUi) : MyStoreBlazeCampaignState + } - @Parcelize data class BlazeProductUi( val name: String, val imgUrl: String - ) : Parcelable + ) - @Parcelize data class BlazeCampaignUi( val product: BlazeProductUi, val status: CampaignStatusUi, val impressions: Int, val clicks: Int, val budget: Int - ) : Parcelable + ) enum class CampaignStatusUi( @StringRes val statusDisplayText: Int, diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/mystore/MyStoreFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/mystore/MyStoreFragment.kt index b110f44a288..655225cd543 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/mystore/MyStoreFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/mystore/MyStoreFragment.kt @@ -49,6 +49,7 @@ import com.woocommerce.android.ui.blaze.BlazeBannerViewModel import com.woocommerce.android.ui.blaze.IsBlazeEnabled.BlazeFlowSource.MY_STORE_BANNER import com.woocommerce.android.ui.blaze.MyStoreBlazeView import com.woocommerce.android.ui.blaze.MyStoreBlazeViewModel +import com.woocommerce.android.ui.blaze.MyStoreBlazeViewModel.MyStoreBlazeCampaignState import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground import com.woocommerce.android.ui.feedback.SurveyType import com.woocommerce.android.ui.jitm.JitmFragment @@ -262,7 +263,7 @@ class MyStoreFragment : private fun setUpBlazeCampaignView() { myStoreBlazeViewModel.blazeCampaignState.observe(viewLifecycleOwner) { blazeCampaignState -> - if (!blazeCampaignState.isVisible) binding.blazeCampaignView.hide() + if (blazeCampaignState is MyStoreBlazeCampaignState.Hidden) binding.blazeCampaignView.hide() else { binding.blazeCampaignView.apply { setContent { From 691a86a048ef53805344bad01808063a67ac134c Mon Sep 17 00:00:00 2001 From: Hicham Boushaba Date: Mon, 16 Oct 2023 12:13:03 +0100 Subject: [PATCH 04/13] Handle showing the most recent product when no active blaze campaign --- .../android/ui/blaze/MyStoreBlazeViewModel.kt | 21 ++++++++++++++++--- .../ui/products/ProductListRepository.kt | 5 +++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt index c37a4221c96..502fc5d3f9d 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt @@ -5,6 +5,9 @@ import androidx.annotation.StringRes import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.asLiveData import com.woocommerce.android.R +import com.woocommerce.android.model.Product +import com.woocommerce.android.ui.products.ProductListRepository +import com.woocommerce.android.ui.products.ProductStatus import com.woocommerce.android.util.FeatureFlag import com.woocommerce.android.viewmodel.ScopedViewModel import dagger.hilt.android.lifecycle.HiltViewModel @@ -12,12 +15,15 @@ import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import org.wordpress.android.fluxc.model.blaze.BlazeCampaignModel +import org.wordpress.android.fluxc.store.WCProductStore.ProductFilterOption +import org.wordpress.android.fluxc.store.WCProductStore.ProductSorting import javax.inject.Inject @HiltViewModel class MyStoreBlazeViewModel @Inject constructor( savedStateHandle: SavedStateHandle, - observeMostRecentBlazeCampaign: ObserveMostRecentBlazeCampaign + observeMostRecentBlazeCampaign: ObserveMostRecentBlazeCampaign, + private val productListRepository: ProductListRepository ) : ScopedViewModel(savedStateHandle) { val blazeCampaignState = flow { if (!FeatureFlag.BLAZE_ITERATION_2.isEnabled()) emit(MyStoreBlazeCampaignState.Hidden) @@ -34,14 +40,16 @@ class MyStoreBlazeViewModel @Inject constructor( }.asLiveData() private fun prepareUiForNoCampaign(): MyStoreBlazeCampaignState { + val product = getMostRecentProduct() ?: return MyStoreBlazeCampaignState.Hidden return MyStoreBlazeCampaignState.NoCampaign( product = BlazeProductUi( - name = "Product name", - imgUrl = "https://hips.hearstapps.com/hmg-prod/images/gh-082420-ghi-best-sofas-1598293488.png", + name = product.name, + imgUrl = product.firstImageUrl.orEmpty(), ) ) } + @Suppress("UNUSED_PARAMETER") private fun prepareUiForCampaign(campaign: BlazeCampaignModel): MyStoreBlazeCampaignState { return MyStoreBlazeCampaignState.Campaign( campaign = BlazeCampaignUi( @@ -57,6 +65,13 @@ class MyStoreBlazeViewModel @Inject constructor( ) } + private fun getMostRecentProduct(): Product? { + return productListRepository.getProductList( + productFilterOptions = mapOf(ProductFilterOption.STATUS to ProductStatus.PUBLISH.value), + sortType = ProductSorting.DATE_DESC, + ).firstOrNull { !it.isSampleProduct } + } + sealed interface MyStoreBlazeCampaignState { object Hidden : MyStoreBlazeCampaignState data class NoCampaign(val product: BlazeProductUi) : MyStoreBlazeCampaignState diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductListRepository.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductListRepository.kt index cc127042942..90a36461c3a 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductListRepository.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductListRepository.kt @@ -163,14 +163,15 @@ class ProductListRepository @Inject constructor( */ fun getProductList( productFilterOptions: Map = emptyMap(), - excludedProductIds: List? = null + excludedProductIds: List? = null, + sortType: ProductSorting = productSortingChoice ): List { val excludedIds = excludedProductIds?.takeIf { it.isNotEmpty() } return if (selectedSite.exists()) { val wcProducts = productStore.getProducts( selectedSite.get(), filterOptions = productFilterOptions, - sortType = productSortingChoice, + sortType = sortType, excludedProductIds = excludedIds ) wcProducts.map { it.toAppModel() } From 47f7c06edc2fd30a88e424a61709b26f514bbffd Mon Sep 17 00:00:00 2001 From: Hicham Boushaba Date: Mon, 16 Oct 2023 12:37:11 +0100 Subject: [PATCH 05/13] Fix UI of the no-campaign state --- .../android/ui/blaze/MyStoreBlazeView.kt | 55 ++++++++++++++++--- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeView.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeView.kt index 86e8c44169f..3437fbf2d06 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeView.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeView.kt @@ -7,12 +7,14 @@ import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.ButtonDefaults import androidx.compose.material.Card import androidx.compose.material.Divider import androidx.compose.material.Icon @@ -83,6 +85,7 @@ fun MyStoreBlazeView( modifier = Modifier.padding(top = dimensionResource(id = R.dimen.major_100)) ) } + else -> error("Invalid state") } } @@ -92,7 +95,11 @@ fun MyStoreBlazeView( onCreateCampaignClicked ) - is MyStoreBlazeCampaignState.NoCampaign -> CreateCampaignFooter(onCreateCampaignClicked) + is MyStoreBlazeCampaignState.NoCampaign -> CreateCampaignFooter( + onCreateCampaignClicked = onCreateCampaignClicked, + modifier = Modifier.padding(top = dimensionResource(id = R.dimen.major_100)) + ) + else -> error("Invalid state") } } @@ -100,13 +107,23 @@ fun MyStoreBlazeView( } @Composable -private fun CreateCampaignFooter(onCreateCampaignClicked: () -> Unit) { - Divider() - WCTextButton( - modifier = Modifier.padding(start = dimensionResource(id = R.dimen.major_75)), - onClick = onCreateCampaignClicked - ) { - Text(stringResource(id = R.string.blaze_campaign_create_campaign_button)) +private fun CreateCampaignFooter( + onCreateCampaignClicked: () -> Unit, + modifier: Modifier = Modifier, +) { + Column(modifier) { + Divider(Modifier.padding(start = dimensionResource(id = R.dimen.major_100))) + WCTextButton( + onClick = onCreateCampaignClicked, + contentPadding = PaddingValues( + start = dimensionResource(id = R.dimen.major_100), + end = dimensionResource(id = R.dimen.major_100), + top = ButtonDefaults.TextButtonContentPadding.calculateTopPadding(), + bottom = ButtonDefaults.TextButtonContentPadding.calculateBottomPadding(), + ) + ) { + Text(stringResource(id = R.string.blaze_campaign_create_campaign_button)) + } } } @@ -280,7 +297,7 @@ private fun CampaignStat( @Preview(name = "mid screen", device = Devices.PIXEL_4) @Preview(name = "large screen", device = Devices.NEXUS_10) @Composable -fun MyStoreBlazeViewPreview() { +fun MyStoreBlazeViewCampaignPreview() { val product = BlazeProductUi( name = "Product name", imgUrl = "", @@ -299,3 +316,23 @@ fun MyStoreBlazeViewPreview() { onShowAllClicked = {} ) } + +@ExperimentalFoundationApi +@Preview(name = "dark", uiMode = Configuration.UI_MODE_NIGHT_YES) +@Preview(name = "light", uiMode = Configuration.UI_MODE_NIGHT_NO) +@Preview(name = "small screen", device = Devices.PIXEL) +@Preview(name = "mid screen", device = Devices.PIXEL_4) +@Preview(name = "large screen", device = Devices.NEXUS_10) +@Composable +fun MyStoreBlazeViewNoCampaignPreview() { + MyStoreBlazeView( + state = MyStoreBlazeCampaignState.NoCampaign( + product = BlazeProductUi( + name = "Product name", + imgUrl = "", + ) + ), + onCreateCampaignClicked = {}, + onShowAllClicked = {} + ) +} From e10c39f68574b9697711ebf574ce0ac64b00b2fb Mon Sep 17 00:00:00 2001 From: Hicham Boushaba Date: Mon, 16 Oct 2023 13:05:45 +0100 Subject: [PATCH 06/13] Handle navigation to Blaze campaign creation --- .../android/ui/blaze/MyStoreBlazeView.kt | 24 +++++------ .../android/ui/blaze/MyStoreBlazeViewModel.kt | 42 +++++++++++++++---- .../android/ui/mystore/MyStoreFragment.kt | 18 ++++---- 3 files changed, 56 insertions(+), 28 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeView.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeView.kt index 3437fbf2d06..d878a6c3f27 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeView.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeView.kt @@ -44,9 +44,7 @@ import com.woocommerce.android.ui.compose.component.WCTextButton @Composable fun MyStoreBlazeView( - state: MyStoreBlazeCampaignState, - onCreateCampaignClicked: () -> Unit, - onShowAllClicked: () -> Unit + state: MyStoreBlazeCampaignState ) { Card( modifier = Modifier @@ -91,12 +89,12 @@ fun MyStoreBlazeView( } when (state) { is MyStoreBlazeCampaignState.Campaign -> ShowAllOrCreateCampaignFooter( - onShowAllClicked, - onCreateCampaignClicked + onShowAllClicked = state.onViewAllCampaignsClicked, + onCreateCampaignClicked = state.onCreateCampaignClicked ) is MyStoreBlazeCampaignState.NoCampaign -> CreateCampaignFooter( - onCreateCampaignClicked = onCreateCampaignClicked, + onCreateCampaignClicked = state.onCreateCampaignClicked, modifier = Modifier.padding(top = dimensionResource(id = R.dimen.major_100)) ) @@ -311,9 +309,10 @@ fun MyStoreBlazeViewCampaignPreview() { clicks = 10, budget = 1000 ), - ), - onCreateCampaignClicked = {}, - onShowAllClicked = {} + onCampaignClicked = {}, + onViewAllCampaignsClicked = {}, + onCreateCampaignClicked = {} + ) ) } @@ -330,9 +329,8 @@ fun MyStoreBlazeViewNoCampaignPreview() { product = BlazeProductUi( name = "Product name", imgUrl = "", - ) - ), - onCreateCampaignClicked = {}, - onShowAllClicked = {} + ), + onCreateCampaignClicked = {} + ) ) } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt index 502fc5d3f9d..1b6e4b49d79 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt @@ -6,9 +6,11 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.asLiveData import com.woocommerce.android.R import com.woocommerce.android.model.Product +import com.woocommerce.android.ui.blaze.IsBlazeEnabled.BlazeFlowSource import com.woocommerce.android.ui.products.ProductListRepository import com.woocommerce.android.ui.products.ProductStatus import com.woocommerce.android.util.FeatureFlag +import com.woocommerce.android.viewmodel.MultiLiveEvent import com.woocommerce.android.viewmodel.ScopedViewModel import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.emitAll @@ -23,7 +25,8 @@ import javax.inject.Inject class MyStoreBlazeViewModel @Inject constructor( savedStateHandle: SavedStateHandle, observeMostRecentBlazeCampaign: ObserveMostRecentBlazeCampaign, - private val productListRepository: ProductListRepository + private val productListRepository: ProductListRepository, + private val isBlazeEnabled: IsBlazeEnabled ) : ScopedViewModel(savedStateHandle) { val blazeCampaignState = flow { if (!FeatureFlag.BLAZE_ITERATION_2.isEnabled()) emit(MyStoreBlazeCampaignState.Hidden) @@ -40,12 +43,21 @@ class MyStoreBlazeViewModel @Inject constructor( }.asLiveData() private fun prepareUiForNoCampaign(): MyStoreBlazeCampaignState { - val product = getMostRecentProduct() ?: return MyStoreBlazeCampaignState.Hidden + val products = getProducts() + val product = products.firstOrNull() ?: return MyStoreBlazeCampaignState.Hidden return MyStoreBlazeCampaignState.NoCampaign( product = BlazeProductUi( name = product.name, imgUrl = product.firstImageUrl.orEmpty(), - ) + ), + onCreateCampaignClicked = { + val url = if (products.size == 1) { + isBlazeEnabled.buildUrlForProduct(product.remoteId, BlazeFlowSource.MY_STORE_BANNER) + } else { + isBlazeEnabled.buildUrlForSite(BlazeFlowSource.MY_STORE_BANNER) + } + triggerEvent(LaunchBlazeCampaignCreation(url, BlazeFlowSource.MY_STORE_BANNER)) + } ) } @@ -61,21 +73,33 @@ class MyStoreBlazeViewModel @Inject constructor( impressions = 100, clicks = 10, budget = 1000 - ) + ), + onCampaignClicked = { /* TODO */ }, + onViewAllCampaignsClicked = { /* TODO */ }, + onCreateCampaignClicked = { /* TODO */ } ) } - private fun getMostRecentProduct(): Product? { + private fun getProducts(): List { return productListRepository.getProductList( productFilterOptions = mapOf(ProductFilterOption.STATUS to ProductStatus.PUBLISH.value), sortType = ProductSorting.DATE_DESC, - ).firstOrNull { !it.isSampleProduct } + ).filterNot { !it.isSampleProduct } } sealed interface MyStoreBlazeCampaignState { object Hidden : MyStoreBlazeCampaignState - data class NoCampaign(val product: BlazeProductUi) : MyStoreBlazeCampaignState - data class Campaign(val campaign: BlazeCampaignUi) : MyStoreBlazeCampaignState + data class NoCampaign( + val product: BlazeProductUi, + val onCreateCampaignClicked: () -> Unit, + ) : MyStoreBlazeCampaignState + + data class Campaign( + val campaign: BlazeCampaignUi, + val onCampaignClicked: () -> Unit, + val onViewAllCampaignsClicked: () -> Unit, + val onCreateCampaignClicked: () -> Unit, + ) : MyStoreBlazeCampaignState } data class BlazeProductUi( @@ -117,4 +141,6 @@ class MyStoreBlazeViewModel @Inject constructor( backgroundColor = R.color.blaze_campaign_status_completed_background ), } + + data class LaunchBlazeCampaignCreation(val url: String, val source: BlazeFlowSource) : MultiLiveEvent.Event() } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/mystore/MyStoreFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/mystore/MyStoreFragment.kt index 655225cd543..ce9035a9597 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/mystore/MyStoreFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/mystore/MyStoreFragment.kt @@ -46,6 +46,7 @@ import com.woocommerce.android.ui.base.TopLevelFragment import com.woocommerce.android.ui.base.UIMessageResolver import com.woocommerce.android.ui.blaze.BlazeBanner import com.woocommerce.android.ui.blaze.BlazeBannerViewModel +import com.woocommerce.android.ui.blaze.IsBlazeEnabled.BlazeFlowSource import com.woocommerce.android.ui.blaze.IsBlazeEnabled.BlazeFlowSource.MY_STORE_BANNER import com.woocommerce.android.ui.blaze.MyStoreBlazeView import com.woocommerce.android.ui.blaze.MyStoreBlazeViewModel @@ -254,7 +255,7 @@ class MyStoreFragment : blazeBannerViewModel.event.observe(viewLifecycleOwner) { event -> when (event) { - is BlazeBannerViewModel.OpenBlazeEvent -> openBlazeWebView(event) + is BlazeBannerViewModel.OpenBlazeEvent -> openBlazeWebView(event.url, event.source) is BlazeBannerViewModel.DismissBlazeBannerEvent -> binding.blazeBannerView.collapse() is ShowDialog -> event.showDialog() } @@ -269,9 +270,7 @@ class MyStoreFragment : setContent { WooThemeWithBackground { MyStoreBlazeView( - state = blazeCampaignState, - onCreateCampaignClicked = { }, - onShowAllClicked = { }, + state = blazeCampaignState ) } } @@ -279,13 +278,18 @@ class MyStoreFragment : } } } + myStoreBlazeViewModel.event.observe(viewLifecycleOwner) { event -> + when (event) { + is MyStoreBlazeViewModel.LaunchBlazeCampaignCreation -> openBlazeWebView(event.url, event.source) + } + } } - private fun openBlazeWebView(event: BlazeBannerViewModel.OpenBlazeEvent) { + private fun openBlazeWebView(url: String, source: BlazeFlowSource) { findNavController().navigateSafely( NavGraphMainDirections.actionGlobalBlazeWebViewFragment( - urlToLoad = event.url, - source = event.source + urlToLoad = url, + source = source ) ) } From 6bf5d8c8f6903c715e4b647aef48e9eb6b9b11b9 Mon Sep 17 00:00:00 2001 From: Hicham Boushaba Date: Mon, 16 Oct 2023 13:14:01 +0100 Subject: [PATCH 07/13] Handle fetching products and update the no-campaign view --- .../android/ui/blaze/MyStoreBlazeViewModel.kt | 84 +++++++++++-------- .../ui/products/ProductListRepository.kt | 5 +- 2 files changed, 53 insertions(+), 36 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt index 1b6e4b49d79..192ade1b156 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt @@ -13,8 +13,12 @@ import com.woocommerce.android.util.FeatureFlag import com.woocommerce.android.viewmodel.MultiLiveEvent import com.woocommerce.android.viewmodel.ScopedViewModel import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emitAll +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import org.wordpress.android.fluxc.model.blaze.BlazeCampaignModel import org.wordpress.android.fluxc.store.WCProductStore.ProductFilterOption @@ -28,11 +32,12 @@ class MyStoreBlazeViewModel @Inject constructor( private val productListRepository: ProductListRepository, private val isBlazeEnabled: IsBlazeEnabled ) : ScopedViewModel(savedStateHandle) { + @OptIn(ExperimentalCoroutinesApi::class) val blazeCampaignState = flow { if (!FeatureFlag.BLAZE_ITERATION_2.isEnabled()) emit(MyStoreBlazeCampaignState.Hidden) else { emitAll( - observeMostRecentBlazeCampaign().map { + observeMostRecentBlazeCampaign().flatMapLatest { when (it) { null -> prepareUiForNoCampaign() else -> prepareUiForCampaign(it) @@ -42,49 +47,60 @@ class MyStoreBlazeViewModel @Inject constructor( } }.asLiveData() - private fun prepareUiForNoCampaign(): MyStoreBlazeCampaignState { - val products = getProducts() - val product = products.firstOrNull() ?: return MyStoreBlazeCampaignState.Hidden - return MyStoreBlazeCampaignState.NoCampaign( - product = BlazeProductUi( - name = product.name, - imgUrl = product.firstImageUrl.orEmpty(), - ), - onCreateCampaignClicked = { - val url = if (products.size == 1) { - isBlazeEnabled.buildUrlForProduct(product.remoteId, BlazeFlowSource.MY_STORE_BANNER) - } else { - isBlazeEnabled.buildUrlForSite(BlazeFlowSource.MY_STORE_BANNER) + private fun prepareUiForNoCampaign(): Flow { + return getProducts().map { products -> + val product = products.firstOrNull() ?: return@map MyStoreBlazeCampaignState.Hidden + MyStoreBlazeCampaignState.NoCampaign( + product = BlazeProductUi( + name = product.name, + imgUrl = product.firstImageUrl.orEmpty(), + ), + onCreateCampaignClicked = { + val url = if (products.size == 1) { + isBlazeEnabled.buildUrlForProduct(product.remoteId, BlazeFlowSource.MY_STORE_BANNER) + } else { + isBlazeEnabled.buildUrlForSite(BlazeFlowSource.MY_STORE_BANNER) + } + triggerEvent(LaunchBlazeCampaignCreation(url, BlazeFlowSource.MY_STORE_BANNER)) } - triggerEvent(LaunchBlazeCampaignCreation(url, BlazeFlowSource.MY_STORE_BANNER)) - } - ) + ) + } } @Suppress("UNUSED_PARAMETER") - private fun prepareUiForCampaign(campaign: BlazeCampaignModel): MyStoreBlazeCampaignState { - return MyStoreBlazeCampaignState.Campaign( - campaign = BlazeCampaignUi( - product = BlazeProductUi( - name = "Product name", - imgUrl = "https://hips.hearstapps.com/hmg-prod/images/gh-082420-ghi-best-sofas-1598293488.png", + private fun prepareUiForCampaign(campaign: BlazeCampaignModel): Flow { + return flowOf( + MyStoreBlazeCampaignState.Campaign( + campaign = BlazeCampaignUi( + product = BlazeProductUi( + name = "Product name", + imgUrl = "https://hips.hearstapps.com/hmg-prod/images/gh-082420-ghi-best-sofas-1598293488.png", + ), + status = CampaignStatusUi.Active, + impressions = 100, + clicks = 10, + budget = 1000 ), - status = CampaignStatusUi.Active, - impressions = 100, - clicks = 10, - budget = 1000 - ), - onCampaignClicked = { /* TODO */ }, - onViewAllCampaignsClicked = { /* TODO */ }, - onCreateCampaignClicked = { /* TODO */ } + onCampaignClicked = { /* TODO */ }, + onViewAllCampaignsClicked = { /* TODO */ }, + onCreateCampaignClicked = { /* TODO */ } + ) ) } - private fun getProducts(): List { - return productListRepository.getProductList( + private fun getProducts(): Flow> { + fun getCachedProducts() = productListRepository.getProductList( productFilterOptions = mapOf(ProductFilterOption.STATUS to ProductStatus.PUBLISH.value), sortType = ProductSorting.DATE_DESC, - ).filterNot { !it.isSampleProduct } + ).filterNot { it.isSampleProduct } + return flow { + emit(getCachedProducts()) + productListRepository.fetchProductList( + productFilterOptions = mapOf(ProductFilterOption.STATUS to ProductStatus.PUBLISH.value), + sortType = ProductSorting.DATE_DESC, + ) + emit(getCachedProducts()) + } } sealed interface MyStoreBlazeCampaignState { diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductListRepository.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductListRepository.kt index 90a36461c3a..8fa2c49ca53 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductListRepository.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductListRepository.kt @@ -81,7 +81,8 @@ class ProductListRepository @Inject constructor( suspend fun fetchProductList( loadMore: Boolean = false, productFilterOptions: Map = emptyMap(), - excludedProductIds: List? = null + excludedProductIds: List? = null, + sortType: ProductSorting = productSortingChoice ): List { loadContinuation.callAndWaitUntilTimeout(AppConstants.REQUEST_TIMEOUT) { offset = if (loadMore) offset + PRODUCT_PAGE_SIZE else 0 @@ -91,7 +92,7 @@ class ProductListRepository @Inject constructor( site = selectedSite.get(), pageSize = PRODUCT_PAGE_SIZE, offset = offset, - sorting = productSortingChoice, + sorting = sortType, filterOptions = productFilterOptions, excludedProductIds = excludedProductIds ) From a6700a5fe3175dc6a428463c484f3c7359b94745 Mon Sep 17 00:00:00 2001 From: Hicham Boushaba Date: Mon, 16 Oct 2023 13:15:31 +0100 Subject: [PATCH 08/13] Launch blaze campaign creation when clicking on the product too. --- .../kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeView.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeView.kt index d878a6c3f27..4b85dad36f4 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeView.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeView.kt @@ -79,7 +79,7 @@ fun MyStoreBlazeView( ) BlazeProductItem( product = state.product, - onProductSelected = {}, + onProductSelected = state.onCreateCampaignClicked, modifier = Modifier.padding(top = dimensionResource(id = R.dimen.major_100)) ) } From db96c8ce25a05c39e97998e90b968db420fef73e Mon Sep 17 00:00:00 2001 From: Hicham Boushaba Date: Mon, 16 Oct 2023 15:24:01 +0100 Subject: [PATCH 09/13] Update logic of blaze flow when clicking on the product --- .../android/ui/blaze/MyStoreBlazeView.kt | 3 ++- .../android/ui/blaze/MyStoreBlazeViewModel.kt | 20 +++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeView.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeView.kt index 4b85dad36f4..3cb97ecc380 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeView.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeView.kt @@ -79,7 +79,7 @@ fun MyStoreBlazeView( ) BlazeProductItem( product = state.product, - onProductSelected = state.onCreateCampaignClicked, + onProductSelected = state.onProductClicked, modifier = Modifier.padding(top = dimensionResource(id = R.dimen.major_100)) ) } @@ -330,6 +330,7 @@ fun MyStoreBlazeViewNoCampaignPreview() { name = "Product name", imgUrl = "", ), + onProductClicked = {}, onCreateCampaignClicked = {} ) ) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt index 192ade1b156..48eb36c54e0 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/blaze/MyStoreBlazeViewModel.kt @@ -48,6 +48,15 @@ class MyStoreBlazeViewModel @Inject constructor( }.asLiveData() private fun prepareUiForNoCampaign(): Flow { + fun launchCampaignCreation(productId: Long?) { + val url = if (productId != null) { + isBlazeEnabled.buildUrlForProduct(productId, BlazeFlowSource.MY_STORE_BANNER) + } else { + isBlazeEnabled.buildUrlForSite(BlazeFlowSource.MY_STORE_BANNER) + } + triggerEvent(LaunchBlazeCampaignCreation(url, BlazeFlowSource.MY_STORE_BANNER)) + } + return getProducts().map { products -> val product = products.firstOrNull() ?: return@map MyStoreBlazeCampaignState.Hidden MyStoreBlazeCampaignState.NoCampaign( @@ -55,13 +64,11 @@ class MyStoreBlazeViewModel @Inject constructor( name = product.name, imgUrl = product.firstImageUrl.orEmpty(), ), + onProductClicked = { + launchCampaignCreation(product.remoteId) + }, onCreateCampaignClicked = { - val url = if (products.size == 1) { - isBlazeEnabled.buildUrlForProduct(product.remoteId, BlazeFlowSource.MY_STORE_BANNER) - } else { - isBlazeEnabled.buildUrlForSite(BlazeFlowSource.MY_STORE_BANNER) - } - triggerEvent(LaunchBlazeCampaignCreation(url, BlazeFlowSource.MY_STORE_BANNER)) + launchCampaignCreation(if (products.size == 1) product.remoteId else null) } ) } @@ -107,6 +114,7 @@ class MyStoreBlazeViewModel @Inject constructor( object Hidden : MyStoreBlazeCampaignState data class NoCampaign( val product: BlazeProductUi, + val onProductClicked: () -> Unit, val onCreateCampaignClicked: () -> Unit, ) : MyStoreBlazeCampaignState From 229add887d1f6a54680ecd59664f1283e13a5593 Mon Sep 17 00:00:00 2001 From: Hicham Boushaba Date: Tue, 17 Oct 2023 00:22:53 +0100 Subject: [PATCH 10/13] Avoid using property as default argument This was causing issues with unit tests, probably because Mockito is still not a Kotlin library --- .../android/ui/products/ProductListRepository.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductListRepository.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductListRepository.kt index 8fa2c49ca53..0e6fdec2b25 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductListRepository.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductListRepository.kt @@ -82,7 +82,7 @@ class ProductListRepository @Inject constructor( loadMore: Boolean = false, productFilterOptions: Map = emptyMap(), excludedProductIds: List? = null, - sortType: ProductSorting = productSortingChoice + sortType: ProductSorting? = null ): List { loadContinuation.callAndWaitUntilTimeout(AppConstants.REQUEST_TIMEOUT) { offset = if (loadMore) offset + PRODUCT_PAGE_SIZE else 0 @@ -92,7 +92,7 @@ class ProductListRepository @Inject constructor( site = selectedSite.get(), pageSize = PRODUCT_PAGE_SIZE, offset = offset, - sorting = sortType, + sorting = sortType ?: productSortingChoice, filterOptions = productFilterOptions, excludedProductIds = excludedProductIds ) @@ -165,14 +165,14 @@ class ProductListRepository @Inject constructor( fun getProductList( productFilterOptions: Map = emptyMap(), excludedProductIds: List? = null, - sortType: ProductSorting = productSortingChoice + sortType: ProductSorting? = null ): List { val excludedIds = excludedProductIds?.takeIf { it.isNotEmpty() } return if (selectedSite.exists()) { val wcProducts = productStore.getProducts( selectedSite.get(), filterOptions = productFilterOptions, - sortType = sortType, + sortType = sortType ?: productSortingChoice, excludedProductIds = excludedIds ) wcProducts.map { it.toAppModel() } From e753e9ee5e5cf2512375262b09aa2e632b266a6b Mon Sep 17 00:00:00 2001 From: Hicham Boushaba Date: Tue, 17 Oct 2023 10:39:22 +0100 Subject: [PATCH 11/13] UI Tests: fix products endpoint matching --- .../mocks/mappings/jetpack-blogs/wc/products/products.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WooCommerce/src/androidTest/assets/mocks/mappings/jetpack-blogs/wc/products/products.json b/WooCommerce/src/androidTest/assets/mocks/mappings/jetpack-blogs/wc/products/products.json index 198d7612b32..6e46e7146a0 100644 --- a/WooCommerce/src/androidTest/assets/mocks/mappings/jetpack-blogs/wc/products/products.json +++ b/WooCommerce/src/androidTest/assets/mocks/mappings/jetpack-blogs/wc/products/products.json @@ -10,8 +10,8 @@ "matches": "/wc/v3/products/(.*)" }, "query": { - "matches": "(.*)orderby\":\"title(.*)" - }, + "matches": "(.*)orderby\":\"(.*)" + }, "locale": { "matches": "(.*)" } From 4bb0b4ddebe30e68d7bd5b96aee283caaf2606cb Mon Sep 17 00:00:00 2001 From: Hicham Boushaba Date: Tue, 17 Oct 2023 12:16:25 +0100 Subject: [PATCH 12/13] UI Tests: add a mock for blaze campaigns endpoint --- .../mocks/mappings/blaze/blaze_campaigns.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 WooCommerce/src/androidTest/assets/mocks/mappings/blaze/blaze_campaigns.json diff --git a/WooCommerce/src/androidTest/assets/mocks/mappings/blaze/blaze_campaigns.json b/WooCommerce/src/androidTest/assets/mocks/mappings/blaze/blaze_campaigns.json new file mode 100644 index 00000000000..e63228c94ca --- /dev/null +++ b/WooCommerce/src/androidTest/assets/mocks/mappings/blaze/blaze_campaigns.json @@ -0,0 +1,19 @@ +{ + "request": { + "method": "GET", + "urlPattern": "/wpcom/v2/sites/([0-9]+)/wordads/dsp/api/v1/search/campaigns/site/([0-9]+)\\?(.*)" + }, + "response": { + "status": 200, + "jsonBody": { + "total_items": 0, + "campaigns": [], + "total_pages": 0, + "page": 1 + }, + "headers": { + "Content-Type": "application/json", + "Connection": "keep-alive" + } + } +} From c1318f3ebad37d7fbbdaaa0b77676be66b546634 Mon Sep 17 00:00:00 2001 From: Hicham Boushaba Date: Tue, 17 Oct 2023 14:26:29 +0100 Subject: [PATCH 13/13] Bump fluxc version --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 2a35ca6e921..cf28ade3534 100644 --- a/build.gradle +++ b/build.gradle @@ -95,7 +95,7 @@ tasks.register("installGitHooks", Copy) { } ext { - fluxCVersion = '2871-0eff506ba6c0aea40d51c3e295a5d6dc1c636336' + fluxCVersion = 'trunk-3caf1058bd9384da8a134cee9855c92e31e7ff5f' glideVersion = '4.13.2' coilVersion = '2.1.0' constraintLayoutVersion = '1.2.0'