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

Paywalls: template 4 #1349

Merged
merged 25 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
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 @@ -297,7 +297,9 @@ object SamplePaywalls {
return PaywallData(
templateName = "4",
config = PaywallData.Configuration(
images = images,
images = PaywallData.Configuration.Images(
background = "300883_1690710097.jpg",
),
colors = PaywallData.Configuration.ColorInformation(
light = PaywallData.Configuration.Colors(
background = PaywallColor("#FFFFFF"),
Expand All @@ -313,9 +315,9 @@ object SamplePaywalls {
PackageType.MONTHLY.identifier!!,
PackageType.SIX_MONTH.identifier!!,
PackageType.ANNUAL.identifier!!,
PackageType.LIFETIME.identifier!!,
),
defaultPackage = PackageType.SIX_MONTH.identifier!!,
blurredBackgroundImage = true,
),
assetBaseURL = paywallAssetBaseURL,
localization = mapOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import com.revenuecat.purchases.ui.revenuecatui.helpers.toAndroidContext
import com.revenuecat.purchases.ui.revenuecatui.templates.Template1
import com.revenuecat.purchases.ui.revenuecatui.templates.Template2
import com.revenuecat.purchases.ui.revenuecatui.templates.Template3
import com.revenuecat.purchases.ui.revenuecatui.templates.Template4

@Composable
internal fun InternalPaywall(
Expand Down Expand Up @@ -80,8 +81,8 @@ private fun LoadedPaywall(state: PaywallState.Loaded, viewModel: PaywallViewMode
Modifier
.clip(
RoundedCornerShape(
topStart = UIConstant.footerRoundedBorderHeight,
topEnd = UIConstant.footerRoundedBorderHeight,
topStart = UIConstant.defaultCornerRadius,
topEnd = UIConstant.defaultCornerRadius,
),
)
.background(backgroundColor)
Expand All @@ -97,7 +98,7 @@ private fun TemplatePaywall(state: PaywallState.Loaded, viewModel: PaywallViewMo
PaywallTemplate.TEMPLATE_1 -> Template1(state = state, viewModel = viewModel)
PaywallTemplate.TEMPLATE_2 -> Template2(state = state, viewModel = viewModel)
PaywallTemplate.TEMPLATE_3 -> Template3(state = state, viewModel = viewModel)
PaywallTemplate.TEMPLATE_4 -> Text(text = "Error: Template 4 not supported")
PaywallTemplate.TEMPLATE_4 -> Template4(state = state, viewModel = viewModel)
PaywallTemplate.TEMPLATE_5 -> Text(text = "Error: Template 5 not supported")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ fun PaywallFooter(
modifier = Modifier.fillMaxSize(),
// This is a workaround to make the main content be able to go below the footer, so it's visible through
// the rounded corners. We pass this padding back to the developer so they can add this padding to their content
verticalArrangement = Arrangement.spacedBy(-UIConstant.footerRoundedBorderHeight),
verticalArrangement = Arrangement.spacedBy(-UIConstant.defaultCornerRadius),
) {
Box(
modifier = Modifier
.fillMaxWidth()
.weight(1f),
) {
mainContent(PaddingValues(bottom = UIConstant.footerRoundedBorderHeight))
mainContent(PaddingValues(bottom = UIConstant.defaultCornerRadius))
}
options.mode = PaywallMode.footerMode(condensed)
Paywall(options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ internal object UIConstant {
val defaultHorizontalPadding = 12.dp
val defaultVerticalSpacing = 12.dp

val footerRoundedBorderHeight = 20.dp

val defaultCornerRadius = 20.dp
vegaro marked this conversation as resolved.
Show resolved Hide resolved
val defaultPackageCornerRadius = 16.dp
val defaultPackageBorderWidth = 1.5.dp

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,21 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import com.revenuecat.purchases.ui.revenuecatui.UIConstant
import com.revenuecat.purchases.ui.revenuecatui.data.PaywallState
import com.revenuecat.purchases.ui.revenuecatui.data.processed.TemplateConfiguration

@Composable
internal fun ConsistentPackageContentView(
state: PaywallState.Loaded,
creator: @Composable (TemplateConfiguration.PackageInfo) -> Unit,
) {
ConsistentPackageContentView(
packages = state.templateConfiguration.packages.all,
selected = state.selectedPackage.value,
creator = creator,
)
}

/**
* A wrapper composable that can display content based on a selected package
* and maintain a consistent layout when that selected package changes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ package com.revenuecat.purchases.ui.revenuecatui.composables

import BlurTransformation
import android.os.Build
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.BlurredEdgeTreatment
import androidx.compose.ui.draw.blur
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
Expand All @@ -17,6 +20,7 @@ import com.revenuecat.purchases.ui.revenuecatui.R
import com.revenuecat.purchases.ui.revenuecatui.data.processed.TemplateConfiguration
import com.revenuecat.purchases.ui.revenuecatui.extensions.conditional
import com.revenuecat.purchases.ui.revenuecatui.extensions.defaultBackgroundPlaceholder
import com.revenuecat.purchases.ui.revenuecatui.helpers.isInPreviewMode

// Current implementation uses a transformation on API level < 31, modifier on > 31.
@Composable
Expand Down Expand Up @@ -49,13 +53,19 @@ internal fun BoxScope.PaywallBackground(templateConfiguration: TemplateConfigura
alpha = imageAlpha,
)
} else if (templateConfiguration.images.backgroundUri != null) {
RemoteImage(
urlString = templateConfiguration.images.backgroundUri.toString(),
modifier = modifier,
contentScale = BackgroundUIConstants.contentScale,
transformation = backwardsCompatibleTransformation,
alpha = imageAlpha,
)
if (isInPreviewMode()) {
Box(
modifier = modifier.background(Color.Gray),
)
} else {
RemoteImage(
urlString = templateConfiguration.images.backgroundUri.toString(),
modifier = modifier,
contentScale = BackgroundUIConstants.contentScale,
transformation = backwardsCompatibleTransformation,
alpha = imageAlpha,
)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ internal object PackageConfigurationFactory {
private fun productDiscount(pricePerMonth: Price?, mostExpensive: Price?): Double? {
return pricePerMonth?.amountMicros?.let { price ->
mostExpensive?.amountMicros?.let { expensive ->
return if (price >= expensive) {
if (price >= expensive) {
vegaro marked this conversation as resolved.
Show resolved Hide resolved
null
} else {
(expensive - price).toDouble() / expensive.toDouble()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ internal enum class PaywallTemplate(val id: String, val configurationType: Packa
TEMPLATE_1("1", PackageConfigurationType.SINGLE),
TEMPLATE_2("2", PackageConfigurationType.MULTIPLE),
TEMPLATE_3("3", PackageConfigurationType.SINGLE),
TEMPLATE_4("4_disabled", PackageConfigurationType.MULTIPLE),
TEMPLATE_4("4", PackageConfigurationType.MULTIPLE),
TEMPLATE_5("5_disabled", PackageConfigurationType.MULTIPLE),
;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.revenuecat.purchases.ui.revenuecatui.data.processed.VariableDataProvi
import com.revenuecat.purchases.ui.revenuecatui.data.testdata.templates.template1
import com.revenuecat.purchases.ui.revenuecatui.data.testdata.templates.template2
import com.revenuecat.purchases.ui.revenuecatui.data.testdata.templates.template3
import com.revenuecat.purchases.ui.revenuecatui.data.testdata.templates.template4
import com.revenuecat.purchases.ui.revenuecatui.helpers.ApplicationContext
import com.revenuecat.purchases.ui.revenuecatui.helpers.toPaywallState
import kotlinx.coroutines.delay
Expand Down Expand Up @@ -131,6 +132,19 @@ internal object TestData {
serverDescription = "",
)

val template4Offering = Offering(
identifier = "Template4",
availablePackages = listOf(
Packages.monthly,
Packages.semester,
Packages.annual,
Packages.weekly,
),
metadata = mapOf(),
paywall = template4,
serverDescription = "",
)

object Packages {
val weekly = Package(
packageType = PackageType.WEEKLY,
Expand Down Expand Up @@ -230,7 +244,7 @@ internal class MockApplicationContext : ApplicationContext {

// This is hardcoding the english version of the strings for now. We can't access the actual resources since
// we don't have access to a real context in some cases here.
override fun getString(resId: Int): String {
override fun getString(resId: Int, vararg formatArgs: Any): String {
return when (resId) {
R.string.restore_purchases -> "Restore purchases"
R.string.annual -> "Annual"
Expand All @@ -244,6 +258,7 @@ internal class MockApplicationContext : ApplicationContext {
R.string.default_offer_details_with_intro_offer ->
"Start your {{ sub_offer_duration }} trial, " +
"then {{ total_price_and_per_month }}."
R.string.package_discount -> "${formatArgs[0]}% off"
else -> error("Unknown string resource $resId")
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.revenuecat.purchases.ui.revenuecatui.data.testdata.templates

import com.revenuecat.purchases.PackageType
import com.revenuecat.purchases.paywalls.PaywallColor
import com.revenuecat.purchases.paywalls.PaywallData
import com.revenuecat.purchases.ui.revenuecatui.data.processed.PaywallTemplate
import com.revenuecat.purchases.ui.revenuecatui.data.testdata.TestData
import java.net.URL

internal val TestData.template4: PaywallData
get() = PaywallData(
templateName = PaywallTemplate.TEMPLATE_4.id,
config = PaywallData.Configuration(
packageIds = listOf(
PackageType.MONTHLY.identifier!!,
PackageType.SIX_MONTH.identifier!!,
PackageType.ANNUAL.identifier!!,
PackageType.WEEKLY.identifier!!,
),
defaultPackage = PackageType.SIX_MONTH.identifier!!,
images = PaywallData.Configuration.Images(
background = "300883_1690710097.jpg",
),
displayRestorePurchases = true,
termsOfServiceURL = URL("https://revenuecat.com/tos"),
privacyURL = URL("https://revenuecat.com/privacy"),
colors = PaywallData.Configuration.ColorInformation(
light = PaywallData.Configuration.Colors(
background = PaywallColor(stringRepresentation = "#FFFFFF"),
text1 = PaywallColor(stringRepresentation = "#111111"),
callToActionBackground = PaywallColor(stringRepresentation = "#06357D"),
callToActionForeground = PaywallColor(stringRepresentation = "#FFFFFF"),
accent1 = PaywallColor(stringRepresentation = "#D4B5FC"),
accent2 = PaywallColor(stringRepresentation = "#DFDFDF"),
),
),
),
localization = mapOf(
"en_US" to PaywallData.LocalizedConfiguration(
title = "Get _unlimited_ access",
callToAction = "Continue",
offerDetails = "Cancel anytime",
offerDetailsWithIntroOffer = "Includes {{ sub_offer_duration }} **free** trial",
offerName = "{{ sub_duration_in_months }}",
),
),
assetBaseURL = TestData.Constants.assetBaseURL,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.revenuecat.purchases.ui.revenuecatui.extensions

import com.revenuecat.purchases.ui.revenuecatui.R
import com.revenuecat.purchases.ui.revenuecatui.data.processed.TemplateConfiguration
import com.revenuecat.purchases.ui.revenuecatui.helpers.ApplicationContext
import kotlin.math.roundToInt

@SuppressWarnings("MagicNumber")
internal fun TemplateConfiguration.PackageInfo.localizedDiscount(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can you add a test for this?
Also I wonder if this would be easier to find inside of VariableDataProvider

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes... Maybe would you add it as a extension there? The thing is that there's really no variable in this, that's why I didn't put it there. But I will move it as an extension for now

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 thing is that there's really no variable in this,

This will be used for {{ sub_relative_discount }}.

applicationContext: ApplicationContext,
): String {
return (discountRelativeToMostExpensivePerMonth?.times(100.0))?.roundToInt()?.let {
applicationContext.getString(R.string.package_discount, it)
} ?: ""
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import java.util.Locale
*/
internal interface ApplicationContext {
fun getApplicationName(): String
fun getString(@StringRes resId: Int): String
fun getString(@StringRes resId: Int, vararg formatArgs: Any): String
fun getLocale(): Locale
}

Expand All @@ -18,8 +18,8 @@ internal class AndroidApplicationContext(private val applicationContext: Context
return applicationContext.applicationInfo.loadLabel(applicationContext.packageManager).toString()
}

override fun getString(@StringRes resId: Int): String {
return applicationContext.resources.getString(resId)
override fun getString(@StringRes resId: Int, vararg formatArgs: Any): String {
return applicationContext.resources.getString(resId, *formatArgs)
vegaro marked this conversation as resolved.
Show resolved Hide resolved
}

override fun getLocale(): Locale {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.revenuecat.purchases.ui.revenuecatui.helpers

import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.isUnspecified

@SuppressWarnings("LongParameterList")
@Composable
fun AutoResizedText(
Copy link
Contributor

Choose a reason for hiding this comment

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

There's a bit of delay when showing this takes a bit to load when there's rezising needed, so we might want to improve it in the future, maybe add an animation. I think it works for now and didn't want to spend much more time in this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

😬 Compose doesn't provide this? Yikes.
We should probably avoid using this for the most part because it's not accessibility friendly at all. This is the only place where I'm using it in iOS.

For now this solution is fine I think 👍🏻

A couple of suggested changes:

  • Move this to composables
  • Add a docstring to explain what this does

text: String,
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
style: TextStyle = MaterialTheme.typography.bodySmall,
fontWeight: FontWeight = FontWeight.Bold,
textAlign: TextAlign = TextAlign.Center,
) {
var resizedTextStyle by remember {
mutableStateOf(style)
}
var shouldDraw by remember {
mutableStateOf(false)
}

val defaultFontSize = style.fontSize
Text(
text = text,
fontWeight = fontWeight,
textAlign = textAlign,
color = color,
modifier = modifier
.drawWithContent {
if (shouldDraw) {
drawContent()
}
},
softWrap = false,
style = resizedTextStyle,
onTextLayout = { result ->
if (result.didOverflowWidth) {
if (style.fontSize.isUnspecified) {
resizedTextStyle = resizedTextStyle.copy(
fontSize = defaultFontSize,
)
}
val newFontSize = resizedTextStyle.fontSize * 0.95
resizedTextStyle = resizedTextStyle.copy(
fontSize = newFontSize,
)
} else {
shouldDraw = true
}
},
)
}
Loading