Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update afterpay/clearpay to support FR and ES. #5221

Merged
merged 24 commits into from
Jul 6, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a2cd70c
Translate the afterpay messaging element.
michelleb-stripe Jun 27, 2022
e234927
[WIP] Looking pretty solid with the side by side comparison of origin…
michelleb-stripe Jun 27, 2022
71aaad0
Merge remote-tracking branch 'origin/master' into michelleb/afterpay-…
michelleb-stripe Jun 27, 2022
3c415dd
CLicking the link is now working correctly when there is an image in …
michelleb-stripe Jun 27, 2022
25940b4
Merge remote-tracking branch 'origin/master' into michelleb/afterpay-…
michelleb-stripe Jun 27, 2022
c4d324f
Code cleanup still original and new.
michelleb-stripe Jun 28, 2022
b294f15
Fix the logo and string name so it appears correctly based on country.
michelleb-stripe Jun 28, 2022
1553ad3
Add string translations.
michelleb-stripe Jun 28, 2022
1ea81b2
CLeanup, improve tests.
michelleb-stripe Jun 28, 2022
7aa2be1
Fix ktlint errors
michelleb-stripe Jun 28, 2022
8bf3b59
Remove the side by side comparison.
michelleb-stripe Jun 28, 2022
35eee61
apiDump
michelleb-stripe Jun 28, 2022
e94636b
Update the changelog
michelleb-stripe Jun 28, 2022
5a74a25
Update the changelog
michelleb-stripe Jun 28, 2022
9d5207b
Merge remote-tracking branch 'origin/master' into michelleb/afterpay-…
michelleb-stripe Jun 28, 2022
4cdac38
Fix rest of detkt errors.
michelleb-stripe Jun 28, 2022
355b295
Add France for 3 installment testing.
michelleb-stripe Jun 28, 2022
2eb7fd4
Merge with master.
michelleb-stripe Jun 28, 2022
81ea5e1
Add missing translations.
michelleb-stripe Jun 28, 2022
dcfefcd
Fixed the modal for new countries.
michelleb-stripe Jun 30, 2022
689db6a
IT is also clearpay even if not released.
michelleb-stripe Jun 30, 2022
3d777ba
Fix modal link.
michelleb-stripe Jun 30, 2022
f42a527
Merge remote-tracking branch 'origin/master' into michelleb/afterpay-…
michelleb-stripe Jul 5, 2022
455f2c6
Get the afterpay url each time the url is clicked.
michelleb-stripe Jul 5, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 0 additions & 6 deletions payments-ui-core/res/values/donottranslate.xml
Expand Up @@ -14,12 +14,6 @@
<string name="stripe_paymentsheet_payment_method_affirm">Affirm</string>
<!-- <string name="paymentsheet_payment_method_item_card_number">····%1$s</string>-->

<!-- Message shown for Afterpay/Clearpay payments. Placeholder will be replaced with the installment amount,
and the Afterpay/Clearpay logo will be added at the end of the message, so the translation must make sense
when reading as: "Pay in 4 interest-free payments of $100 with <Afterpay> ⓘ"
This will not be translated initially as it must have specific values per language -->
<string name="afterpay_clearpay_message">Pay in 4 interest-free payments of %s with</string>

