Skip to content

Commit

Permalink
Support partially failed purchase triggering "Subscription is being a…
Browse files Browse the repository at this point in the history
…ctivated" state (#2639)
  • Loading branch information
miasma13 authored Mar 25, 2024
1 parent 36df625 commit dde20ab
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 29 deletions.
7 changes: 5 additions & 2 deletions DuckDuckGo/SettingsState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ struct SettingsState {
var enabled: Bool
var canPurchase: Bool
var hasActiveSubscription: Bool
var isSubscriptionPendingActivation: Bool
}

struct SyncSettings {
Expand Down Expand Up @@ -113,8 +114,10 @@ struct SettingsState {
speechRecognitionAvailable: false,
loginsEnabled: false,
networkProtection: NetworkProtection(enabled: false, status: ""),
subscription: Subscription(enabled: false, canPurchase: false,
hasActiveSubscription: false),
subscription: Subscription(enabled: false,
canPurchase: false,
hasActiveSubscription: false,
isSubscriptionPendingActivation: false),
sync: SyncSettings(enabled: false, title: "")
)
}
Expand Down
7 changes: 3 additions & 4 deletions DuckDuckGo/SettingsSubscriptionView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,6 @@ struct SettingsSubscriptionView: View {
var body: some View {
if viewModel.state.subscription.enabled {
Section(header: Text(UserText.settingsPProSection)) {

if viewModel.state.subscription.hasActiveSubscription {

if !viewModel.isLoadingSubscriptionState {
Expand All @@ -196,13 +195,13 @@ struct SettingsSubscriptionView: View {
noEntitlementsAvailableView
}
}
} else if viewModel.state.subscription.isSubscriptionPendingActivation {
noEntitlementsAvailableView
} else {
purchaseSubscriptionView

}

}

// Selected Feature handler for Subscription Flow
.onChange(of: subscriptionFlowViewModel.selectedFeature) { value in
guard let value else { return }
Expand Down
64 changes: 41 additions & 23 deletions DuckDuckGo/SettingsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -310,23 +310,30 @@ extension SettingsViewModel {
var enabled = false
var canPurchase = false
var hasActiveSubscription = false

var isSubscriptionPendingActivation = false

#if SUBSCRIPTION
if #available(iOS 15, *) {
enabled = isPrivacyProEnabled
canPurchase = SubscriptionPurchaseEnvironment.canPurchase
await setupSubscriptionEnvironment()
if let token = AccountManager().accessToken {
let subscriptionResult = await SubscriptionService.getSubscription(accessToken: token)
if case .success(let subscription) = subscriptionResult {
switch subscriptionResult {
case .success(let subscription):
hasActiveSubscription = subscription.isActive
case .failure:
if await PurchaseManager.hasActiveSubscription() {
isSubscriptionPendingActivation = true
}
}
}
}
#endif
return SettingsState.Subscription(enabled: enabled,
canPurchase: canPurchase,
hasActiveSubscription: hasActiveSubscription)
hasActiveSubscription: hasActiveSubscription,
isSubscriptionPendingActivation: isSubscriptionPendingActivation)
}

private func getSyncState() -> SettingsState.SyncSettings {
Expand Down Expand Up @@ -374,31 +381,42 @@ extension SettingsViewModel {
// Fetch available subscriptions from the backend (or sign out)
switch await SubscriptionService.getSubscription(accessToken: token) {

case .success(let subscription) where subscription.isActive:

// Check entitlements and update UI accordingly
let entitlements: [Entitlement.ProductName] = [.networkProtection, .dataBrokerProtection, .identityTheftRestoration]
for entitlement in entitlements {
if case let .success(result) = await AccountManager().hasEntitlement(for: entitlement) {
switch entitlement {
case .identityTheftRestoration:
self.shouldShowITP = result
case .dataBrokerProtection:
self.shouldShowDBP = result
case .networkProtection:
self.shouldShowNetP = result
case .unknown:
return
case .success(let subscription):
if subscription.isActive {
state.subscription.hasActiveSubscription = true
state.subscription.isSubscriptionPendingActivation = false

// Check entitlements and update UI accordingly
let entitlements: [Entitlement.ProductName] = [.networkProtection, .dataBrokerProtection, .identityTheftRestoration]
for entitlement in entitlements {
if case let .success(result) = await AccountManager().hasEntitlement(for: entitlement) {
switch entitlement {
case .identityTheftRestoration:
self.shouldShowITP = result
case .dataBrokerProtection:
self.shouldShowDBP = result
case .networkProtection:
self.shouldShowNetP = result
case .unknown:
return
}
}
}
} else {
// Sign out in case subscription is no longer active
signOutUser()
}
isLoadingSubscriptionState = false

default:

case .failure:
// Account is active but there's not a valid subscription / entitlements
isLoadingSubscriptionState = false
signOutUser()
if await PurchaseManager.hasActiveSubscription() {
state.subscription.isSubscriptionPendingActivation = true
} else {
// Sign out in case access token is present but no subscription and there is no active transaction on Apple ID
signOutUser()
}
}
isLoadingSubscriptionState = false
}

@available(iOS 15.0, *)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ final class SubscriptionFlowViewModel: ObservableObject {
state.transactionError = .purchaseFailed
case .missingEntitlements:
isBackendError = true
state.shouldDismissView = true
state.transactionError = .missingEntitlements
case .failedToGetSubscriptionOptions:
isStoreError = true
Expand Down

0 comments on commit dde20ab

Please sign in to comment.