diff --git a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/customercenter/InternalCustomerCenter.kt b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/customercenter/InternalCustomerCenter.kt index 8ef0e6caa6..707a2235fd 100644 --- a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/customercenter/InternalCustomerCenter.kt +++ b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/customercenter/InternalCustomerCenter.kt @@ -225,6 +225,7 @@ private fun MainScreen( configuration.getManagementScreen()?.let { managementScreen -> ManageSubscriptionsView( screen = managementScreen, + localization = configuration.localization, purchaseInformation = state.purchaseInformation, onPathButtonPress = { path -> onAction(CustomerCenterAction.PathButtonPressed(path)) @@ -238,6 +239,7 @@ private fun MainScreen( configuration.getNoActiveScreen()?.let { noActiveScreen -> ManageSubscriptionsView( screen = noActiveScreen, + localization = configuration.localization, onPathButtonPress = { path -> onAction(CustomerCenterAction.PathButtonPressed(path)) }, diff --git a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/customercenter/SubscriptionDetailsView.kt b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/customercenter/SubscriptionDetailsView.kt index 8957b121f8..c5bbf48a9e 100644 --- a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/customercenter/SubscriptionDetailsView.kt +++ b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/customercenter/SubscriptionDetailsView.kt @@ -141,8 +141,8 @@ private fun getSubscriptionExplanation( localization: CustomerCenterConfigData.Localization, ): String { return when (purchaseInformation.explanation) { + Explanation.APPLE -> localization.commonLocalizedString(CommonLocalizedString.APPLE_SUBSCRIPTION_MANAGE) Explanation.PROMOTIONAL -> localization.commonLocalizedString(CommonLocalizedString.YOU_HAVE_PROMO) - Explanation.GOOGLE -> localization.commonLocalizedString(CommonLocalizedString.GOOGLE_SUBSCRIPTION_MANAGE) Explanation.WEB -> localization.commonLocalizedString(CommonLocalizedString.WEB_SUBSCRIPTION_MANAGE) Explanation.OTHER_STORE_PURCHASE -> localization.commonLocalizedString(CommonLocalizedString.PLEASE_CONTACT_SUPPORT) diff --git a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/customercenter/data/PurchaseInformation.kt b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/customercenter/data/PurchaseInformation.kt index a01b5ce8b4..5f92074378 100644 --- a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/customercenter/data/PurchaseInformation.kt +++ b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/customercenter/data/PurchaseInformation.kt @@ -9,6 +9,7 @@ import com.revenuecat.purchases.ui.revenuecatui.utils.DateFormatter import com.revenuecat.purchases.ui.revenuecatui.utils.DefaultDateFormatter import java.util.Locale +@SuppressWarnings("LongParameterList") internal class PurchaseInformation( val title: String, val durationTitle: String, @@ -48,7 +49,11 @@ internal class PurchaseInformation( transaction.expiresDate?.let { date -> val dateString = dateFormatter.format(date, locale) val label = if (transaction.isActive) { - if (transaction.willRenew) ExpirationOrRenewal.Label.NEXT_BILLING_DATE else ExpirationOrRenewal.Label.EXPIRES + if (transaction.willRenew) { + ExpirationOrRenewal.Label.NEXT_BILLING_DATE + } else { + ExpirationOrRenewal.Label.EXPIRES + } } else { ExpirationOrRenewal.Label.EXPIRED } @@ -69,30 +74,19 @@ internal class PurchaseInformation( } private fun EntitlementInfo.priceBestEffort(subscribedProduct: StoreProduct?): PriceDetails { - subscribedProduct?.let { - return PriceDetails.Paid(it.price.formatted) - } - if (store == Store.PROMOTIONAL) { - return PriceDetails.Free + return subscribedProduct?.let { + PriceDetails.Paid(it.price.formatted) + } ?: if (store == Store.PROMOTIONAL) { + PriceDetails.Free + } else { + PriceDetails.Unknown } - return PriceDetails.Unknown } private fun EntitlementInfo.explanation(): Explanation { return when (store) { - Store.APP_STORE, Store.MAC_APP_STORE -> { - if (expirationDate != null) { - if (isActive) { - if (willRenew) Explanation.EARLIEST_RENEWAL else Explanation.EARLIEST_EXPIRATION - } else { - Explanation.EXPIRED - } - } else { - Explanation.LIFETIME - } - } - - Store.PLAY_STORE -> Explanation.GOOGLE + Store.APP_STORE, Store.MAC_APP_STORE -> Explanation.APPLE + Store.PLAY_STORE -> explanationForPlayStore() Store.STRIPE, Store.RC_BILLING -> Explanation.WEB Store.PROMOTIONAL -> Explanation.PROMOTIONAL Store.EXTERNAL, Store.UNKNOWN_STORE -> Explanation.OTHER_STORE_PURCHASE @@ -100,6 +94,14 @@ private fun EntitlementInfo.explanation(): Explanation { } } +private fun EntitlementInfo.explanationForPlayStore(): Explanation { + return when { + expirationDate == null -> Explanation.LIFETIME + isActive -> if (willRenew) Explanation.EARLIEST_RENEWAL else Explanation.EARLIEST_EXPIRATION + else -> Explanation.EXPIRED + } +} + private fun EntitlementInfo.expirationOrRenewal(dateFormatter: DateFormatter, locale: Locale): ExpirationOrRenewal? { val date = expirationDateBestEffort(dateFormatter, locale) val label = if (isActive) { @@ -114,13 +116,13 @@ private fun EntitlementInfo.expirationDateBestEffort( dateFormatter: DateFormatter, locale: Locale, ): ExpirationOrRenewal.Date { - expirationDate?.let { expirationDate -> + return expirationDate?.let { expirationDate -> if (store == Store.PROMOTIONAL && productIdentifier.isPromotionalLifetime(store)) { - return ExpirationOrRenewal.Date.Never + ExpirationOrRenewal.Date.Never } else { - return ExpirationOrRenewal.Date.DateString(dateFormatter.format(expirationDate, locale)) + ExpirationOrRenewal.Date.DateString(dateFormatter.format(expirationDate, locale)) } - } ?: return ExpirationOrRenewal.Date.Never + } ?: ExpirationOrRenewal.Date.Never } private fun String.isPromotionalLifetime(store: Store): Boolean { @@ -150,8 +152,8 @@ internal sealed class PriceDetails { } internal enum class Explanation { + APPLE, PROMOTIONAL, - GOOGLE, WEB, OTHER_STORE_PURCHASE, AMAZON, diff --git a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/customercenter/viewmodel/CustomerCenterViewModel.kt b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/customercenter/viewmodel/CustomerCenterViewModel.kt index ada89ae17c..2533f9815c 100644 --- a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/customercenter/viewmodel/CustomerCenterViewModel.kt +++ b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/customercenter/viewmodel/CustomerCenterViewModel.kt @@ -196,21 +196,20 @@ internal class CustomerCenterViewModelImpl( val hasActiveProducts = customerInfo.activeSubscriptions.isNotEmpty() || customerInfo.nonSubscriptionTransactions.isNotEmpty() - if (!hasActiveProducts) { - return null - } + if (hasActiveProducts) { + val activeTransactionDetails = findActiveTransaction(customerInfo) - val activeTransactionDetails = findActiveTransaction(customerInfo) + if (activeTransactionDetails != null) { + val entitlement = customerInfo.entitlements.all.values + .firstOrNull { it.productIdentifier == activeTransactionDetails.productIdentifier } - if (activeTransactionDetails == null) { - Logger.w("Could not find subscription information") - return null + return createPurchaseInformation(activeTransactionDetails, entitlement, dateFormatter, locale) + } else { + Logger.w("Could not find subscription information") + } } - val entitlement = customerInfo.entitlements.all.values - .firstOrNull { it.productIdentifier == activeTransactionDetails.productIdentifier } - - return createPurchaseInformation(activeTransactionDetails, entitlement, dateFormatter, locale) + return null } private fun findActiveTransaction(customerInfo: CustomerInfo): TransactionDetails? { @@ -252,31 +251,22 @@ internal class CustomerCenterViewModelImpl( dateFormatter: DateFormatter, locale: Locale, ): PurchaseInformation { - if (transaction.store == Store.PLAY_STORE) { - val product = purchases.awaitGetProduct(transaction.productIdentifier).firstOrNull() - if (product != null) { - return PurchaseInformation( - entitlementInfo = entitlement, - subscribedProduct = product, - transaction = transaction, - dateFormatter = dateFormatter, - locale = locale, - ) - } else { - Logger.w( - "Could not find product, loading without product information: ${transaction.productIdentifier}", - ) - return PurchaseInformation( - entitlementInfo = entitlement, - transaction = transaction, - dateFormatter = dateFormatter, - locale = locale, - ) + val product = if (transaction.store == Store.PLAY_STORE) { + purchases.awaitGetProduct(transaction.productIdentifier).firstOrNull().also { + if (it == null) { + Logger.w( + "Could not find product, loading without product information: ${transaction.productIdentifier}", + ) + } } + } else { + Logger.w("Active product is not from Google, loading without product information: ${transaction.store}") + null } - Logger.w("Active product is not from Google, loading without product information: ${transaction.store}") + return PurchaseInformation( entitlementInfo = entitlement, + subscribedProduct = product, transaction = transaction, dateFormatter = dateFormatter, locale = locale, diff --git a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/customercenter/views/ManageSubscriptionsView.kt b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/customercenter/views/ManageSubscriptionsView.kt index 57635e151e..dc9423e68a 100644 --- a/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/customercenter/views/ManageSubscriptionsView.kt +++ b/ui/revenuecatui/src/main/kotlin/com/revenuecat/purchases/ui/revenuecatui/customercenter/views/ManageSubscriptionsView.kt @@ -33,6 +33,7 @@ import com.revenuecat.purchases.ui.revenuecatui.customercenter.data.PurchaseInfo @Composable internal fun ManageSubscriptionsView( screen: CustomerCenterConfigData.Screen, + localization: CustomerCenterConfigData.Localization, modifier: Modifier = Modifier, purchaseInformation: PurchaseInformation? = null, onPathButtonPress: (CustomerCenterConfigData.HelpPath) -> Unit, @@ -50,6 +51,7 @@ internal fun ManageSubscriptionsView( purchaseInformation?.let { purchaseInformation -> ActiveUserManagementView( screen = screen, + localization = localization, purchaseInformation = purchaseInformation, onDetermineFlow = onPathButtonPress, ) @@ -65,6 +67,7 @@ internal fun ManageSubscriptionsView( @Composable private fun ActiveUserManagementView( screen: CustomerCenterConfigData.Screen, + localization: CustomerCenterConfigData.Localization, purchaseInformation: PurchaseInformation, onDetermineFlow: (CustomerCenterConfigData.HelpPath) -> Unit, ) { @@ -94,7 +97,7 @@ private fun ActiveUserManagementView( defaultElevation = 4.dp, ), ) { - SubscriptionDetailsView(details = purchaseInformation) + SubscriptionDetailsView(details = purchaseInformation, localization = localization) } } @@ -172,6 +175,7 @@ private fun ManageSubscriptionsViewPreview() { val managementScreen = testData.screens[CustomerCenterConfigData.Screen.ScreenType.MANAGEMENT]!! ManageSubscriptionsView( screen = managementScreen, + localization = testData.localization, purchaseInformation = CustomerCenterConfigTestData.purchaseInformationMonthlyRenewing, onPathButtonPress = {}, ) @@ -186,6 +190,7 @@ private fun NoActiveSubscriptionsViewPreview() { ManageSubscriptionsView( screen = noActiveScreen, + localization = testData.localization, purchaseInformation = null, onPathButtonPress = {}, ) diff --git a/ui/revenuecatui/src/test/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/PaywallViewModelTest.kt b/ui/revenuecatui/src/test/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/PaywallViewModelTest.kt index d492cd8527..bd2cb27b2a 100644 --- a/ui/revenuecatui/src/test/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/PaywallViewModelTest.kt +++ b/ui/revenuecatui/src/test/kotlin/com/revenuecat/purchases/ui/revenuecatui/data/PaywallViewModelTest.kt @@ -16,6 +16,7 @@ import com.revenuecat.purchases.PurchasesAreCompletedBy import com.revenuecat.purchases.PurchasesError import com.revenuecat.purchases.PurchasesErrorCode import com.revenuecat.purchases.PurchasesException +import com.revenuecat.purchases.Store import com.revenuecat.purchases.models.StoreTransaction import com.revenuecat.purchases.models.Transaction import com.revenuecat.purchases.paywalls.PaywallData @@ -1158,7 +1159,9 @@ class PaywallViewModelTest { UUID.randomUUID().toString(), it, it, - Date() + Date(), + UUID.randomUUID().toString(), + Store.PLAY_STORE, ) } }