Skip to content

Commit

Permalink
Handle subscription cancellation
Browse files Browse the repository at this point in the history
  • Loading branch information
MiSikora committed Jan 23, 2025
1 parent cb46393 commit ab96140
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ class AccountDetailsFragment : BaseFragment() {
upgradeBannerState = upgradeBannerState.collectAsState(null).value,
sectionsState = accountViewModel.sectionsState.collectAsState().value,
)

AccountDetailsPage(
state = state,
theme = theme.activeTheme,
Expand Down Expand Up @@ -149,10 +150,12 @@ class AccountDetailsFragment : BaseFragment() {
val onboardingFlow = OnboardingFlow.PatronAccountUpgrade(source)
OnboardingLauncher.openOnboardingFlow(activity, onboardingFlow)
},
onCancelSubscription = {
onCancelSubscription = { winbackParams ->
analyticsTracker.track(AnalyticsEvent.ACCOUNT_DETAILS_CANCEL_TAPPED)
if (FeatureFlag.isEnabled(Feature.WINBACK)) {
WinbackFragment().show(childFragmentManager, "subscription_windback")
WinbackFragment
.create(winbackParams)
.show(childFragmentManager, "subscription_windback")
} else {
CancelConfirmationFragment
.newInstance()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import au.com.shiftyjelly.pocketcasts.compose.preview.ThemePreviewParameterProvi
import au.com.shiftyjelly.pocketcasts.compose.theme
import au.com.shiftyjelly.pocketcasts.models.type.SubscriptionFrequency
import au.com.shiftyjelly.pocketcasts.models.type.SubscriptionTier
import au.com.shiftyjelly.pocketcasts.profile.winback.WinbackInitParams
import au.com.shiftyjelly.pocketcasts.ui.theme.Theme
import kotlin.time.Duration.Companion.days
import au.com.shiftyjelly.pocketcasts.account.viewmodel.ProfileUpgradeBannerViewModel.State as UpgradeBannerState
Expand All @@ -50,7 +51,7 @@ internal fun AccountDetailsPage(
onChangeEmail: () -> Unit,
onChangePassword: () -> Unit,
onUpgradeToPatron: () -> Unit,
onCancelSubscription: () -> Unit,
onCancelSubscription: (WinbackInitParams) -> Unit,
onChangeNewsletterSubscription: (Boolean) -> Unit,
onShowPrivacyPolicy: () -> Unit,
onShowTermsOfUse: () -> Unit,
Expand Down Expand Up @@ -222,6 +223,7 @@ private fun AccountDetailsPageStub(
sectionsState = AccountSectionsState(
isSubscribedToNewsLetter = false,
email = "[email protected]",
winbackInitParams = WinbackInitParams.Empty,
canChangeCredentials = true,
canUpgradeAccount = true,
canCancelSubscription = true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ import au.com.shiftyjelly.pocketcasts.models.to.SignInState
import au.com.shiftyjelly.pocketcasts.models.to.SubscriptionStatus
import au.com.shiftyjelly.pocketcasts.models.type.Subscription
import au.com.shiftyjelly.pocketcasts.models.type.SubscriptionMapper
import au.com.shiftyjelly.pocketcasts.models.type.SubscriptionPlatform
import au.com.shiftyjelly.pocketcasts.models.type.SubscriptionTier
import au.com.shiftyjelly.pocketcasts.preferences.Settings
import au.com.shiftyjelly.pocketcasts.profile.winback.WinbackInitParams
import au.com.shiftyjelly.pocketcasts.repositories.subscription.ProductDetailsState
import au.com.shiftyjelly.pocketcasts.repositories.subscription.PurchasesState
import au.com.shiftyjelly.pocketcasts.repositories.subscription.SubscriptionManager
import au.com.shiftyjelly.pocketcasts.repositories.sync.SyncManager
import au.com.shiftyjelly.pocketcasts.repositories.user.UserManager
Expand All @@ -34,12 +37,11 @@ import kotlinx.coroutines.withContext
import timber.log.Timber

@HiltViewModel
class AccountDetailsViewModel
@Inject constructor(
subscriptionManager: SubscriptionManager,
class AccountDetailsViewModel @Inject constructor(
userManager: UserManager,
private val settings: Settings,
private val subscriptionManager: SubscriptionManager,
private val syncManager: SyncManager,
private val settings: Settings,
private val analyticsTracker: AnalyticsTracker,
private val crashLogging: CrashLogging,
private val subscriptionMapper: SubscriptionMapper,
Expand Down Expand Up @@ -131,6 +133,7 @@ class AccountDetailsViewModel
AccountSectionsState(
isSubscribedToNewsLetter = marketingOptIn,
email = signedInState?.email,
winbackInitParams = computeWinbackParams(signInState),
canChangeCredentials = !syncManager.isGoogleLogin(),
canUpgradeAccount = signedInState?.isSignedInAsPlus == true && isGiftExpiring,
canCancelSubscription = signedInState?.isSignedInAsPaid == true,
Expand All @@ -141,12 +144,31 @@ class AccountDetailsViewModel
initialValue = AccountSectionsState(
isSubscribedToNewsLetter = false,
email = null,
winbackInitParams = WinbackInitParams.Empty,
canChangeCredentials = false,
canUpgradeAccount = false,
canCancelSubscription = false,
),
)

private suspend fun computeWinbackParams(signInState: SignInState): WinbackInitParams {
val paidSubscriptionStatus = (signInState as? SignInState.SignedIn)?.subscriptionStatus as? SubscriptionStatus.Paid
val subscriptionPlatform = paidSubscriptionStatus?.platform

return if (subscriptionPlatform != SubscriptionPlatform.ANDROID) {
WinbackInitParams.Empty
} else {
when (val purchasesState = subscriptionManager.loadPurchases()) {
is PurchasesState.Failure -> WinbackInitParams.Empty
is PurchasesState.Loaded -> WinbackInitParams(
hasGoogleSubscription = purchasesState.purchases
.filter { it.isAcknowledged && it.isAutoRenewing }
.isNotEmpty(),
)
}
}
}

internal val miniPlayerInset = settings.bottomInset.stateIn(
scope = viewModelScope,
started = SharingStarted.Eagerly,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import au.com.shiftyjelly.pocketcasts.compose.components.TextH40
import au.com.shiftyjelly.pocketcasts.compose.components.TextP50
import au.com.shiftyjelly.pocketcasts.compose.patronPurple
import au.com.shiftyjelly.pocketcasts.compose.theme
import au.com.shiftyjelly.pocketcasts.profile.winback.WinbackInitParams
import au.com.shiftyjelly.pocketcasts.ui.theme.Theme
import au.com.shiftyjelly.pocketcasts.images.R as IR
import au.com.shiftyjelly.pocketcasts.localization.R as LR
Expand All @@ -52,7 +53,7 @@ internal fun AccountSections(
onChangeEmail: () -> Unit,
onChangePassword: () -> Unit,
onUpgradeToPatron: () -> Unit,
onCancelSubscription: () -> Unit,
onCancelSubscription: (WinbackInitParams) -> Unit,
onChangeNewsletterSubscription: (Boolean) -> Unit,
onShowPrivacyPolicy: () -> Unit,
onShowTermsOfUse: () -> Unit,
Expand Down Expand Up @@ -96,7 +97,7 @@ internal fun AccountSections(
AccountSection.CancelSubscription -> ButtonSection(
section = section,
config = config,
onClick = onCancelSubscription,
onClick = { onCancelSubscription(state.winbackInitParams) },
)
AccountSection.Newsletter -> SwitchSection(
isToggled = state.isSubscribedToNewsLetter,
Expand Down Expand Up @@ -149,17 +150,20 @@ internal data class AccountSectionsConfig(
internal data class AccountSectionsState(
val isSubscribedToNewsLetter: Boolean,
val email: String?,
val winbackInitParams: WinbackInitParams,
val availableSections: List<AccountSection>,
) {
constructor(
isSubscribedToNewsLetter: Boolean,
email: String?,
winbackInitParams: WinbackInitParams,
canChangeCredentials: Boolean,
canUpgradeAccount: Boolean,
canCancelSubscription: Boolean,
) : this(
isSubscribedToNewsLetter = isSubscribedToNewsLetter,
email = email,
winbackInitParams = winbackInitParams,
availableSections = buildList {
addAll(AccountSection.entries)
if (email == null) {
Expand All @@ -182,6 +186,7 @@ internal data class AccountSectionsState(
fun empty() = AccountSectionsState(
isSubscribedToNewsLetter = false,
email = null,
winbackInitParams = WinbackInitParams.Empty,
canChangeCredentials = false,
canUpgradeAccount = false,
canCancelSubscription = false,
Expand Down Expand Up @@ -390,6 +395,7 @@ private fun AccountSectionsPreview() {
isSubscribedToNewsLetter = false,
email = "",
availableSections = AccountSection.entries,
winbackInitParams = WinbackInitParams.Empty,
),
onChangeAvatar = { },
onChangeEmail = { },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package au.com.shiftyjelly.pocketcasts.profile.winback
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.os.Parcelable
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
Expand Down Expand Up @@ -31,6 +32,8 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.core.os.BundleCompat
import androidx.core.os.bundleOf
import androidx.fragment.app.viewModels
import androidx.fragment.compose.content
import androidx.navigation.NavBackStackEntry
Expand All @@ -49,12 +52,15 @@ import au.com.shiftyjelly.pocketcasts.views.activity.WebViewActivity
import au.com.shiftyjelly.pocketcasts.views.fragments.BaseDialogFragment
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
import au.com.shiftyjelly.pocketcasts.localization.R as LR

@AndroidEntryPoint
class WinbackFragment : BaseDialogFragment() {
private val viewModel by viewModels<WinbackViewModel>()

private val params get() = BundleCompat.getParcelable(requireArguments(), INPUT_ARGS, WinbackInitParams::class.java)!!

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
Expand All @@ -72,7 +78,11 @@ class WinbackFragment : BaseDialogFragment() {
Box {
NavHost(
navController = navController,
startDestination = WinbackNavRoutes.WinbackOffer,
startDestination = if (params.hasGoogleSubscription) {
WinbackNavRoutes.WinbackOffer
} else {
WinbackNavRoutes.CancelConfirmation
},
enterTransition = { slideInToStart() },
exitTransition = { slideOutToStart() },
popEnterTransition = { slideInToEnd() },
Expand Down Expand Up @@ -197,7 +207,7 @@ class WinbackFragment : BaseDialogFragment() {
}

private fun handleSubscriptionCancellation(productIds: List<String>): Boolean {
return if (productIds.isNotEmpty()) {
return if (productIds.isNotEmpty() && params.hasGoogleSubscription) {
goToPlayStoreSubscriptions(productIds.singleOrNull())
} else {
WebViewActivity.show(
Expand All @@ -223,6 +233,25 @@ class WinbackFragment : BaseDialogFragment() {
.build()
return runCatching { startActivity(Intent(Intent.ACTION_VIEW, uri)) }.isSuccess
}

companion object {
private const val INPUT_ARGS = "WinbackFragment.Params"

fun create(params: WinbackInitParams) = WinbackFragment().apply {
arguments = bundleOf(INPUT_ARGS to params)
}
}
}

@Parcelize
data class WinbackInitParams(
val hasGoogleSubscription: Boolean,
) : Parcelable {
companion object {
val Empty = WinbackInitParams(
hasGoogleSubscription = false,
)
}
}

private object WinbackNavRoutes {
Expand Down

0 comments on commit ab96140

Please sign in to comment.