Skip to content

Commit

Permalink
Add error handler to buying subscription
Browse files Browse the repository at this point in the history
  • Loading branch information
AleksandarIlic committed Nov 8, 2024
1 parent 560b9be commit fc3fc82
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package net.primal.android.premium.buying

import android.app.Activity
import net.primal.android.core.compose.profile.model.ProfileDetailsUi
import net.primal.android.premium.domain.MembershipError
import net.primal.android.wallet.store.domain.SubscriptionProduct

interface PremiumBuyingContract {
Expand All @@ -15,6 +16,8 @@ interface PremiumBuyingContract {
val profile: ProfileDetailsUi? = null,
val promoCodeValidity: Boolean? = null,
val isCheckingPromoCodeValidity: Boolean = false,

val error: MembershipError? = null,
)

sealed class UiEvent {
Expand All @@ -26,6 +29,8 @@ interface PremiumBuyingContract {

data object RestoreSubscription : UiEvent()

data object DismissError : UiEvent()

data class RequestPurchase(
val activity: Activity,
val subscriptionProduct: SubscriptionProduct,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,25 @@ import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import net.primal.android.R
import net.primal.android.core.compose.SnackbarErrorHandler
import net.primal.android.premium.buying.home.PremiumBuyingHomeStage
import net.primal.android.premium.buying.name.PremiumPrimalNameStage
import net.primal.android.premium.buying.purchase.PremiumPurchaseStage
import net.primal.android.premium.buying.success.PremiumBuyingSuccessStage
import net.primal.android.premium.ui.toHumanReadableString
import net.primal.android.theme.AppTheme

@Composable
Expand Down Expand Up @@ -50,77 +58,92 @@ private fun PremiumBuyingScreen(
eventPublisher = eventPublisher,
)

AnimatedContent(
modifier = Modifier
.background(AppTheme.colorScheme.surfaceVariant)
.fillMaxSize(),
label = "PremiumStages",
targetState = state.stage,
transitionSpec = { transitionSpecBetweenStages() },
) { stage ->
when (stage) {
PremiumBuyingContract.PremiumStage.Home -> {
PremiumBuyingHomeStage(
loading = state.loading,
subscriptions = state.subscriptions,
onClose = onClose,
onLearnMoreClick = onMoreInfoClick,
onFindPrimalName = {
eventPublisher(
PremiumBuyingContract.UiEvent.MoveToPremiumStage(
PremiumBuyingContract.PremiumStage.FindPrimalName,
),
)
},
)
}
val snackbarHostState = remember { SnackbarHostState() }
SnackbarErrorHandler(
error = state.error,
snackbarHostState = snackbarHostState,
errorMessageResolver = { it.toHumanReadableString() },
onErrorDismiss = { eventPublisher(PremiumBuyingContract.UiEvent.DismissError) },
)

PremiumBuyingContract.PremiumStage.FindPrimalName -> {
PremiumPrimalNameStage(
titleText = stringResource(id = R.string.premium_primal_name_title),
initialName = state.primalName,
onBack = {
eventPublisher(
PremiumBuyingContract.UiEvent.MoveToPremiumStage(
PremiumBuyingContract.PremiumStage.Home,
),
)
},
onPrimalNameAvailable = {
eventPublisher(
PremiumBuyingContract.UiEvent.SetPrimalName(primalName = it),
)
eventPublisher(
PremiumBuyingContract.UiEvent.MoveToPremiumStage(
PremiumBuyingContract.PremiumStage.Purchase,
),
)
},
)
}
Box(contentAlignment = Alignment.BottomCenter) {
AnimatedContent(
modifier = Modifier
.background(AppTheme.colorScheme.surfaceVariant)
.fillMaxSize(),
label = "BuyingPremiumStages",
targetState = state.stage,
transitionSpec = { transitionSpecBetweenStages() },
) { stage ->
when (stage) {
PremiumBuyingContract.PremiumStage.Home -> {
PremiumBuyingHomeStage(
loading = state.loading,
subscriptions = state.subscriptions,
onClose = onClose,
onLearnMoreClick = onMoreInfoClick,
onFindPrimalName = {
eventPublisher(
PremiumBuyingContract.UiEvent.MoveToPremiumStage(
PremiumBuyingContract.PremiumStage.FindPrimalName,
),
)
},
)
}

PremiumBuyingContract.PremiumStage.Purchase -> {
PremiumPurchaseStage(
state = state,
eventPublisher = eventPublisher,
onBack = {
eventPublisher(
PremiumBuyingContract.UiEvent.MoveToPremiumStage(
PremiumBuyingContract.PremiumStage.FindPrimalName,
),
)
},
onLearnMoreClick = onMoreInfoClick,
)
}
PremiumBuyingContract.PremiumStage.FindPrimalName -> {
PremiumPrimalNameStage(
titleText = stringResource(id = R.string.premium_primal_name_title),
initialName = state.primalName,
onBack = {
eventPublisher(
PremiumBuyingContract.UiEvent.MoveToPremiumStage(
PremiumBuyingContract.PremiumStage.Home,
),
)
},
onPrimalNameAvailable = {
eventPublisher(
PremiumBuyingContract.UiEvent.SetPrimalName(primalName = it),
)
eventPublisher(
PremiumBuyingContract.UiEvent.MoveToPremiumStage(
PremiumBuyingContract.PremiumStage.Purchase,
),
)
},
)
}

PremiumBuyingContract.PremiumStage.Success -> {
PremiumBuyingSuccessStage(
modifier = Modifier.fillMaxSize(),
onDoneClick = onClose,
)
PremiumBuyingContract.PremiumStage.Purchase -> {
PremiumPurchaseStage(
state = state,
eventPublisher = eventPublisher,
onBack = {
eventPublisher(
PremiumBuyingContract.UiEvent.MoveToPremiumStage(
PremiumBuyingContract.PremiumStage.FindPrimalName,
),
)
},
onLearnMoreClick = onMoreInfoClick,
)
}

PremiumBuyingContract.PremiumStage.Success -> {
PremiumBuyingSuccessStage(
modifier = Modifier.fillMaxSize(),
onDoneClick = onClose,
)
}
}
}

SnackbarHost(
modifier = Modifier.navigationBarsPadding(),
hostState = snackbarHostState,
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import net.primal.android.core.utils.isGoogleBuild
import net.primal.android.networking.sockets.errors.WssException
import net.primal.android.premium.buying.PremiumBuyingContract.UiEvent
import net.primal.android.premium.buying.PremiumBuyingContract.UiState
import net.primal.android.premium.domain.MembershipError
import net.primal.android.premium.repository.PremiumRepository
import net.primal.android.profile.repository.ProfileRepository
import net.primal.android.user.accounts.active.ActiveAccountStore
Expand Down Expand Up @@ -80,6 +81,7 @@ class PremiumBuyingViewModel @Inject constructor(
UiEvent.ClearPromoCodeValidity -> setState { copy(promoCodeValidity = null) }
is UiEvent.RequestPurchase -> launchBillingFlow(it)
UiEvent.RestoreSubscription -> restorePurchase()
UiEvent.DismissError -> setState { copy(error = null) }
}
}
}
Expand Down Expand Up @@ -110,7 +112,12 @@ class PremiumBuyingViewModel @Inject constructor(
} catch (error: WssException) {
Timber.e(error)
this@PremiumBuyingViewModel.purchase = purchase
setState { copy(hasActiveSubscription = true) }
setState {
copy(
hasActiveSubscription = true,
error = MembershipError.FailedToProcessSubscriptionPurchase(cause = error),
)
}
}
}
}
Expand Down

0 comments on commit fc3fc82

Please sign in to comment.