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

Show dialog after completed or error purchasing #1246

Merged
merged 4 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -3,7 +3,9 @@ package com.revenuecat.paywallstester.ui.screens.paywall
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.AlertDialog
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
Expand All @@ -29,8 +31,40 @@ fun PaywallScreen(
Text(text = "Error: ${state.errorMessage}")
}
is PaywallScreenState.Loaded -> {
PaywallView(PaywallViewOptions.Builder().setOffering(state.offering).build())
PaywallView(
PaywallViewOptions.Builder()
.setOffering(state.offering)
.setListener(viewModel)
.build(),
)
state?.dialogText?.let {
PurchaseAlertDialog(viewModel, it)
}
}
}
}
}

@Composable
private fun PurchaseAlertDialog(
Copy link
Contributor

Choose a reason for hiding this comment

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

We might want to extract this to a different file... But for now, I think it's fine to keep here.

viewModel: PaywallScreenViewModel,
text: String,
) {
AlertDialog(
onDismissRequest = {
viewModel.onDialogDismissed()
},
buttons = {
TextButton(
onClick = {
viewModel.onDialogDismissed()
},
) {
Text("Ok")
}
},
text = {
Text(text)
},
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@ import com.revenuecat.purchases.Offering
sealed class PaywallScreenState {
object Loading : PaywallScreenState()
data class Error(val errorMessage: String) : PaywallScreenState()
data class Loaded(val offering: Offering) : PaywallScreenState()
data class Loaded(
val offering: Offering,
val dialogText: String? = null,
) : PaywallScreenState()
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,26 @@ import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.revenuecat.purchases.CustomerInfo
import com.revenuecat.purchases.Purchases
import com.revenuecat.purchases.PurchasesError
import com.revenuecat.purchases.PurchasesException
import com.revenuecat.purchases.awaitOfferings
import com.revenuecat.purchases.models.StoreTransaction
import com.revenuecat.purchases.ui.revenuecatui.PaywallViewListener
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

interface PaywallScreenViewModel {
interface PaywallScreenViewModel : PaywallViewListener {
companion object {
const val OFFERING_ID_KEY = "offering_id"
}
val state: StateFlow<PaywallScreenState>

fun onDialogDismissed()
}

class PaywallScreenViewModelImpl(
Expand All @@ -35,6 +41,72 @@ class PaywallScreenViewModelImpl(
updateOffering()
}

override fun onRestoreStarted() {
val value = _state.value
if (value is PaywallScreenState.Loaded) {
_state.update {
value.copy(
dialogText = "Restoring purchases...",
)
}
}
}

override fun onRestoreCompleted(customerInfo: CustomerInfo) {
val value = _state.value
if (value is PaywallScreenState.Loaded) {
_state.update {
value.copy(
dialogText = "Restore completed",
)
}
}
}

override fun onRestoreError(error: PurchasesError) {
val value = _state.value
if (value is PaywallScreenState.Loaded) {
_state.update {
value.copy(
dialogText = "There was an error restoring purchases:\n${error.message}",
)
}
}
}

override fun onPurchaseCompleted(customerInfo: CustomerInfo, storeTransaction: StoreTransaction) {
val value = _state.value
if (value is PaywallScreenState.Loaded) {
_state.update {
value.copy(
dialogText = "Purchase was successful",
)
}
}
}

override fun onPurchaseError(error: PurchasesError) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess we are not displaying the error message in the dialog right? I wonder if instead of a boolean, we should use a nullable string in the state so we can display the error message.

Copy link
Contributor

Choose a reason for hiding this comment

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

I see we already made the conversion to a nullable string in the followup PR... So then the question is whether we want to include the error in the dialog... IMO, it might be a good idea for a tester app since it would be easier to visualize errors in case they happen when downloaded from the play store.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added error message

val value = _state.value
if (value is PaywallScreenState.Loaded) {
_state.update {
value.copy(
dialogText = "There was an error purchasing:\n${error.message}",
)
}
}
}

override fun onDialogDismissed() {
val value = _state.value
if (value is PaywallScreenState.Loaded) {
_state.update {
value.copy(
dialogText = null,
)
}
}
}

private fun updateOffering() {
viewModelScope.launch {
try {
Expand Down