<!-- &lt;!&ndash; Label indicating the payment intent is in testmode &ndash;&gt;-->
<!-- <string name="stripe_paymentsheet_test_mode_indicator">TEST MODE</string>-->
</resources>
2 changes: 2 additions & 0 deletions payments-ui-core/res/values/strings.xml
Expand Up @@ -38,6 +38,8 @@
<string name="address_label_suburb">Suburb</string>
<!-- Used to describe an address field. -->
<string name="address_label_village_township">Village or Township</string>
<!-- This shows the message on Buy Now Pay Later LPM, afterpay. After the word "with" an Afterpay logo is displayed. can be moved to move the logo in the string. -->
<string name="afterpay_clearpay_message"><![CDATA[Pay in <num_installments/> interest-free payments of <installment_price/> with <img/>]]></string>
<!-- Billing address section title for card form entry. -->
<string name="billing_details">Billing address</string>
<!-- Used when a required field is blank and submit is clicked. -->
Expand Down
Expand Up @@ -5,6 +5,8 @@ import android.net.Uri
import androidx.annotation.RestrictTo
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.MaterialTheme
Expand All @@ -17,6 +19,8 @@ import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.PlaceholderVerticalAlign
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
Expand All @@ -31,6 +35,48 @@ import com.stripe.android.ui.core.shouldUseDarkDynamicColor
fun AfterpayClearpayElementUI(
enabled: Boolean,
element: AfterpayClearpayHeaderElement
) {
AfterpayClearpayElementUINew(enabled = enabled, element = element)
AfterpayClearpayElementUIOriginal(enabled = enabled, element = element)
}

@Composable
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun AfterpayClearpayElementUINew(
enabled: Boolean,
element: AfterpayClearpayHeaderElement
) {
val context = LocalContext.current
val messageFormatString = element.getLabel(context.resources)
.replace("<img/>", "<img src=\"afterpay\"/>")

Html(
html = messageFormatString,
enabled = false,
imageGetter = mapOf(
"afterpay" to EmbeddableImage(
R.drawable.stripe_ic_afterpay_clearpay_logo,
R.string.stripe_paymentsheet_payment_method_afterpay_clearpay,
colorFilter = if (MaterialTheme.colors.surface.shouldUseDarkDynamicColor()) {
null
} else {
ColorFilter.tint(Color.White)
}
)
),
modifier = Modifier.padding(4.dp, 8.dp, 4.dp, 4.dp),
color = MaterialTheme.paymentsColors.subtitle,
style = MaterialTheme.typography.h6,
urlSpanStyle = SpanStyle(),
imageAlign = PlaceholderVerticalAlign.Bottom
)
}

