diff --git a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/processed/PackageConfigurationFactory.kt b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/processed/PackageConfigurationFactory.kt index 85be12617..c9ae356a5 100644 --- a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/processed/PackageConfigurationFactory.kt +++ b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/processed/PackageConfigurationFactory.kt @@ -52,14 +52,21 @@ internal object PackageConfigurationFactory { PackageType.UNKNOWN -> false } + val discountRelativeToMostExpensivePerMonth = productDiscount( + it.product.pricePerMonth(), + mostExpensivePricePerMonth, + ) TemplateConfiguration.PackageInfo( rcPackage = it, - localization = ProcessedLocalizedConfiguration.create(variableDataProvider, localization, it, locale), - currentlySubscribed = currentlySubscribed, - discountRelativeToMostExpensivePerMonth = productDiscount( - it.product.pricePerMonth(), - mostExpensivePricePerMonth, + localization = ProcessedLocalizedConfiguration.create( + variableDataProvider, + VariableProcessor.Context(discountRelativeToMostExpensivePerMonth), + localization, + it, + locale ), + currentlySubscribed = currentlySubscribed, + discountRelativeToMostExpensivePerMonth = discountRelativeToMostExpensivePerMonth, ) } diff --git a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/processed/ProcessedLocalizedConfiguration.kt b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/processed/ProcessedLocalizedConfiguration.kt index 5e30e4d9b..d8fa33d27 100644 --- a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/processed/ProcessedLocalizedConfiguration.kt +++ b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/processed/ProcessedLocalizedConfiguration.kt @@ -19,6 +19,7 @@ internal data class ProcessedLocalizedConfiguration( companion object { fun create( variableDataProvider: VariableDataProvider, + context: VariableProcessor.Context, localizedConfiguration: PaywallData.LocalizedConfiguration, rcPackage: Package, locale: Locale, @@ -26,6 +27,7 @@ internal data class ProcessedLocalizedConfiguration( fun String.processVariables(): String { return VariableProcessor.processVariables( variableDataProvider, + context, this, rcPackage, locale, diff --git a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/processed/VariableDataProvider.kt b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/processed/VariableDataProvider.kt index b9864efec..753d47724 100644 --- a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/processed/VariableDataProvider.kt +++ b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/processed/VariableDataProvider.kt @@ -95,6 +95,11 @@ internal class VariableDataProvider( return "$pricePerPeriod ($pricePerMonth/$unit)" } + @SuppressWarnings("MagicNumber") + fun localizedRelativeDiscount(discountRelativeToMostExpensivePerMonth: Double?): String? { + return applicationContext.localizedDiscount(discountRelativeToMostExpensivePerMonth) + } + private fun getFirstIntroOfferToApply(rcPackage: Package): PricingPhase? { val option = rcPackage.product.defaultOption return option?.freePhase ?: option?.introPhase @@ -109,12 +114,18 @@ internal class VariableDataProvider( } } -@SuppressWarnings("MagicNumber") internal fun TemplateConfiguration.PackageInfo.localizedDiscount( applicationContext: ApplicationContext, +): String? { + return applicationContext.localizedDiscount(discountRelativeToMostExpensivePerMonth) +} + +@SuppressWarnings("MagicNumber") +private fun ApplicationContext.localizedDiscount( + discountRelativeToMostExpensivePerMonth: Double?, ): String? { return (discountRelativeToMostExpensivePerMonth?.times(100.0))?.roundToInt()?.let { - applicationContext.getString(R.string.package_discount, it) + getString(R.string.package_discount, it) } } diff --git a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/processed/VariableProcessor.kt b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/processed/VariableProcessor.kt index cc5d60424..0b8b86617 100644 --- a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/processed/VariableProcessor.kt +++ b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/processed/VariableProcessor.kt @@ -7,6 +7,11 @@ import java.util.Locale internal object VariableProcessor { private val REGEX = Regex("\\{\\{\\s[a-zA-Z0-9_]+\\s\\}\\}") + /** + * Information necessary for computing variables. + */ + data class Context(val discountRelativeToMostExpensivePerMonth: Double?) + /** * Returns a set of invalid variables in the String */ @@ -21,12 +26,13 @@ internal object VariableProcessor { fun processVariables( variableDataProvider: VariableDataProvider, + context: Context, originalString: String, rcPackage: Package, locale: Locale, ): String { val resultString = handleVariablesAndReplace(originalString) { variable -> - variableValue(variableDataProvider, variable, rcPackage, locale) + variableValue(variableDataProvider, context, variable, rcPackage, locale) } return resultString } @@ -49,6 +55,7 @@ internal object VariableProcessor { private fun variableValue( variableDataProvider: VariableDataProvider, + context: Context, variableNameString: String, rcPackage: Package, locale: Locale, @@ -58,7 +65,7 @@ internal object VariableProcessor { Logger.e("Unknown variable: $variableNameString") null } else { - return processVariable(variableName, variableDataProvider, rcPackage, locale) ?: run { + return processVariable(variableName, variableDataProvider, context, rcPackage, locale) ?: run { Logger.w( "Could not process value for variable '$variableNameString' for " + "package '${rcPackage.identifier}'. Please check that the product for that package matches " + @@ -72,9 +79,10 @@ internal object VariableProcessor { private fun processVariable( variableName: VariableName, variableDataProvider: VariableDataProvider, + context: Context, rcPackage: Package, locale: Locale, - ) = when (variableName) { + ): String? = when (variableName) { VariableName.APP_NAME -> variableDataProvider.applicationName VariableName.PRICE -> variableDataProvider.localizedPrice(rcPackage) VariableName.PRICE_PER_PERIOD -> variableDataProvider.localizedPricePerPeriod(rcPackage, locale) @@ -96,6 +104,9 @@ internal object VariableProcessor { VariableName.SUB_OFFER_DURATION_2 -> variableDataProvider.secondIntroductoryOfferDuration(rcPackage, locale) VariableName.SUB_OFFER_PRICE -> variableDataProvider.localizedFirstIntroductoryOfferPrice(rcPackage) VariableName.SUB_OFFER_PRICE_2 -> variableDataProvider.localizedSecondIntroductoryOfferPrice(rcPackage) + VariableName.SUB_RELATIVE_DISCOUNT -> variableDataProvider.localizedRelativeDiscount( + context.discountRelativeToMostExpensivePerMonth + ) } private enum class VariableName(val identifier: String) { @@ -112,6 +123,7 @@ internal object VariableProcessor { SUB_OFFER_DURATION_2("sub_offer_duration_2"), SUB_OFFER_PRICE("sub_offer_price"), SUB_OFFER_PRICE_2("sub_offer_price_2"), + SUB_RELATIVE_DISCOUNT("sub_relative_discount"), ; companion object { diff --git a/ui/revenuecatui/src/test/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/processed/VariableProcessorTest.kt b/ui/revenuecatui/src/test/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/processed/VariableProcessorTest.kt index 1ea3443f9..0eb53db8c 100644 --- a/ui/revenuecatui/src/test/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/processed/VariableProcessorTest.kt +++ b/ui/revenuecatui/src/test/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/processed/VariableProcessorTest.kt @@ -24,12 +24,14 @@ class VariableProcessorTest { private lateinit var applicationContext: ApplicationContext private lateinit var variableDataProvider: VariableDataProvider + private lateinit var context: VariableProcessor.Context private lateinit var rcPackage: Package @Before fun setUp() { applicationContext = MockApplicationContext() variableDataProvider = VariableDataProvider(applicationContext) + context = mockk() rcPackage = TestData.Packages.annual } @@ -354,6 +356,22 @@ class VariableProcessorTest { // endregion + // region sub_relative_discount + + @Test + fun `process variables processes sub_relative_discount with no discount`() { + every { context.discountRelativeToMostExpensivePerMonth }.returns(null) + expectVariablesResult("{{ sub_relative_discount }}", "") + } + + @Test + fun `process variables processes sub_relative_discount`() { + every { context.discountRelativeToMostExpensivePerMonth }.returns(0.1) + expectVariablesResult("{{ sub_relative_discount }}", "10% off") + } + + // endregion + // endregion Variables @Test @@ -378,7 +396,13 @@ class VariableProcessorTest { locale: Locale = usLocale, rcPackage: Package = this.rcPackage, ) { - val resultText = VariableProcessor.processVariables(variableDataProvider, originalText, rcPackage, locale) + val resultText = VariableProcessor.processVariables( + variableDataProvider, + context, + originalText, + rcPackage, + locale + ) assertThat(resultText).isEqualTo(expectedText) } }