From c372cf1dba97faec419bde94052cdf54105ef4d0 Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Thu, 15 Jun 2023 18:12:08 +0700 Subject: [PATCH 01/33] Add: Use latest fluxC version to bring in JetpackAIStore. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 8e3ccfd9b0b6..7140f92abf16 100644 --- a/build.gradle +++ b/build.gradle @@ -95,7 +95,7 @@ tasks.register("installGitHooks", Copy) { } ext { - fluxCVersion = 'trunk-466e0b8f67bfb134682e4d03a408f4f25b7d3693' + fluxCVersion = 'trunk-9037aa3c964116d7ee5629f87859a63257fff43c' glideVersion = '4.13.2' coilVersion = '2.1.0' constraintLayoutVersion = '1.2.0' From b7d8cf6652c6b4d1455d7c6f4584e9c6dd9861a8 Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Thu, 15 Jun 2023 18:13:25 +0700 Subject: [PATCH 02/33] Add: AI Repository class. --- .../woocommerce/android/ai/AIRepository.kt | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 WooCommerce/src/main/kotlin/com/woocommerce/android/ai/AIRepository.kt diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ai/AIRepository.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ai/AIRepository.kt new file mode 100644 index 000000000000..bf5dc98bf61a --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ai/AIRepository.kt @@ -0,0 +1,32 @@ +package com.woocommerce.android.ai + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.wordpress.android.fluxc.model.SiteModel +import org.wordpress.android.fluxc.network.rest.wpcom.jetpackai.JetpackAIRestClient.JetpackAICompletionsResponse +import org.wordpress.android.fluxc.store.jetpackai.JetpackAIStore +import java.lang.Exception +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AIRepository @Inject constructor( + private val jetpackAIStore: JetpackAIStore +) { + suspend fun fetchJetpackAICompletionsForSite( + site: SiteModel, + prompt: String, + skipCache: Boolean = false + ): Result = withContext(Dispatchers.IO) { + jetpackAIStore.fetchJetpackAICompletionsForSite(site, prompt, skipCache).run { + when (this) { + is JetpackAICompletionsResponse.Success -> { + Result.success(completion) + } + is JetpackAICompletionsResponse.Error -> { + Result.failure(Exception(message ?: "Unable to fetch AI completions")) + } + } + } + } +} From 6eb314c7af13437b703472ab3895d2cf1549879c Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Thu, 15 Jun 2023 19:21:18 +0700 Subject: [PATCH 03/33] Add: AI Prompts class. --- .../com/woocommerce/android/ai/AIPrompts.kt | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 WooCommerce/src/main/kotlin/com/woocommerce/android/ai/AIPrompts.kt diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ai/AIPrompts.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ai/AIPrompts.kt new file mode 100644 index 000000000000..5ab4ce82a656 --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ai/AIPrompts.kt @@ -0,0 +1,30 @@ +package com.woocommerce.android.ai + +object AIPrompts { + private const val PRODUCT_DESCRIPTION_PROMPT = "Write a description for a product with title \"%1\$s\"%2\$s.\n" + + "Identify the language used in the product title and use the same language in your response.\n" + + "Make the description 50-60 words or less.\n" + + "Use a 9th grade reading level.\n" + + "Perform in-depth keyword research relating to the product in the same language of the product title, " + + "and use them in your sentences without listing them out." + + fun generateProductDescriptionPrompt(name: String, features: String = ""): String { + val featuresPart = if (features.isNotEmpty()) " and features: \"$features\"" else "" + return String.format(PRODUCT_DESCRIPTION_PROMPT, name, featuresPart) + } + + private const val PRODUCT_SHARING_PROMPT = "Your task is to help a merchant create a message to share with " + + "their customers a product named \"%1\$s\". More information about the product:\n" + + "%2\$s\n" + + "- Product URL: %3\$s.\n" + + "Identify the language used in the product name and use the same language in your response.\n" + + "The length should be up to 3 sentences.\n" + + "Use a 9th grade reading level.\n" + + "Add related hashtags at the end of the message.\n" + + "Do not include the URL in the message." + + fun generateProductSharingPrompt(name: String, url: String, description: String = ""): String { + val descriptionPart = if (description.isNotEmpty()) "- Product description: \"$description\"" else "" + return String.format(PRODUCT_SHARING_PROMPT, name, descriptionPart, url) + } +} From f6d69e3341f14b790ef6559fdc8dc1479041a757 Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Thu, 15 Jun 2023 19:48:49 +0700 Subject: [PATCH 04/33] Feat: fetch sharing suggestion from AI and display it. --- .../ui/products/ProductSharingViewModel.kt | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt index fe91ebea302b..2df908d424de 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt @@ -3,6 +3,9 @@ package com.woocommerce.android.ui.products import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.asLiveData import com.woocommerce.android.R +import com.woocommerce.android.ai.AIPrompts +import com.woocommerce.android.ai.AIRepository +import com.woocommerce.android.tools.SelectedSite import com.woocommerce.android.ui.products.ProductSharingViewModel.ViewState.LoadingState import com.woocommerce.android.ui.products.ProductSharingViewModel.ViewState.ProductSharingViewState import com.woocommerce.android.viewmodel.ResourceProvider @@ -11,11 +14,14 @@ import com.woocommerce.android.viewmodel.navArgs import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class ProductSharingViewModel @Inject constructor( + private val aiRepository: AIRepository, private val resourceProvider: ResourceProvider, + private val selectedSite: SelectedSite, savedStateHandle: SavedStateHandle ) : ScopedViewModel(savedStateHandle) { private val navArgs: ProductSharingDialogArgs by savedStateHandle.navArgs() @@ -43,7 +49,36 @@ class ProductSharingViewModel @Inject constructor( isGenerating = true ) } - // todo generate message here. + + launch { + val result = aiRepository.fetchJetpackAICompletionsForSite( + site = selectedSite.get(), + prompt = AIPrompts.generateProductSharingPrompt( + navArgs.productName, + navArgs.permalink, + navArgs.productDescription.orEmpty() + ) + ) + if (result.isFailure) { + // error handling + } else { + val completions = result.getOrNull() + completions?.let { + _viewState.update { + ProductSharingViewState( + productTitle = navArgs.productName, + buttonState = AIButtonState.Regenerate( + resourceProvider.getString(R.string.product_sharing_regenerate) + ), + shareMessage = completions, + isGenerating = false + ) + } + } + } + } + + } sealed class ViewState { From 3d395f09652ecd3956849401345f0a0fc3e599eb Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Thu, 15 Jun 2023 20:05:43 +0700 Subject: [PATCH 05/33] Feat: handle share message editing. To be used later for actual sharing. --- .../android/ui/products/ProductSharingBottomSheet.kt | 6 ++++-- .../android/ui/products/ProductSharingViewModel.kt | 11 +++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingBottomSheet.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingBottomSheet.kt index b31f3be46285..773533b003a4 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingBottomSheet.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingBottomSheet.kt @@ -46,6 +46,7 @@ fun ProductSharingBottomSheet(viewModel: ProductSharingViewModel) { ProductShareWithAI( viewState = it, onGenerateButtonClick = viewModel::onGenerateButtonClicked, + onShareMessageEdit = viewModel::onShareMessageEdited ) } @@ -57,7 +58,8 @@ fun ProductSharingBottomSheet(viewModel: ProductSharingViewModel) { @Composable fun ProductShareWithAI( viewState: ProductSharingViewState, - onGenerateButtonClick: () -> Unit = {} + onGenerateButtonClick: () -> Unit = {}, + onShareMessageEdit: (String) -> Unit = {} ) { Column( modifier = Modifier @@ -82,7 +84,7 @@ fun ProductShareWithAI( } else { WCOutlinedTextField( value = viewState.shareMessage, - onValueChange = { /*TODO*/ }, + onValueChange = { onShareMessageEdit(it) }, label = stringResource(id = R.string.product_sharing_optional_message_label), maxLines = 4 ) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt index 2df908d424de..7d5cf33549af 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt @@ -28,6 +28,7 @@ class ProductSharingViewModel @Inject constructor( private val _viewState = MutableStateFlow(LoadingState) val viewState = _viewState.asLiveData() + init { _viewState.update { ProductSharingViewState( @@ -81,6 +82,16 @@ class ProductSharingViewModel @Inject constructor( } + fun onShareMessageEdited(message: String) { + _viewState.update { state -> + if (state is ProductSharingViewState) { + state.copy(shareMessage = message) + } else { + state// shouldn't happen but needed to satisfy compiler + } + } + } + sealed class ViewState { object LoadingState : ViewState() data class ProductSharingViewState( From 1d0951643e4527634a6d9318ea0c3b51f4e55397 Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Thu, 15 Jun 2023 20:09:26 +0700 Subject: [PATCH 06/33] Refactor: use fold instead for result handling. --- .../ui/products/ProductSharingViewModel.kt | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt index 7d5cf33549af..c9f438eaef04 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt @@ -60,11 +60,8 @@ class ProductSharingViewModel @Inject constructor( navArgs.productDescription.orEmpty() ) ) - if (result.isFailure) { - // error handling - } else { - val completions = result.getOrNull() - completions?.let { + result.fold( + onSuccess = { completions -> _viewState.update { ProductSharingViewState( productTitle = navArgs.productName, @@ -75,11 +72,12 @@ class ProductSharingViewModel @Inject constructor( isGenerating = false ) } + }, + onFailure = { + // error handling } - } + ) } - - } fun onShareMessageEdited(message: String) { @@ -87,7 +85,8 @@ class ProductSharingViewModel @Inject constructor( if (state is ProductSharingViewState) { state.copy(shareMessage = message) } else { - state// shouldn't happen but needed to satisfy compiler + // shouldn't happen but needed to satisfy compiler + state } } } From 76480e747c11cf487890d20329aad9187c7a592c Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Thu, 15 Jun 2023 20:23:12 +0700 Subject: [PATCH 07/33] Refactor: Remove unneeded LoadingState and simplify code. --- .../ui/products/ProductSharingBottomSheet.kt | 18 +++---- .../ui/products/ProductSharingViewModel.kt | 51 +++++++------------ 2 files changed, 23 insertions(+), 46 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingBottomSheet.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingBottomSheet.kt index 773533b003a4..28157ec1c1fe 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingBottomSheet.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingBottomSheet.kt @@ -36,22 +36,16 @@ import com.woocommerce.android.ui.products.ProductSharingViewModel.AIButtonState import com.woocommerce.android.ui.products.ProductSharingViewModel.AIButtonState.Generating import com.woocommerce.android.ui.products.ProductSharingViewModel.AIButtonState.Regenerate import com.woocommerce.android.ui.products.ProductSharingViewModel.AIButtonState.WriteWithAI -import com.woocommerce.android.ui.products.ProductSharingViewModel.ViewState.ProductSharingViewState +import com.woocommerce.android.ui.products.ProductSharingViewModel.ProductSharingViewState @Composable fun ProductSharingBottomSheet(viewModel: ProductSharingViewModel) { viewModel.viewState.observeAsState().value?.let { - when (it) { - is ProductSharingViewState -> { - ProductShareWithAI( - viewState = it, - onGenerateButtonClick = viewModel::onGenerateButtonClicked, - onShareMessageEdit = viewModel::onShareMessageEdited - ) - } - - else -> { /* nothing to show for Loading state. */ } - } + ProductShareWithAI( + viewState = it, + onGenerateButtonClick = viewModel::onGenerateButtonClicked, + onShareMessageEdit = viewModel::onShareMessageEdited + ) } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt index c9f438eaef04..fe301de52bff 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt @@ -6,8 +6,6 @@ import com.woocommerce.android.R import com.woocommerce.android.ai.AIPrompts import com.woocommerce.android.ai.AIRepository import com.woocommerce.android.tools.SelectedSite -import com.woocommerce.android.ui.products.ProductSharingViewModel.ViewState.LoadingState -import com.woocommerce.android.ui.products.ProductSharingViewModel.ViewState.ProductSharingViewState import com.woocommerce.android.viewmodel.ResourceProvider import com.woocommerce.android.viewmodel.ScopedViewModel import com.woocommerce.android.viewmodel.navArgs @@ -26,24 +24,19 @@ class ProductSharingViewModel @Inject constructor( ) : ScopedViewModel(savedStateHandle) { private val navArgs: ProductSharingDialogArgs by savedStateHandle.navArgs() - private val _viewState = MutableStateFlow(LoadingState) - val viewState = _viewState.asLiveData() - - init { - _viewState.update { - ProductSharingViewState( - productTitle = navArgs.productName, - buttonState = AIButtonState.WriteWithAI( - resourceProvider.getString(R.string.product_sharing_write_with_ai) - ) + private val _viewState = MutableStateFlow( + ProductSharingViewState( + productTitle = navArgs.productName, + buttonState = AIButtonState.WriteWithAI( + resourceProvider.getString(R.string.product_sharing_write_with_ai) ) - } - } + ) + ) + val viewState = _viewState.asLiveData() fun onGenerateButtonClicked() { _viewState.update { - ProductSharingViewState( - productTitle = navArgs.productName, + it.copy( buttonState = AIButtonState.Generating( resourceProvider.getString(R.string.product_sharing_generating) ), @@ -63,8 +56,7 @@ class ProductSharingViewModel @Inject constructor( result.fold( onSuccess = { completions -> _viewState.update { - ProductSharingViewState( - productTitle = navArgs.productName, + it.copy( buttonState = AIButtonState.Regenerate( resourceProvider.getString(R.string.product_sharing_regenerate) ), @@ -82,24 +74,15 @@ class ProductSharingViewModel @Inject constructor( fun onShareMessageEdited(message: String) { _viewState.update { state -> - if (state is ProductSharingViewState) { - state.copy(shareMessage = message) - } else { - // shouldn't happen but needed to satisfy compiler - state - } + state.copy(shareMessage = message) } } - - sealed class ViewState { - object LoadingState : ViewState() - data class ProductSharingViewState( - val productTitle: String, - val shareMessage: String = "", - val buttonState: AIButtonState, - val isGenerating: Boolean = false - ) : ViewState() - } + data class ProductSharingViewState( + val productTitle: String, + val shareMessage: String = "", + val buttonState: AIButtonState, + val isGenerating: Boolean = false + ) sealed class AIButtonState(val label: String) { data class WriteWithAI(val text: String) : AIButtonState(text) From 42b5ba184c208a6bc9729b8047b1ae44b6a4a5d2 Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Thu, 15 Jun 2023 20:25:20 +0700 Subject: [PATCH 08/33] Add: state update on result failure. --- .../android/ui/products/ProductSharingViewModel.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt index fe301de52bff..b5b30d99966d 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt @@ -66,7 +66,9 @@ class ProductSharingViewModel @Inject constructor( } }, onFailure = { - // error handling + _viewState.update { + it.copy(isGenerating = false) + } } ) } From 5e80da313da4d866992f070592bc422d38d69cfd Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Thu, 15 Jun 2023 20:31:13 +0700 Subject: [PATCH 09/33] Refactor: make separate variables for button labels as they're to be reused. --- .../ui/products/ProductSharingViewModel.kt | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt index b5b30d99966d..44c590c9385d 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt @@ -24,12 +24,14 @@ class ProductSharingViewModel @Inject constructor( ) : ScopedViewModel(savedStateHandle) { private val navArgs: ProductSharingDialogArgs by savedStateHandle.navArgs() + private val labelForWriteWithAI = resourceProvider.getString(R.string.product_sharing_write_with_ai) + private val labelForGenerating = resourceProvider.getString(R.string.product_sharing_generating) + private val labelForRegenerate = resourceProvider.getString(R.string.product_sharing_regenerate) + private val _viewState = MutableStateFlow( ProductSharingViewState( productTitle = navArgs.productName, - buttonState = AIButtonState.WriteWithAI( - resourceProvider.getString(R.string.product_sharing_write_with_ai) - ) + buttonState = AIButtonState.WriteWithAI(labelForWriteWithAI) ) ) val viewState = _viewState.asLiveData() @@ -37,9 +39,7 @@ class ProductSharingViewModel @Inject constructor( fun onGenerateButtonClicked() { _viewState.update { it.copy( - buttonState = AIButtonState.Generating( - resourceProvider.getString(R.string.product_sharing_generating) - ), + buttonState = AIButtonState.Generating(labelForGenerating), isGenerating = true ) } @@ -57,9 +57,7 @@ class ProductSharingViewModel @Inject constructor( onSuccess = { completions -> _viewState.update { it.copy( - buttonState = AIButtonState.Regenerate( - resourceProvider.getString(R.string.product_sharing_regenerate) - ), + buttonState = AIButtonState.Regenerate(labelForRegenerate), shareMessage = completions, isGenerating = false ) From 1149c519bea12701b0441b8b6f0ae6a52f1f2128 Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Thu, 15 Jun 2023 20:36:21 +0700 Subject: [PATCH 10/33] Add: result logging in AI Repository's fetching. --- .../src/main/kotlin/com/woocommerce/android/ai/AIRepository.kt | 3 +++ .../src/main/kotlin/com/woocommerce/android/util/WooLog.kt | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ai/AIRepository.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ai/AIRepository.kt index bf5dc98bf61a..7452f93ff95b 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ai/AIRepository.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ai/AIRepository.kt @@ -1,5 +1,6 @@ package com.woocommerce.android.ai +import com.woocommerce.android.util.WooLog import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.wordpress.android.fluxc.model.SiteModel @@ -21,9 +22,11 @@ class AIRepository @Inject constructor( jetpackAIStore.fetchJetpackAICompletionsForSite(site, prompt, skipCache).run { when (this) { is JetpackAICompletionsResponse.Success -> { + WooLog.d(WooLog.T.AI, "Fetching Jetpack AI completions succeeded") Result.success(completion) } is JetpackAICompletionsResponse.Error -> { + WooLog.w(WooLog.T.AI, "Fetching Jetpack AI completions failed: $message") Result.failure(Exception(message ?: "Unable to fetch AI completions")) } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/util/WooLog.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/util/WooLog.kt index dc275c270dd5..60cf42d2cef9 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/util/WooLog.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/util/WooLog.kt @@ -33,7 +33,8 @@ object WooLog { PLUGINS, IAP, ONBOARDING, - WOO_TRIAL + WOO_TRIAL, + AI } // Breaking convention to be consistent with org.wordpress.android.util.AppLog From e13cbfdcb7cedfcaa5c3c4fda1470b254d9d292d Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Thu, 15 Jun 2023 20:58:35 +0700 Subject: [PATCH 11/33] Add: display error message on the UI during AI fetching error. --- .../ui/products/ProductSharingBottomSheet.kt | 6 ++++- .../ui/products/ProductSharingViewModel.kt | 25 ++++++++++++++++--- WooCommerce/src/main/res/values/strings.xml | 1 + 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingBottomSheet.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingBottomSheet.kt index 28157ec1c1fe..860b77d55358 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingBottomSheet.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingBottomSheet.kt @@ -76,11 +76,15 @@ fun ProductShareWithAI( if (viewState.isGenerating) { SharingMessageSkeletonView() } else { + val isError = viewState.errorMessage.isNotEmpty() + WCOutlinedTextField( value = viewState.shareMessage, onValueChange = { onShareMessageEdit(it) }, label = stringResource(id = R.string.product_sharing_optional_message_label), - maxLines = 4 + maxLines = 4, + isError = isError, + helperText = if (isError) viewState.errorMessage else null ) } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt index 44c590c9385d..d16ec7e761c9 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt @@ -27,6 +27,7 @@ class ProductSharingViewModel @Inject constructor( private val labelForWriteWithAI = resourceProvider.getString(R.string.product_sharing_write_with_ai) private val labelForGenerating = resourceProvider.getString(R.string.product_sharing_generating) private val labelForRegenerate = resourceProvider.getString(R.string.product_sharing_regenerate) + private val labelForGeneratingFailure = resourceProvider.getString(R.string.product_sharing_ai_generating_failure) private val _viewState = MutableStateFlow( ProductSharingViewState( @@ -40,7 +41,8 @@ class ProductSharingViewModel @Inject constructor( _viewState.update { it.copy( buttonState = AIButtonState.Generating(labelForGenerating), - isGenerating = true + isGenerating = true, + errorMessage = "" ) } @@ -65,7 +67,18 @@ class ProductSharingViewModel @Inject constructor( }, onFailure = { _viewState.update { - it.copy(isGenerating = false) + // This is to return the previous button's state before generating. + val previousButtonState = if (it.buttonState is AIButtonState.Regenerate) { + AIButtonState.Regenerate(labelForRegenerate) + } else { + AIButtonState.WriteWithAI(labelForWriteWithAI) + } + + it.copy( + buttonState = previousButtonState, + errorMessage = labelForGeneratingFailure, + isGenerating = false + ) } } ) @@ -74,14 +87,18 @@ class ProductSharingViewModel @Inject constructor( fun onShareMessageEdited(message: String) { _viewState.update { state -> - state.copy(shareMessage = message) + state.copy( + shareMessage = message, + errorMessage = "" + ) } } data class ProductSharingViewState( val productTitle: String, val shareMessage: String = "", val buttonState: AIButtonState, - val isGenerating: Boolean = false + val isGenerating: Boolean = false, + val errorMessage: String = "" ) sealed class AIButtonState(val label: String) { diff --git a/WooCommerce/src/main/res/values/strings.xml b/WooCommerce/src/main/res/values/strings.xml index b5dd448f0c87..e013a2e9c85b 100644 --- a/WooCommerce/src/main/res/values/strings.xml +++ b/WooCommerce/src/main/res/values/strings.xml @@ -3347,6 +3347,7 @@ Generating… Add an optional message Learn more about our AI feature + Unable to generate sharing message. Please try again! From b07c7dbb161ae308ff55221421ae1d996fddd146 Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Fri, 16 Jun 2023 12:57:45 +0700 Subject: [PATCH 12/33] Update: Prompt for product sharing. --- .../src/main/kotlin/com/woocommerce/android/ai/AIPrompts.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ai/AIPrompts.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ai/AIPrompts.kt index 5ab4ce82a656..fa5b6bae4b12 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ai/AIPrompts.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ai/AIPrompts.kt @@ -17,7 +17,7 @@ object AIPrompts { "their customers a product named \"%1\$s\". More information about the product:\n" + "%2\$s\n" + "- Product URL: %3\$s.\n" + - "Identify the language used in the product name and use the same language in your response.\n" + + "Identify the language used in the product name and product description, if any, to use in your response.\n" + "The length should be up to 3 sentences.\n" + "Use a 9th grade reading level.\n" + "Add related hashtags at the end of the message.\n" + From be9fb507e39a72547f928978b438a9955c570f6c Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Fri, 16 Jun 2023 14:51:23 +0700 Subject: [PATCH 13/33] Feat: Open sharing intent when "Share" button in the AI sharing bottom sheet is used. This allows actual sharing to happen. --- .../ui/products/ProductNavigationTarget.kt | 5 +++++ .../android/ui/products/ProductNavigator.kt | 16 +++++++++++++++- .../ui/products/ProductSharingBottomSheet.kt | 8 +++++--- .../android/ui/products/ProductSharingDialog.kt | 11 +++++++++++ .../ui/products/ProductSharingViewModel.kt | 12 ++++++++++++ 5 files changed, 48 insertions(+), 4 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductNavigationTarget.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductNavigationTarget.kt index 31f699d1aadb..e52f7de12b6e 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductNavigationTarget.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductNavigationTarget.kt @@ -23,6 +23,11 @@ import com.woocommerce.android.viewmodel.MultiLiveEvent.Event */ sealed class ProductNavigationTarget : Event() { data class ShareProduct(val url: String, val title: String) : ProductNavigationTarget() + data class ShareProductWithMessage( + val permalink: String, + val title: String, + val subject: String + ) : ProductNavigationTarget() data class ShareProductWithAI( val permalink: String, val title: String, diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductNavigator.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductNavigator.kt index 5aabf44877f0..5d057042b169 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductNavigator.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductNavigator.kt @@ -21,6 +21,8 @@ import com.woocommerce.android.ui.products.ProductNavigationTarget.NavigateToPro import com.woocommerce.android.ui.products.ProductNavigationTarget.NavigateToVariationSelector import com.woocommerce.android.ui.products.ProductNavigationTarget.RenameProductAttribute import com.woocommerce.android.ui.products.ProductNavigationTarget.ShareProduct +import com.woocommerce.android.ui.products.ProductNavigationTarget.ShareProductWithAI +import com.woocommerce.android.ui.products.ProductNavigationTarget.ShareProductWithMessage import com.woocommerce.android.ui.products.ProductNavigationTarget.ViewGroupedProducts import com.woocommerce.android.ui.products.ProductNavigationTarget.ViewLinkedProducts import com.woocommerce.android.ui.products.ProductNavigationTarget.ViewMediaUploadErrors @@ -82,7 +84,19 @@ class ProductNavigator @Inject constructor() { fragment.startActivity(Intent.createChooser(shareIntent, title)) } - is ProductNavigationTarget.ShareProductWithAI -> { + is ShareProductWithMessage -> { + val shareIntent: Intent = Intent().apply { + val text = target.subject + "\n" + target.permalink + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_TEXT, text) + putExtra(Intent.EXTRA_TITLE, target.title) + type = "text/plain" + } + val title = fragment.resources.getText(R.string.product_share_dialog_title) + fragment.startActivity(Intent.createChooser(shareIntent, title)) + } + + is ShareProductWithAI -> { val action = ProductDetailFragmentDirections .actionProductDetailFragmentToProductSharingFragment( target.permalink, diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingBottomSheet.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingBottomSheet.kt index 860b77d55358..3c39ab14465c 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingBottomSheet.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingBottomSheet.kt @@ -44,7 +44,8 @@ fun ProductSharingBottomSheet(viewModel: ProductSharingViewModel) { ProductShareWithAI( viewState = it, onGenerateButtonClick = viewModel::onGenerateButtonClicked, - onShareMessageEdit = viewModel::onShareMessageEdited + onShareMessageEdit = viewModel::onShareMessageEdited, + onSharingButtonClick = viewModel::onShareButtonClicked ) } } @@ -53,7 +54,8 @@ fun ProductSharingBottomSheet(viewModel: ProductSharingViewModel) { fun ProductShareWithAI( viewState: ProductSharingViewState, onGenerateButtonClick: () -> Unit = {}, - onShareMessageEdit: (String) -> Unit = {} + onShareMessageEdit: (String) -> Unit = {}, + onSharingButtonClick: () -> Unit = {} ) { Column( modifier = Modifier @@ -116,7 +118,7 @@ fun ProductShareWithAI( } WCColoredButton( - onClick = { /*TODO*/ }, + onClick = onSharingButtonClick, modifier = Modifier.fillMaxWidth(), enabled = !viewState.isGenerating ) { diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingDialog.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingDialog.kt index 411536560647..a8cb2703d70b 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingDialog.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingDialog.kt @@ -10,9 +10,12 @@ import androidx.fragment.app.viewModels import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject @AndroidEntryPoint class ProductSharingDialog : BottomSheetDialogFragment() { + @Inject + lateinit var navigator: ProductNavigator private val viewModel: ProductSharingViewModel by viewModels() override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { @@ -26,4 +29,12 @@ class ProductSharingDialog : BottomSheetDialogFragment() { } } } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + viewModel.event.observe(viewLifecycleOwner) { event -> + when (event) { + is ProductNavigationTarget -> navigator.navigate(this, event) + } + } + } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt index d16ec7e761c9..5641ee6f7af5 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt @@ -93,6 +93,18 @@ class ProductSharingViewModel @Inject constructor( ) } } + + fun onShareButtonClicked() { + val subject = _viewState.value.shareMessage.ifEmpty { navArgs.productName } + triggerEvent( + ProductNavigationTarget.ShareProductWithMessage( + permalink = navArgs.permalink, + title = navArgs.productName, + subject = subject + ) + ) + } + data class ProductSharingViewState( val productTitle: String, val shareMessage: String = "", From 4765dadd0325542b955649fc0a081046c025a0cc Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Fri, 16 Jun 2023 14:53:48 +0700 Subject: [PATCH 14/33] Refactor: put sharing subject formatting in viewmodel instead. --- .../woocommerce/android/ui/products/ProductNavigationTarget.kt | 1 - .../com/woocommerce/android/ui/products/ProductNavigator.kt | 3 +-- .../woocommerce/android/ui/products/ProductSharingViewModel.kt | 3 +-- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductNavigationTarget.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductNavigationTarget.kt index e52f7de12b6e..6dba7193d3a1 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductNavigationTarget.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductNavigationTarget.kt @@ -24,7 +24,6 @@ import com.woocommerce.android.viewmodel.MultiLiveEvent.Event sealed class ProductNavigationTarget : Event() { data class ShareProduct(val url: String, val title: String) : ProductNavigationTarget() data class ShareProductWithMessage( - val permalink: String, val title: String, val subject: String ) : ProductNavigationTarget() diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductNavigator.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductNavigator.kt index 5d057042b169..1549d8efd130 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductNavigator.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductNavigator.kt @@ -86,9 +86,8 @@ class ProductNavigator @Inject constructor() { is ShareProductWithMessage -> { val shareIntent: Intent = Intent().apply { - val text = target.subject + "\n" + target.permalink action = Intent.ACTION_SEND - putExtra(Intent.EXTRA_TEXT, text) + putExtra(Intent.EXTRA_TEXT, target.subject) putExtra(Intent.EXTRA_TITLE, target.title) type = "text/plain" } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt index 5641ee6f7af5..1534aed0ead9 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt @@ -95,10 +95,9 @@ class ProductSharingViewModel @Inject constructor( } fun onShareButtonClicked() { - val subject = _viewState.value.shareMessage.ifEmpty { navArgs.productName } + val subject = _viewState.value.shareMessage.ifEmpty { navArgs.productName } + "\n" + navArgs.permalink triggerEvent( ProductNavigationTarget.ShareProductWithMessage( - permalink = navArgs.permalink, title = navArgs.productName, subject = subject ) From a0171df886bec199e4482d3f8b2eba1551c3a66b Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Fri, 16 Jun 2023 15:03:45 +0700 Subject: [PATCH 15/33] Feat: Make info button open Automattic's AI guidelines page. --- .../src/main/kotlin/com/woocommerce/android/AppUrls.kt | 1 + .../android/ui/products/ProductSharingBottomSheet.kt | 10 ++++++---- .../android/ui/products/ProductSharingDialog.kt | 3 +++ .../android/ui/products/ProductSharingViewModel.kt | 8 ++++++++ 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/AppUrls.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/AppUrls.kt index aa3766e587ef..6a948c74083c 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/AppUrls.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/AppUrls.kt @@ -12,6 +12,7 @@ object AppUrls { const val AUTOMATTIC_PRIVACY_POLICY_CA = "https://automattic.com/privacy/#california-consumer-privacy-act-ccpa" const val AUTOMATTIC_COOKIE_POLICY = "https://www.automattic.com/cookies" const val AUTOMATTIC_HIRING = "https://automattic.com/work-with-us" + const val AUTOMATTIC_AI_GUIDELINES = "https://automattic.com/ai-guidelines/" const val WOOCOMMERCE_UPGRADE = "https://docs.woocommerce.com/document/how-to-update-woocommerce/" const val WOOCOMMERCE_PLUGIN = "https://wordpress.org/plugins/woocommerce/" diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingBottomSheet.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingBottomSheet.kt index 3c39ab14465c..e0fab4fcaa33 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingBottomSheet.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingBottomSheet.kt @@ -45,7 +45,8 @@ fun ProductSharingBottomSheet(viewModel: ProductSharingViewModel) { viewState = it, onGenerateButtonClick = viewModel::onGenerateButtonClicked, onShareMessageEdit = viewModel::onShareMessageEdited, - onSharingButtonClick = viewModel::onShareButtonClicked + onSharingButtonClick = viewModel::onShareButtonClicked, + onInfoButtonClick = viewModel::onInfoButtonClicked ) } } @@ -55,7 +56,8 @@ fun ProductShareWithAI( viewState: ProductSharingViewState, onGenerateButtonClick: () -> Unit = {}, onShareMessageEdit: (String) -> Unit = {}, - onSharingButtonClick: () -> Unit = {} + onSharingButtonClick: () -> Unit = {}, + onInfoButtonClick: () -> Unit = {} ) { Column( modifier = Modifier @@ -84,7 +86,7 @@ fun ProductShareWithAI( value = viewState.shareMessage, onValueChange = { onShareMessageEdit(it) }, label = stringResource(id = R.string.product_sharing_optional_message_label), - maxLines = 4, + maxLines = 5, isError = isError, helperText = if (isError) viewState.errorMessage else null ) @@ -104,7 +106,7 @@ fun ProductShareWithAI( } IconButton( - onClick = { /* TODO */ }, + onClick = onInfoButtonClick, enabled = !viewState.isGenerating ) { Icon( diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingDialog.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingDialog.kt index a8cb2703d70b..780d7fe7f448 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingDialog.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingDialog.kt @@ -9,6 +9,8 @@ import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.fragment.app.viewModels import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground +import com.woocommerce.android.util.ChromeCustomTabUtils +import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.LaunchUrlInChromeTab import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @@ -34,6 +36,7 @@ class ProductSharingDialog : BottomSheetDialogFragment() { viewModel.event.observe(viewLifecycleOwner) { event -> when (event) { is ProductNavigationTarget -> navigator.navigate(this, event) + is LaunchUrlInChromeTab -> ChromeCustomTabUtils.launchUrl(requireContext(), event.url) } } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt index 1534aed0ead9..f7cb005b63d4 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt @@ -2,10 +2,12 @@ package com.woocommerce.android.ui.products import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.asLiveData +import com.woocommerce.android.AppUrls import com.woocommerce.android.R import com.woocommerce.android.ai.AIPrompts import com.woocommerce.android.ai.AIRepository import com.woocommerce.android.tools.SelectedSite +import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.LaunchUrlInChromeTab import com.woocommerce.android.viewmodel.ResourceProvider import com.woocommerce.android.viewmodel.ScopedViewModel import com.woocommerce.android.viewmodel.navArgs @@ -104,6 +106,12 @@ class ProductSharingViewModel @Inject constructor( ) } + fun onInfoButtonClicked() { + triggerEvent( + LaunchUrlInChromeTab(AppUrls.AUTOMATTIC_AI_GUIDELINES) + ) + } + data class ProductSharingViewState( val productTitle: String, val shareMessage: String = "", From 5872af63b540c05571313131ec118678908c0204 Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Fri, 16 Jun 2023 16:58:03 +0700 Subject: [PATCH 16/33] Add: Analytics events for AI sharing. --- .../woocommerce/android/analytics/AnalyticsEvent.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/analytics/AnalyticsEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/analytics/AnalyticsEvent.kt index 97c35e1add30..395935c7102b 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/analytics/AnalyticsEvent.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/analytics/AnalyticsEvent.kt @@ -855,5 +855,13 @@ enum class AnalyticsEvent(val siteless: Boolean = false) { // Privacy Banner PRIVACY_CHOICES_BANNER_PRESENTED, PRIVACY_CHOICES_BANNER_SETTINGS_BUTTON_TAPPED, - PRIVACY_CHOICES_BANNER_SAVE_BUTTON_TAPPED + PRIVACY_CHOICES_BANNER_SAVE_BUTTON_TAPPED, + + // AI Features + PRODUCT_SHARING_AI_DISPLAYED, + PRODUCT_SHARING_AI_GENERATE_TAPPED, + PRODUCT_SHARING_AI_SHARE_TAPPED, + PRODUCT_SHARING_AI_DISMISSED, + PRODUCT_SHARING_AI_MESSAGE_GENERATED, + PRODUCT_SHARING_AI_MESSAGE_GENERATION_FAILED } From ce3f00ef41b50abad910cb541134c301019ba8ab Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Fri, 16 Jun 2023 16:58:48 +0700 Subject: [PATCH 17/33] Add: Analytics tracking for sheet displayed. --- .../android/ui/products/ProductSharingViewModel.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt index f7cb005b63d4..efd60a01b205 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt @@ -6,6 +6,8 @@ import com.woocommerce.android.AppUrls import com.woocommerce.android.R import com.woocommerce.android.ai.AIPrompts import com.woocommerce.android.ai.AIRepository +import com.woocommerce.android.analytics.AnalyticsEvent +import com.woocommerce.android.analytics.AnalyticsTrackerWrapper import com.woocommerce.android.tools.SelectedSite import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.LaunchUrlInChromeTab import com.woocommerce.android.viewmodel.ResourceProvider @@ -20,6 +22,7 @@ import javax.inject.Inject @HiltViewModel class ProductSharingViewModel @Inject constructor( private val aiRepository: AIRepository, + private val tracker: AnalyticsTrackerWrapper, private val resourceProvider: ResourceProvider, private val selectedSite: SelectedSite, savedStateHandle: SavedStateHandle @@ -39,6 +42,10 @@ class ProductSharingViewModel @Inject constructor( ) val viewState = _viewState.asLiveData() + init { + tracker.track(AnalyticsEvent.PRODUCT_SHARING_AI_DISPLAYED) + } + fun onGenerateButtonClicked() { _viewState.update { it.copy( From 801311c90a0bd98308024155a70bf895bd5fa60d Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Fri, 16 Jun 2023 17:01:14 +0700 Subject: [PATCH 18/33] Add: Analytics tracking keys for AI sharing. --- .../com/woocommerce/android/analytics/AnalyticsTracker.kt | 4 ++++ 1 file changed, 4 insertions(+) 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 872b7b114777..50a523174974 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/analytics/AnalyticsTracker.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/analytics/AnalyticsTracker.kt @@ -533,6 +533,10 @@ class AnalyticsTracker private constructor(private val context: Context) { const val VALUE_PRODUCT_SELECTOR = "product_selector" const val VALUE_VARIATION_SELECTOR = "variation_selector" + // -- Product sharing with AI + const val KEY_IS_RETRY = "is_retry" + const val KEY_WITH_MESSAGE = "with_message" + var sendUsageStats: Boolean = true set(value) { if (value != field) { From 010290d0ff30207e0451844ea1f3534bc3ddb7f5 Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Fri, 16 Jun 2023 17:04:04 +0700 Subject: [PATCH 19/33] Add: Analytics tracking for button tapped. --- .../android/ui/products/ProductSharingViewModel.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt index efd60a01b205..7dff980a6949 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt @@ -7,6 +7,7 @@ import com.woocommerce.android.R import com.woocommerce.android.ai.AIPrompts import com.woocommerce.android.ai.AIRepository import com.woocommerce.android.analytics.AnalyticsEvent +import com.woocommerce.android.analytics.AnalyticsTracker import com.woocommerce.android.analytics.AnalyticsTrackerWrapper import com.woocommerce.android.tools.SelectedSite import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.LaunchUrlInChromeTab @@ -47,6 +48,14 @@ class ProductSharingViewModel @Inject constructor( } fun onGenerateButtonClicked() { + val isRetry = _viewState.value.buttonState is AIButtonState.Regenerate + tracker.track( + AnalyticsEvent.PRODUCT_SHARING_AI_GENERATE_TAPPED, + mapOf( + AnalyticsTracker.KEY_IS_RETRY to isRetry + ) + ) + _viewState.update { it.copy( buttonState = AIButtonState.Generating(labelForGenerating), From fcab87a926b7cf42d0d2a0dbbe69300444f5c28b Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Fri, 16 Jun 2023 17:10:30 +0700 Subject: [PATCH 20/33] Add: Analytics tracking for share button tapped (with some refactoring). --- .../android/ui/products/ProductSharingViewModel.kt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt index 7dff980a6949..8281cea4213c 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt @@ -113,11 +113,20 @@ class ProductSharingViewModel @Inject constructor( } fun onShareButtonClicked() { - val subject = _viewState.value.shareMessage.ifEmpty { navArgs.productName } + "\n" + navArgs.permalink + val writtenMessage = _viewState.value.shareMessage + val messageToShare = writtenMessage.ifEmpty { navArgs.productName } + "\n" + navArgs.permalink + + tracker.track( + AnalyticsEvent.PRODUCT_SHARING_AI_GENERATE_TAPPED, + mapOf( + AnalyticsTracker.KEY_WITH_MESSAGE to writtenMessage.isEmpty() + ) + ) + triggerEvent( ProductNavigationTarget.ShareProductWithMessage( title = navArgs.productName, - subject = subject + subject = messageToShare ) ) } From b5d5588a07c8f9302973ef28f9dacc2eee43ea31 Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Fri, 16 Jun 2023 17:15:35 +0700 Subject: [PATCH 21/33] Add: Analytics tracking for share dialog dismissed. --- .../woocommerce/android/ui/products/ProductSharingDialog.kt | 6 ++++++ .../android/ui/products/ProductSharingViewModel.kt | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingDialog.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingDialog.kt index 780d7fe7f448..491949591c08 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingDialog.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingDialog.kt @@ -1,5 +1,6 @@ package com.woocommerce.android.ui.products +import android.content.DialogInterface import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -40,4 +41,9 @@ class ProductSharingDialog : BottomSheetDialogFragment() { } } } + + override fun onDismiss(dialog: DialogInterface) { + super.onDismiss(dialog) + viewModel.onDialogDismissed() + } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt index 8281cea4213c..96bdedf4394d 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt @@ -137,6 +137,10 @@ class ProductSharingViewModel @Inject constructor( ) } + fun onDialogDismissed() { + tracker.track(AnalyticsEvent.PRODUCT_SHARING_AI_DISMISSED) + } + data class ProductSharingViewState( val productTitle: String, val shareMessage: String = "", From 92786e6424686b2d51a9353fef238f6783dfc909 Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Fri, 16 Jun 2023 17:16:37 +0700 Subject: [PATCH 22/33] Add: Analytics tracking for AI message generated. --- .../woocommerce/android/ui/products/ProductSharingViewModel.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt index 96bdedf4394d..91a29ade48f9 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt @@ -75,6 +75,8 @@ class ProductSharingViewModel @Inject constructor( ) result.fold( onSuccess = { completions -> + tracker.track(AnalyticsEvent.PRODUCT_SHARING_AI_MESSAGE_GENERATED) + _viewState.update { it.copy( buttonState = AIButtonState.Regenerate(labelForRegenerate), From 188d0ef683a4c19cb7044bb11b92f3d735f7fbdc Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Fri, 16 Jun 2023 17:48:17 +0700 Subject: [PATCH 23/33] Add: Analytics tracking for AI message generation failure. --- .../com/woocommerce/android/ai/AIRepository.kt | 12 +++++++++++- .../android/ui/products/ProductSharingViewModel.kt | 13 ++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ai/AIRepository.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ai/AIRepository.kt index 7452f93ff95b..51cc79d7e719 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ai/AIRepository.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ai/AIRepository.kt @@ -27,9 +27,19 @@ class AIRepository @Inject constructor( } is JetpackAICompletionsResponse.Error -> { WooLog.w(WooLog.T.AI, "Fetching Jetpack AI completions failed: $message") - Result.failure(Exception(message ?: "Unable to fetch AI completions")) + Result.failure(this.mapToException()) } } } } + data class JetpackAICompletionsException( + val errorMessage: String, + val errorType: String + ) : Exception(errorMessage) + + private fun JetpackAICompletionsResponse.Error.mapToException() = + JetpackAICompletionsException( + errorMessage = message ?: "Unable to fetch AI completions", + errorType = type.name + ) } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt index 91a29ade48f9..4c0c02640219 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt @@ -85,7 +85,18 @@ class ProductSharingViewModel @Inject constructor( ) } }, - onFailure = { + onFailure = { exception -> + val completionsError = exception as AIRepository.JetpackAICompletionsException + + tracker.track( + AnalyticsEvent.PRODUCT_SHARING_AI_MESSAGE_GENERATION_FAILED, + mapOf( + AnalyticsTracker.KEY_ERROR_CONTEXT to this::class.java.simpleName, + AnalyticsTracker.KEY_ERROR_TYPE to completionsError.errorType, + AnalyticsTracker.KEY_ERROR_DESC to completionsError.errorMessage + ) + ) + _viewState.update { // This is to return the previous button's state before generating. val previousButtonState = if (it.buttonState is AIButtonState.Regenerate) { From 1a92fbf43b9540fbee865e7f47389448205bbd1d Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Fri, 16 Jun 2023 17:54:18 +0700 Subject: [PATCH 24/33] Refactor: Make onGenerateButtonClicked() smaller function. --- .../ui/products/ProductSharingViewModel.kt | 75 ++++++++++--------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt index 4c0c02640219..ec55d34f3251 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt @@ -6,6 +6,7 @@ import com.woocommerce.android.AppUrls import com.woocommerce.android.R import com.woocommerce.android.ai.AIPrompts import com.woocommerce.android.ai.AIRepository +import com.woocommerce.android.ai.AIRepository.JetpackAICompletionsException import com.woocommerce.android.analytics.AnalyticsEvent import com.woocommerce.android.analytics.AnalyticsTracker import com.woocommerce.android.analytics.AnalyticsTrackerWrapper @@ -75,47 +76,53 @@ class ProductSharingViewModel @Inject constructor( ) result.fold( onSuccess = { completions -> - tracker.track(AnalyticsEvent.PRODUCT_SHARING_AI_MESSAGE_GENERATED) - - _viewState.update { - it.copy( - buttonState = AIButtonState.Regenerate(labelForRegenerate), - shareMessage = completions, - isGenerating = false - ) - } + handleCompletionsSuccess(completions) }, onFailure = { exception -> - val completionsError = exception as AIRepository.JetpackAICompletionsException - - tracker.track( - AnalyticsEvent.PRODUCT_SHARING_AI_MESSAGE_GENERATION_FAILED, - mapOf( - AnalyticsTracker.KEY_ERROR_CONTEXT to this::class.java.simpleName, - AnalyticsTracker.KEY_ERROR_TYPE to completionsError.errorType, - AnalyticsTracker.KEY_ERROR_DESC to completionsError.errorMessage - ) - ) - - _viewState.update { - // This is to return the previous button's state before generating. - val previousButtonState = if (it.buttonState is AIButtonState.Regenerate) { - AIButtonState.Regenerate(labelForRegenerate) - } else { - AIButtonState.WriteWithAI(labelForWriteWithAI) - } - - it.copy( - buttonState = previousButtonState, - errorMessage = labelForGeneratingFailure, - isGenerating = false - ) - } + handleCompletionsFailure(exception as JetpackAICompletionsException) } ) } } + private fun handleCompletionsSuccess(completions: String) { + tracker.track(AnalyticsEvent.PRODUCT_SHARING_AI_MESSAGE_GENERATED) + + _viewState.update { + it.copy( + buttonState = AIButtonState.Regenerate(labelForRegenerate), + shareMessage = completions, + isGenerating = false + ) + } + } + + private fun handleCompletionsFailure(error: JetpackAICompletionsException) { + tracker.track( + AnalyticsEvent.PRODUCT_SHARING_AI_MESSAGE_GENERATION_FAILED, + mapOf( + AnalyticsTracker.KEY_ERROR_CONTEXT to this::class.java.simpleName, + AnalyticsTracker.KEY_ERROR_TYPE to error.errorType, + AnalyticsTracker.KEY_ERROR_DESC to error.errorMessage + ) + ) + + _viewState.update { + // This is to return the previous button's state before generating. + val previousButtonState = if (it.buttonState is AIButtonState.Regenerate) { + AIButtonState.Regenerate(labelForRegenerate) + } else { + AIButtonState.WriteWithAI(labelForWriteWithAI) + } + + it.copy( + buttonState = previousButtonState, + errorMessage = labelForGeneratingFailure, + isGenerating = false + ) + } + } + fun onShareMessageEdited(message: String) { _viewState.update { state -> state.copy( From f60dfd5bed643b9f86bbc0d06a50ad38cbd8e64c Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Fri, 16 Jun 2023 17:58:51 +0700 Subject: [PATCH 25/33] Feat: enable Sharing product AI in feature flag. --- .../main/kotlin/com/woocommerce/android/util/FeatureFlag.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/util/FeatureFlag.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/util/FeatureFlag.kt index 319e40a23a48..598b8a1553d0 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/util/FeatureFlag.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/util/FeatureFlag.kt @@ -53,12 +53,12 @@ enum class FeatureFlag { STORE_CREATION_PROFILER, COMPOSITE_PRODUCTS_READ_ONLY_SUPPORT, EU_SHIPPING_NOTIFICATION, - PRIVACY_CHOICES -> true + PRIVACY_CHOICES, + SHARING_PRODUCT_AI-> true MORE_MENU_INBOX, WC_SHIPPING_BANNER, IPP_TAP_TO_PAY, - SHARING_PRODUCT_AI, BLAZE -> PackageUtils.isDebugBuild() IAP_FOR_STORE_CREATION -> false From e3a7be730e5408edd40e83594c3ed44a4dfea1a9 Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Fri, 16 Jun 2023 18:02:31 +0700 Subject: [PATCH 26/33] Add: release note. The note itself is generated by ChatGPT. --- RELEASE-NOTES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 72e0ba5d6634..e3b350ecef1a 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -4,6 +4,7 @@ - [*][Internal] Fixed database access race condition in Product Selector affecting local search performance and featured products selection [https://github.com/woocommerce/woocommerce-android/pull/9202] - [*] [Internal] Added caching mechanism for JITMs [https://github.com/woocommerce/woocommerce-android/pull/9158] - [*] [Internal] Fixed coupon validation logic [https://github.com/woocommerce/woocommerce-android/pull/9210] +- [***] AI: Elevate your product sharing experience with our new AI feature that effortlessly generates captivating sharing messages for you. [https://github.com/woocommerce/woocommerce-android/pull/9236] 14.0 ----- From f59f569613df97da0cecd8aff4d5ef56b2e43502 Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Fri, 16 Jun 2023 18:10:42 +0700 Subject: [PATCH 27/33] Fix: Detekt. --- .../src/main/kotlin/com/woocommerce/android/util/FeatureFlag.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/util/FeatureFlag.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/util/FeatureFlag.kt index 598b8a1553d0..725e4acd7b36 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/util/FeatureFlag.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/util/FeatureFlag.kt @@ -54,7 +54,7 @@ enum class FeatureFlag { COMPOSITE_PRODUCTS_READ_ONLY_SUPPORT, EU_SHIPPING_NOTIFICATION, PRIVACY_CHOICES, - SHARING_PRODUCT_AI-> true + SHARING_PRODUCT_AI -> true MORE_MENU_INBOX, WC_SHIPPING_BANNER, From 4c51592af486bae9617fb104f9a176b187877f10 Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Fri, 16 Jun 2023 21:35:46 +0700 Subject: [PATCH 28/33] Fix: use correct tracking for share. --- .../woocommerce/android/ui/products/ProductSharingViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt index ec55d34f3251..fadb8c8bd71c 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductSharingViewModel.kt @@ -137,7 +137,7 @@ class ProductSharingViewModel @Inject constructor( val messageToShare = writtenMessage.ifEmpty { navArgs.productName } + "\n" + navArgs.permalink tracker.track( - AnalyticsEvent.PRODUCT_SHARING_AI_GENERATE_TAPPED, + AnalyticsEvent.PRODUCT_SHARING_AI_SHARE_TAPPED, mapOf( AnalyticsTracker.KEY_WITH_MESSAGE to writtenMessage.isEmpty() ) From 61b02696d43c6d8c7199e35521472cd2b133c752 Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Fri, 16 Jun 2023 21:52:16 +0700 Subject: [PATCH 29/33] Refactor: Open product sharing feature only to WordPress.com-hosted sites for now. Otherwise it opens the old sharing feature as usual. --- RELEASE-NOTES.txt | 2 +- .../woocommerce/android/ui/products/ProductDetailViewModel.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index e3b350ecef1a..07462cab4bc6 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -4,7 +4,7 @@ - [*][Internal] Fixed database access race condition in Product Selector affecting local search performance and featured products selection [https://github.com/woocommerce/woocommerce-android/pull/9202] - [*] [Internal] Added caching mechanism for JITMs [https://github.com/woocommerce/woocommerce-android/pull/9158] - [*] [Internal] Fixed coupon validation logic [https://github.com/woocommerce/woocommerce-android/pull/9210] -- [***] AI: Elevate your product sharing experience with our new AI feature that effortlessly generates captivating sharing messages for you. [https://github.com/woocommerce/woocommerce-android/pull/9236] +- [***] AI: WordPress.com-hosted sites now has access to an experimental AI feature that generates product sharing messages for you. [https://github.com/woocommerce/woocommerce-android/pull/9236] 14.0 ----- diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailViewModel.kt index 45db00910233..d56b5a2a4b16 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailViewModel.kt @@ -392,7 +392,7 @@ class ProductDetailViewModel @Inject constructor( ) viewState.productDraft?.let { - if (FeatureFlag.SHARING_PRODUCT_AI.isEnabled()) { + if (FeatureFlag.SHARING_PRODUCT_AI.isEnabled() && selectedSite.get().isWPCom) { triggerEvent( ProductNavigationTarget.ShareProductWithAI( it.permalink, From 08d1c5359b9127a97a959565f39e09e908b914d1 Mon Sep 17 00:00:00 2001 From: jorgemucientesfayos Date: Fri, 16 Jun 2023 17:06:58 +0200 Subject: [PATCH 30/33] Use the isWPComAtomic property to correctly determine if site is wpcom --- .../woocommerce/android/ui/products/ProductDetailViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailViewModel.kt index d56b5a2a4b16..d2a5b266b250 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailViewModel.kt @@ -392,7 +392,7 @@ class ProductDetailViewModel @Inject constructor( ) viewState.productDraft?.let { - if (FeatureFlag.SHARING_PRODUCT_AI.isEnabled() && selectedSite.get().isWPCom) { + if (FeatureFlag.SHARING_PRODUCT_AI.isEnabled() && selectedSite.get().isWPComAtomic) { triggerEvent( ProductNavigationTarget.ShareProductWithAI( it.permalink, From 501a406b14675ec5b234c2b309eaa768976d1c9a Mon Sep 17 00:00:00 2001 From: jorgemucientesfayos Date: Fri, 16 Jun 2023 17:17:05 +0200 Subject: [PATCH 31/33] Fix detekt indentation issue after merge with trunk --- .../src/main/kotlin/com/woocommerce/android/util/FeatureFlag.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/util/FeatureFlag.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/util/FeatureFlag.kt index a860e7cd7a7e..2de0c0fdd998 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/util/FeatureFlag.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/util/FeatureFlag.kt @@ -59,7 +59,7 @@ enum class FeatureFlag { MORE_MENU_INBOX, WC_SHIPPING_BANNER, - IPP_TAP_TO_PAY -> PackageUtils.isDebugBuild() + IPP_TAP_TO_PAY -> PackageUtils.isDebugBuild() IAP_FOR_STORE_CREATION -> false } From 1a2c6e55206e54c19e1e11595c520d7c962438c8 Mon Sep 17 00:00:00 2001 From: Hafiz Rahman Date: Fri, 16 Jun 2023 22:22:07 +0700 Subject: [PATCH 32/33] Fix: add checking for allowing a site to use sharing with AI. Has to be a WPCom Atomic site, and is public. --- .../android/ui/products/ProductDetailViewModel.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailViewModel.kt index e909921098c6..5d9f6f442b28 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailViewModel.kt @@ -28,6 +28,7 @@ import com.woocommerce.android.extensions.containsItem import com.woocommerce.android.extensions.fastStripHtml import com.woocommerce.android.extensions.getList import com.woocommerce.android.extensions.isEmpty +import com.woocommerce.android.extensions.isSitePublic import com.woocommerce.android.extensions.orNullIfEmpty import com.woocommerce.android.extensions.removeItem import com.woocommerce.android.media.MediaFilesRepository @@ -396,7 +397,7 @@ class ProductDetailViewModel @Inject constructor( ) viewState.productDraft?.let { - if (FeatureFlag.SHARING_PRODUCT_AI.isEnabled() && selectedSite.get().isWPComAtomic) { + if (canSiteUseSharingWithAI()) { triggerEvent( ProductNavigationTarget.ShareProductWithAI( it.permalink, @@ -411,6 +412,13 @@ class ProductDetailViewModel @Inject constructor( } } + private fun canSiteUseSharingWithAI() : Boolean { + return FeatureFlag.SHARING_PRODUCT_AI.isEnabled() && + selectedSite.get().isSitePublic && + selectedSite.get().isWPComAtomic + } + + fun onBlazeClicked() { tracker.track( stat = BLAZE_ENTRY_POINT_TAPPED, From c1903e4ef05d824cf639077c9af5b208307e99b0 Mon Sep 17 00:00:00 2001 From: jorgemucientesfayos Date: Fri, 16 Jun 2023 17:32:02 +0200 Subject: [PATCH 33/33] Fix detekt spacing issue --- .../woocommerce/android/ui/products/ProductDetailViewModel.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailViewModel.kt index 5d9f6f442b28..4347220153ba 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ProductDetailViewModel.kt @@ -412,13 +412,12 @@ class ProductDetailViewModel @Inject constructor( } } - private fun canSiteUseSharingWithAI() : Boolean { + private fun canSiteUseSharingWithAI(): Boolean { return FeatureFlag.SHARING_PRODUCT_AI.isEnabled() && selectedSite.get().isSitePublic && selectedSite.get().isWPComAtomic } - fun onBlazeClicked() { tracker.track( stat = BLAZE_ENTRY_POINT_TAPPED,