Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ class BlazeRepository @Inject constructor(

suspend fun fetchAdSuggestions(productId: Long): Result<List<AiSuggestionForAd>> {
fun List<BlazeAdSuggestion>.mapToUiModel(): List<AiSuggestionForAd> {
return map { AiSuggestionForAd(it.tagLine, it.description) }
return map { AiSuggestionForAd(it.tagLine, it.description, it.ctaText) }
}

val result = blazeCampaignsStore.fetchBlazeAdSuggestions(selectedSite.get(), productId)
Expand Down Expand Up @@ -194,7 +194,8 @@ class BlazeRepository @Inject constructor(
targetUrl = product.permalink,
parameters = emptyMap()
),
objectiveId = appPrefsWrapper.blazeCampaignSelectedObjective
objectiveId = appPrefsWrapper.blazeCampaignSelectedObjective,
ctaText = "TODO"
)
}

Expand Down Expand Up @@ -273,6 +274,7 @@ class BlazeRepository @Inject constructor(
)
}

@Suppress("LongMethod")
suspend fun createCampaign(
campaignDetails: CampaignDetails,
paymentMethodId: String
Expand All @@ -297,6 +299,7 @@ class BlazeRepository @Inject constructor(
targetResourceId = campaignDetails.productId,
tagLine = campaignDetails.tagLine,
description = campaignDetails.description,
ctaText = campaignDetails.ctaText,
startDate = campaignDetails.budget.startDate,
endDate = campaignDetails.budget.endDate,
budget = BlazeCampaignCreationRequestBudget(
Expand Down Expand Up @@ -394,6 +397,7 @@ class BlazeRepository @Inject constructor(
val productId: Long,
val tagLine: String,
val description: String,
val ctaText: String,
val campaignImage: BlazeCampaignImage,
val budget: Budget,
val targetingParameters: TargetingParameters,
Expand Down Expand Up @@ -446,6 +450,7 @@ class BlazeRepository @Inject constructor(
data class AiSuggestionForAd(
val tagLine: String,
val description: String,
val ctaText: String
) : Parcelable

@Parcelize
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ fun BlazeCampaignCreationPreviewScreen(viewModel: BlazeCampaignCreationEditAdVie
onMediaPickerDialogDismissed = viewModel::onMediaPickerDialogDismissed,
onProductImagesRequested = viewModel::onProductImagesRequested,
onMediaLibraryRequested = viewModel::onMediaLibraryRequested,
onSaveTapped = viewModel::onSaveTapped
onSaveTapped = viewModel::onSaveTapped,
onCtaTextChanged = viewModel::onCtaTextChanged
)
}
}
Expand All @@ -88,7 +89,8 @@ private fun BlazeCampaignCreationEditAdScreen(
onMediaPickerDialogDismissed: () -> Unit,
onProductImagesRequested: () -> Unit,
onMediaLibraryRequested: (DataSource) -> Unit,
onSaveTapped: () -> Unit
onSaveTapped: () -> Unit,
onCtaTextChanged: (String) -> Unit
) {
if (viewState.isMediaPickerDialogVisible) {
MediaPickerDialog(
Expand Down Expand Up @@ -123,7 +125,8 @@ private fun BlazeCampaignCreationEditAdScreen(
onDescriptionChanged,
onChangeImageTapped,
onPreviousSuggestionTapped,
onNextSuggestionTapped
onNextSuggestionTapped,
onCtaTextChanged
)
}
}
Expand All @@ -137,6 +140,7 @@ fun CampaignEditAdContent(
onChangeImageTapped: () -> Unit,
onPreviousSuggestionTapped: () -> Unit,
onNextSuggestionTapped: () -> Unit,
onCtaTextChanged: (String) -> Unit
) {
Column(
modifier = Modifier
Expand All @@ -156,7 +160,8 @@ fun CampaignEditAdContent(
onTagLineChanged = onTagLineChanged,
onDescriptionChanged = onDescriptionChanged,
onPreviousSuggestionTapped = onPreviousSuggestionTapped,
onNextSuggestionTapped = onNextSuggestionTapped
onNextSuggestionTapped = onNextSuggestionTapped,
onCtaTextChanged = onCtaTextChanged
)
}
}
Expand All @@ -167,70 +172,25 @@ private fun AdDataSection(
onTagLineChanged: (String) -> Unit,
onDescriptionChanged: (String) -> Unit,
onPreviousSuggestionTapped: () -> Unit,
onNextSuggestionTapped: () -> Unit
onNextSuggestionTapped: () -> Unit,
onCtaTextChanged: (String) -> Unit
) {
Column(
modifier = Modifier
.padding(dimensionResource(id = dimen.major_100))
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Box {
WCOutlinedTextField(
value = viewState.tagLine,
onValueChange = onTagLineChanged,
label = stringResource(id = string.blaze_campaign_edit_ad_change_tagline_title),
singleLine = true,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next)
)

CornerCharacterWarning(
charsLeft = viewState.taglineCharactersRemaining,
modifier = Modifier.align(Alignment.TopEnd)
)
}

Text(
text = stringResource(
id = string.blaze_campaign_edit_ad_characters_remaining,
viewState.taglineCharactersRemaining
),
style = MaterialTheme.typography.caption,
color = colorResource(id = color.color_on_surface_disabled),
modifier = Modifier
.padding(top = dimensionResource(id = dimen.minor_100))
.fillMaxWidth()
TaglineInputText(viewState, onTagLineChanged)
DescriptionInputText(
viewState,
onDescriptionChanged,
modifier = Modifier.padding(top = dimensionResource(id = dimen.major_150))
)

Box(
modifier = Modifier
.padding(top = dimensionResource(id = dimen.major_150))
) {
WCOutlinedTextField(
value = viewState.description,
onValueChange = onDescriptionChanged,
label = stringResource(id = string.blaze_campaign_edit_ad_change_description_title),
maxLines = 3,
minLines = 3,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next)
)

CornerCharacterWarning(
charsLeft = viewState.descriptionCharactersRemaining,
modifier = Modifier.align(Alignment.TopEnd)
)
}

Text(
text = stringResource(
id = string.blaze_campaign_edit_ad_characters_remaining,
viewState.descriptionCharactersRemaining
),
style = MaterialTheme.typography.caption,
color = colorResource(id = color.color_on_surface_disabled),
modifier = Modifier
.padding(top = dimensionResource(id = dimen.minor_100))
.fillMaxWidth()
CallToActionInputText(
viewState,
onCtaTextChanged,
modifier = Modifier.padding(top = dimensionResource(id = dimen.major_150))
)

if (viewState.suggestions.size > 1) {
Expand Down Expand Up @@ -271,6 +231,109 @@ private fun AdDataSection(
}
}

@Composable
private fun DescriptionInputText(
viewState: ViewState,
onDescriptionChanged: (String) -> Unit,
modifier: Modifier = Modifier
) {
Box(modifier = modifier) {
WCOutlinedTextField(
value = viewState.description,
onValueChange = onDescriptionChanged,
label = stringResource(id = string.blaze_campaign_edit_ad_change_description_title),
maxLines = 3,
minLines = 3,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next)
)

CornerCharacterWarning(
charsLeft = viewState.descriptionCharactersRemaining,
modifier = Modifier.align(Alignment.TopEnd)
)
}

Text(
text = stringResource(
id = string.blaze_campaign_edit_ad_characters_remaining,
viewState.descriptionCharactersRemaining
),
style = MaterialTheme.typography.caption,
color = colorResource(id = color.color_on_surface_disabled),
modifier = Modifier
.padding(top = dimensionResource(id = dimen.minor_100))
.fillMaxWidth()
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a review, but just a suggestion here, instead of using a separate Text field here, we can simplify this by using the helper argument of the WCOutlinedTextField, it should then prevent design discrepancies between this screen and other screens where we use the same pattern.

The helper can be used to show an error if we pass isError = true, this could be helpful if we want to follow what iOS does by showing it as error when the value is empty (This is a point that I think we should fix for Android, as empty values are accepted for tagline and description, where they shouldn't be).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point @hichamboushaba I've applied your suggestion to keep the component's experience consistent across the different screens.
Also added support for the case where either tagline or description are empty.

}

@Composable
private fun TaglineInputText(
viewState: ViewState,
onTagLineChanged: (String) -> Unit,
modifier: Modifier = Modifier
) {
Box(modifier = modifier) {
WCOutlinedTextField(
value = viewState.tagLine,
onValueChange = onTagLineChanged,
label = stringResource(id = string.blaze_campaign_edit_ad_change_tagline_title),
singleLine = true,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next)
)

CornerCharacterWarning(
charsLeft = viewState.taglineCharactersRemaining,
modifier = Modifier.align(Alignment.TopEnd)
)
}

Text(
text = stringResource(
id = string.blaze_campaign_edit_ad_characters_remaining,
viewState.taglineCharactersRemaining
),
style = MaterialTheme.typography.caption,
color = colorResource(id = color.color_on_surface_disabled),
modifier = Modifier
.padding(top = dimensionResource(id = dimen.minor_100))
.fillMaxWidth()
)
}

@Composable
private fun CallToActionInputText(
viewState: ViewState,
onCtaTextChanged: (String) -> Unit,
modifier: Modifier = Modifier
) {
Box(modifier = modifier) {
WCOutlinedTextField(
value = viewState.ctaText,
onValueChange = onCtaTextChanged,
label = stringResource(id = string.blaze_campaign_edit_ad_change_cta_text_title),
singleLine = true,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next)
)

CornerCharacterWarning(
charsLeft = viewState.ctaTextCharactersRemaining,
modifier = Modifier.align(Alignment.TopEnd)
)
}

Text(
text = stringResource(
id = string.blaze_campaign_edit_ad_characters_remaining,
viewState.ctaTextCharactersRemaining
),
style = MaterialTheme.typography.caption,
color = colorResource(id = color.color_on_surface_disabled),
modifier = Modifier
.padding(top = dimensionResource(id = dimen.minor_100))
.fillMaxWidth()
)
}

@Composable
private fun CornerCharacterWarning(charsLeft: Int, modifier: Modifier = Modifier) {
if (charsLeft < 10) {
Expand Down Expand Up @@ -380,7 +443,8 @@ fun PreviewCampaignEditAdContent() {
onDescriptionChanged = { },
onChangeImageTapped = { },
onPreviousSuggestionTapped = { },
onNextSuggestionTapped = { }
onNextSuggestionTapped = { },
onCtaTextChanged = { },
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class BlazeCampaignCreationEditAdViewModel @Inject constructor(
companion object {
private const val TAGLINE_MAX_LENGTH = 32
private const val DESCRIPTION_MAX_LENGTH = 140
private const val CTA_TEST_MAX_LENGTH = 26
}

private val navArgs: BlazeCampaignCreationEditAdFragmentArgs by savedStateHandle.navArgs()
Expand All @@ -49,7 +50,7 @@ class BlazeCampaignCreationEditAdViewModel @Inject constructor(

private fun loadSuggestions() {
viewModelScope.launch {
val passedDetails = AiSuggestionForAd(navArgs.tagline, navArgs.description)
val passedDetails = AiSuggestionForAd(navArgs.tagline, navArgs.description, navArgs.ctaText)
val suggestions = navArgs.aiSuggestionsForAd.toList()
_viewState.update {
it.copy(
Expand Down Expand Up @@ -83,7 +84,8 @@ class BlazeCampaignCreationEditAdViewModel @Inject constructor(
EditAdResult(
tagline = _viewState.value.tagLine,
description = _viewState.value.description,
campaignImage = _viewState.value.adImage
campaignImage = _viewState.value.adImage,
ctaText = _viewState.value.ctaText
)
)
)
Expand All @@ -107,11 +109,23 @@ class BlazeCampaignCreationEditAdViewModel @Inject constructor(
}

fun onTagLineChanged(tagLine: String) {
updateSuggestion(AiSuggestionForAd(tagLine.take(TAGLINE_MAX_LENGTH), _viewState.value.description))
updateSuggestion(
AiSuggestionForAd(
tagLine.take(TAGLINE_MAX_LENGTH),
_viewState.value.description,
_viewState.value.ctaText
)
)
}

fun onDescriptionChanged(description: String) {
updateSuggestion(AiSuggestionForAd(_viewState.value.tagLine, description.take(DESCRIPTION_MAX_LENGTH)))
updateSuggestion(
AiSuggestionForAd(
_viewState.value.tagLine,
description.take(DESCRIPTION_MAX_LENGTH),
_viewState.value.ctaText
)
)
}

fun onLocalImageSelected(uri: String) {
Expand Down Expand Up @@ -175,6 +189,16 @@ class BlazeCampaignCreationEditAdViewModel @Inject constructor(
setMediaPickerDialogVisibility(false)
}

fun onCtaTextChanged(ctaText: String) {
updateSuggestion(
AiSuggestionForAd(
_viewState.value.tagLine,
_viewState.value.description,
ctaText.take(CTA_TEST_MAX_LENGTH)
)
)
}

data class ShowMediaLibrary(val source: MediaPickerSetup.DataSource) : Event()
data class ShowProductImagePicker(val productId: Long) : Event()

Expand All @@ -189,10 +213,14 @@ class BlazeCampaignCreationEditAdViewModel @Inject constructor(
get() = suggestions.getOrNull(suggestionIndex)?.tagLine ?: ""
val description: String
get() = suggestions.getOrNull(suggestionIndex)?.description ?: ""
val ctaText: String
get() = suggestions.getOrNull(suggestionIndex)?.ctaText ?: ""
val taglineCharactersRemaining: Int
get() = TAGLINE_MAX_LENGTH - (suggestions.getOrNull(suggestionIndex)?.tagLine?.length ?: 0)
val descriptionCharactersRemaining: Int
get() = DESCRIPTION_MAX_LENGTH - (suggestions.getOrNull(suggestionIndex)?.description?.length ?: 0)
val ctaTextCharactersRemaining: Int
get() = CTA_TEST_MAX_LENGTH - (suggestions.getOrNull(suggestionIndex)?.ctaText?.length ?: 0)
val isPreviousSuggestionButtonEnabled: Boolean
get() = suggestionIndex > 0
val isNextSuggestionButtonEnabled: Boolean
Expand All @@ -203,6 +231,7 @@ class BlazeCampaignCreationEditAdViewModel @Inject constructor(
data class EditAdResult(
val tagline: String,
val description: String,
val campaignImage: BlazeRepository.BlazeCampaignImage
val campaignImage: BlazeRepository.BlazeCampaignImage,
val ctaText: String
) : Parcelable
}
Loading
Loading