@Composable
michelleb-stripe marked this conversation as resolved.
Show resolved Hide resolved
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun AfterpayClearpayElementUIOriginal(
enabled: Boolean,
element: AfterpayClearpayHeaderElement
) {
val context = LocalContext.current

Expand All @@ -39,10 +85,11 @@ fun AfterpayClearpayElementUI(
crossAxisAlignment = FlowCrossAxisAlignment.Center
) {
Text(
element.getLabel(context.resources),
element.getLabelOriginal(context.resources),
Modifier
.padding(end = 4.dp),
color = MaterialTheme.paymentsColors.subtitle
color = MaterialTheme.paymentsColors.subtitle,
style = MaterialTheme.typography.h6,
)
Image(
painter = painterResource(R.drawable.stripe_ic_afterpay_clearpay_logo),
Expand Down
Expand Up @@ -21,16 +21,51 @@ data class AfterpayClearpayHeaderElement(

val infoUrl = url.format(Locale.current.region.lowercase())

fun getLabel(resources: Resources) =
resources.getString(
R.string.afterpay_clearpay_message,
CurrencyFormatter.format(
amount.value / 4,
amount.currencyCode

fun getLabelOriginal(resources: Resources): String {
val numInstallments = when (amount.currencyCode.lowercase()) {
"eur" -> 3
else -> 4
}

return resources.getString(
R.string.afterpay_clearpay_message
).replace("<num_installments/>", numInstallments.toString())
.replace(
"<installment_price/>", CurrencyFormatter.format(
amount.value / numInstallments,
amount.currencyCode
)
)
.replace(
"<img/>",
""
)
}

fun getLabel(resources: Resources): String {
val numInstallments = when (amount.currencyCode.lowercase()) {
"eur" -> 3
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we have enums for currency?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, currently we don't. There is a java.util.Currency class that we could use to get strongly typed currencies. Right now I can see it being good to strongly type the currency in the Amount.kt, CurrencyFormatter. Not sure there is a lot of value in adding it for the older classes.

Given the size of this type of change I would recommend pulling it out as a tech debt item.

else -> 4
}
return resources.getString(
R.string.afterpay_clearpay_message
).replace("<num_installments/>", numInstallments.toString())
.replace(
"<installment_price/>", CurrencyFormatter.format(
amount.value / numInstallments,
amount.currencyCode
)
)
)
// The no break space will keep the afterpay logo and (i) on the same line.
.replace(
"<img/>",
"<img/>$NO_BREAK_SPACE<a href=\"$infoUrl\"><b>ⓘ</b></a>"
)
}

companion object {
const val url = "https://static-us.afterpay.com/javascript/modal/%s_rebrand_modal.html"
const val NO_BREAK_SPACE = "\u00A0"
}
}
Expand Up @@ -48,7 +48,8 @@ private const val LINK_TAG = "URL"
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
data class EmbeddableImage(
@DrawableRes val id: Int,
@StringRes val contentDescription: Int
@StringRes val contentDescription: Int,
val colorFilter: androidx.compose.ui.graphics.ColorFilter? = null
)

/**
Expand All @@ -64,7 +65,9 @@ fun Html(
color: Color,
style: TextStyle,
modifier: Modifier = Modifier,
urlSpanStyle: SpanStyle = SpanStyle(textDecoration = TextDecoration.Underline)
enabled: Boolean = true,
urlSpanStyle: SpanStyle = SpanStyle(textDecoration = TextDecoration.Underline),
imageAlign: PlaceholderVerticalAlign = PlaceholderVerticalAlign.AboveBaseline
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The afterpay image is bottom aligned, whereas the affirm one is "aboveBaseline"

) {
val inlineContentMap = imageGetter.entries.associate { (key, value) ->
val painter = painterResource(value.id)
Expand All @@ -76,7 +79,7 @@ fun Html(
Placeholder(
newWidth,
MaterialTheme.typography.body1.fontSize,
PlaceholderVerticalAlign.AboveBaseline
imageAlign
),
children = {
Image(
Expand All @@ -86,7 +89,8 @@ fun Html(
painter = painter,
contentDescription = stringResource(
value.contentDescription
)
),
colorFilter = value.colorFilter
)
}
)
Expand All @@ -98,18 +102,21 @@ fun Html(
ClickableText(
annotatedText,
modifier = modifier
.semantics(mergeDescendants = true) {}, // makes it a separate accessibile item,
.semantics(mergeDescendants = true) {}, // makes it a separate accessible item,
inlineContent = inlineContentMap,
color = color,
style = style,
onClick = {
annotatedText
.getStringAnnotations(LINK_TAG, it, it)
.firstOrNull()?.let { annotation ->
val openURL = Intent(Intent.ACTION_VIEW)
openURL.data = Uri.parse(annotation.item)
context.startActivity(openURL)
}
if (enabled) {
//Position is the position of the tag in the string
annotatedText
.getStringAnnotations(LINK_TAG, it, it)
.firstOrNull()?.let { annotation ->
val openURL = Intent(Intent.ACTION_VIEW)
openURL.data = Uri.parse(annotation.item)
context.startActivity(openURL)
}
}
}
)
}
Expand Down Expand Up @@ -217,8 +224,19 @@ private fun ClickableText(
val layoutResult = remember { mutableStateOf<TextLayoutResult?>(null) }
val pressIndicator = Modifier.pointerInput(onClick) {
detectTapGestures { pos ->
var nonPlaceholderPosition = pos
// If the position is in the bounds of a placeholder set the position to the end of the placeholder.
layoutResult.value?.placeholderRects?.filterNotNull()?.firstOrNull {
pos.x > it.topLeft.x && pos.x < it.topRight.x
}?.let {
nonPlaceholderPosition = it.topRight.copy(
x = it.topRight.x + 0.1f
)
}

layoutResult.value?.let { layoutResult ->
onClick(layoutResult.getOffsetForPosition(pos))
// we need to account for offset and change to an index by subtracting 1.
onClick(layoutResult.getOffsetForPosition(nonPlaceholderPosition) - 1)
}
}
}
Expand Down