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

Manage Expired Subscription #2764

Merged
merged 48 commits into from
May 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
5e736c1
Display expired state in settings
afterxleep Apr 16, 2024
0eba450
Optimize logic for displaying view
afterxleep Apr 16, 2024
3c93c21
Display Subscription Expired in Settings
afterxleep Apr 16, 2024
eb63366
Fix title
afterxleep Apr 16, 2024
d42be43
Merge branch 'daniel/subscription/expired/1.settings' into daniel/sub…
afterxleep Apr 16, 2024
fe42056
Reflect state in management screen
afterxleep Apr 16, 2024
80bfe42
Update subscription timer
afterxleep Apr 16, 2024
ac4b018
View Plans action from Settings
afterxleep Apr 17, 2024
2bf360f
Manage Stripe Subscription
afterxleep Apr 17, 2024
fe5cfc1
Present Purchase Options
afterxleep Apr 17, 2024
43f97f9
Subscription Expired State: 1. Display expired state in settings (#2751)
afterxleep Apr 17, 2024
f9d5f7c
Repurchase subscription (Approach 1)
afterxleep Apr 18, 2024
588a95e
Renew Stripe Subscription
afterxleep Apr 19, 2024
3314382
Merge branch 'daniel/subscription/expired/0.integration' into daniel/…
afterxleep Apr 19, 2024
ec2b02a
Point to BSK Branch
afterxleep Apr 19, 2024
f4c0e13
Move URLs to BSK
afterxleep Apr 19, 2024
50365ac
Fix title
afterxleep Apr 16, 2024
1332fd0
Display Purchase View on Expired Subscription
afterxleep Apr 19, 2024
663308b
Merge branch 'main' into daniel/subscription/expired/2.management
afterxleep Apr 22, 2024
ed76f5e
Merge branch 'main' into daniel/subscription/expired/0.integration
afterxleep Apr 22, 2024
fbe4cc2
Merge branch 'daniel/subscription/expired/0.integration' into daniel/…
afterxleep Apr 22, 2024
9190e6d
Update packages
afterxleep Apr 22, 2024
13450cf
Properly Cache last Subcription State
afterxleep Apr 22, 2024
177ab8e
FIx lint issue
afterxleep Apr 22, 2024
97f098a
Reset cache on Signout
afterxleep Apr 22, 2024
87153e8
Adds loaders for slow connections
afterxleep Apr 22, 2024
6dbbbb4
Reset token fetch
afterxleep Apr 22, 2024
fbba504
Remove embedded BSK
afterxleep Apr 22, 2024
1fae30a
Fix onDisappear
afterxleep Apr 22, 2024
1d63483
Remove Stripe stuff
afterxleep Apr 22, 2024
1ad68f5
Move Asset to PDF and additional stripe removal
afterxleep Apr 22, 2024
dfbe90e
Merge branch 'main' into daniel/subscription/expired/2.management
afterxleep Apr 23, 2024
44d041c
Bump BSK
afterxleep Apr 23, 2024
2ebad18
Bump BSK
afterxleep Apr 23, 2024
eca6924
Merge branch 'main' into daniel/subscription/expired/2.management
afterxleep Apr 23, 2024
32ffde9
Package.resolved
afterxleep Apr 23, 2024
e13e67f
Only return authToken when subscription is active
miasma13 Apr 25, 2024
8b753bf
Always update canPurchase value even if state restored from cache
miasma13 Apr 25, 2024
d128b91
Merge branch 'main' into daniel/subscription/expired/2.management
afterxleep Apr 29, 2024
f73a45e
Merge branch 'main' into daniel/subscription/expired/2.management
afterxleep Apr 30, 2024
9fb5149
Revert Token change
afterxleep Apr 30, 2024
504bc2e
Only pass access token to get the subscription
afterxleep May 2, 2024
2a8e8dc
Method to fetch getAccessToken
afterxleep May 2, 2024
23cb7a9
Return to app settings after restoring a subscription
afterxleep May 2, 2024
351e375
Merge branch 'main' into daniel/subscription/expired/2.management
afterxleep May 3, 2024
22f530c
Bump BSK
afterxleep May 3, 2024
e2a985b
Merge branch 'main' into daniel/subscription/expired/2.management
afterxleep May 3, 2024
a30cdd0
Fix conflict
afterxleep May 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,7 @@
D68DF81C2B58302E0023DBEA /* SubscriptionRestoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68DF81B2B58302E0023DBEA /* SubscriptionRestoreView.swift */; };
D68DF81E2B5830380023DBEA /* SubscriptionRestoreViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68DF81D2B5830380023DBEA /* SubscriptionRestoreViewModel.swift */; };
D69FBF762B28BE3600B505F1 /* SettingsSubscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69FBF752B28BE3600B505F1 /* SettingsSubscriptionView.swift */; };
D6A4645C2BD6D6DA00F80DC2 /* UserDefaultsCacheKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A4645B2BD6D6DA00F80DC2 /* UserDefaultsCacheKey.swift */; };
D6ACEA322BBD55BF008FADDF /* TabURLInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6ACEA312BBD55BF008FADDF /* TabURLInterceptor.swift */; };
D6BFCB5F2B7524AA0051FF81 /* SubscriptionPIRView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6BFCB5E2B7524AA0051FF81 /* SubscriptionPIRView.swift */; };
D6BFCB612B7525160051FF81 /* SubscriptionPIRViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6BFCB602B7525160051FF81 /* SubscriptionPIRViewModel.swift */; };
Expand Down Expand Up @@ -2384,6 +2385,7 @@
D68DF81B2B58302E0023DBEA /* SubscriptionRestoreView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionRestoreView.swift; sourceTree = "<group>"; };
D68DF81D2B5830380023DBEA /* SubscriptionRestoreViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionRestoreViewModel.swift; sourceTree = "<group>"; };
D69FBF752B28BE3600B505F1 /* SettingsSubscriptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionView.swift; sourceTree = "<group>"; };
D6A4645B2BD6D6DA00F80DC2 /* UserDefaultsCacheKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsCacheKey.swift; sourceTree = "<group>"; };
D6ACEA312BBD55BF008FADDF /* TabURLInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabURLInterceptor.swift; sourceTree = "<group>"; };
D6BFCB5E2B7524AA0051FF81 /* SubscriptionPIRView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionPIRView.swift; sourceTree = "<group>"; };
D6BFCB602B7525160051FF81 /* SubscriptionPIRViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionPIRViewModel.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -5141,6 +5143,7 @@
85C8E61C2B0E47380029A6BD /* BookmarksDatabaseSetup.swift */,
9821234D2B6D0A6300F08C57 /* UserAuthenticator.swift */,
9821234F2B6D233E00F08C57 /* UserSession.swift */,
D6A4645B2BD6D6DA00F80DC2 /* UserDefaultsCacheKey.swift */,
);
name = Application;
sourceTree = "<group>";
Expand Down Expand Up @@ -6605,6 +6608,7 @@
D60B1F272B9DDE5A00AE4760 /* SubscriptionGoogleView.swift in Sources */,
984D035C24AE15CD0066CFB8 /* TabSwitcherSettings.swift in Sources */,
D6E83C562B21ECC1006C8AFB /* SettingsLegacyViewProvider.swift in Sources */,
D6A4645C2BD6D6DA00F80DC2 /* UserDefaultsCacheKey.swift in Sources */,
98B31292218CCB8C00E54DE1 /* AppDependencyProvider.swift in Sources */,
D670E5BD2BB6AA0000941A42 /* View+AppearModifiers.swift in Sources */,
D6ACEA322BBD55BF008FADDF /* TabURLInterceptor.swift in Sources */,
Expand Down
4 changes: 0 additions & 4 deletions DuckDuckGo/SettingsRootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,6 @@ struct SettingsRootView: View {
viewModel.onAppear()
}

.onDisappear {
viewModel.onDissapear()
}

// MARK: Deeplink Modifiers

.sheet(isPresented: $shouldDisplayDeepLinkSheet,
Expand Down
16 changes: 10 additions & 6 deletions DuckDuckGo/SettingsState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,17 @@ struct SettingsState {
var enabled: Bool
var status: String
}

struct Subscription {
struct Subscription: Codable {
var enabled: Bool
var canPurchase: Bool
var isSignedIn: Bool
var hasActiveSubscription: Bool
var isSubscriptionPendingActivation: Bool
var isRestoring: Bool
var shouldDisplayRestoreSubscriptionError: Bool
var entitlements: [Entitlement.ProductName]
var platform: DDGSubscription.Platform
var isShowingStripeView: Bool
}

struct SyncSettings {
Expand Down Expand Up @@ -96,7 +98,7 @@ struct SettingsState {

// Sync Properties
var sync: SyncSettings

static var defaults: SettingsState {
return SettingsState(
appTheme: .systemDefault,
Expand All @@ -122,11 +124,13 @@ struct SettingsState {
networkProtection: NetworkProtection(enabled: false, status: ""),
subscription: Subscription(enabled: false,
canPurchase: false,
isSignedIn: false,
hasActiveSubscription: false,
isSubscriptionPendingActivation: false,
isRestoring: false,
shouldDisplayRestoreSubscriptionError: false,
entitlements: []),
entitlements: [],
platform: .unknown,
isShowingStripeView: false),
sync: SyncSettings(enabled: false, title: "")
)
}
Expand Down
148 changes: 101 additions & 47 deletions DuckDuckGo/SettingsSubscriptionView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
// limitations under the License.
//

import Core
import Subscription
import SwiftUI
import UIKit

import Subscription
import Core
@available(iOS 15.0, *)
struct SettingsSubscriptionView: View {

Expand All @@ -31,6 +31,8 @@ struct SettingsSubscriptionView: View {
@State var isShowingITP = false
@State var isShowingRestoreFlow = false
@State var isShowingSubscribeFlow = false
@State var isShowingGoogleView = false
@State var isShowingStripeView = false
@State var isShowingSubscriptionError = false

enum Constants {
Expand All @@ -39,6 +41,7 @@ struct SettingsSubscriptionView: View {
static let noEntitlementsIconWidth = 20.0
static let navigationDelay = 0.3
static let infoIcon = "info-16"
static let alertIcon = "Exclamation-Color-16"
}

private var subscriptionDescriptionView: some View {
Expand Down Expand Up @@ -102,6 +105,41 @@ struct SettingsSubscriptionView: View {
}
}

@ViewBuilder
private var subscriptionExpiredView: some View {
Group {
SettingsCustomCell(content: {
HStack(alignment: .top) {
Image(Constants.alertIcon)
.frame(width: Constants.noEntitlementsIconWidth)
.padding(.top, Constants.topCellPadding)
VStack(alignment: .leading) {
Text(UserText.settingsPProSubscriptionExpiredTitle).daxBodyRegular()
Text(UserText.settingsPProSubscribeAgain).daxFootnoteRegular()
.padding(.bottom, Constants.purchaseDescriptionPadding)
}.foregroundColor(Color(designSystemColor: .textSecondary))
}
})

let subscribeView = SubscriptionContainerView(currentView: .subscribe)
.navigationViewStyle(.stack)
.environmentObject(subscriptionNavigationCoordinator)
NavigationLink(
destination: subscribeView,
isActive: $isShowingSubscribeFlow,
label: { SettingsCellView(label: UserText.subscriptionRestoreNotFoundPlans) })

// Renew Subscription (Expired)
let settingsView = SubscriptionSettingsView(viewPlans: {
isShowingSubscribeFlow = true
})
.environmentObject(subscriptionNavigationCoordinator)
NavigationLink(destination: settingsView) {
SettingsCustomCell(content: { manageSubscriptionView })
}
}
}

@ViewBuilder
private var noEntitlementsAvailableView: some View {
Group {
Expand All @@ -124,58 +162,74 @@ struct SettingsSubscriptionView: View {
@ViewBuilder
private var subscriptionDetailsView: some View {

if viewModel.state.subscription.entitlements.contains(.networkProtection) {
SettingsCellView(label: UserText.settingsPProVPNTitle,
subtitle: viewModel.state.networkProtection.status != "" ? viewModel.state.networkProtection.status : nil,
action: { viewModel.presentLegacyView(.netP) },
disclosureIndicator: true,
isButton: true)
}

if viewModel.state.subscription.entitlements.contains(.dataBrokerProtection) {
NavigationLink(destination: SubscriptionPIRView(),
isActive: $isShowingDBP,
label: {
SettingsCellView(label: UserText.settingsPProDBPTitle,
subtitle: UserText.settingsPProDBPSubTitle)
if viewModel.state.subscription.entitlements.contains(.networkProtection) {
SettingsCellView(
label: UserText.settingsPProVPNTitle,
subtitle: viewModel.state.networkProtection.status != ""
? viewModel.state.networkProtection.status : nil,
action: { viewModel.presentLegacyView(.netP) },
disclosureIndicator: true,
isButton: true)
}

if viewModel.state.subscription.entitlements.contains(.dataBrokerProtection) {
NavigationLink(
destination: SubscriptionPIRView(),
isActive: $isShowingDBP,
label: {
SettingsCellView(
label: UserText.settingsPProDBPTitle,
subtitle: UserText.settingsPProDBPSubTitle)
})

}

if viewModel.state.subscription.entitlements.contains(.identityTheftRestoration) {
NavigationLink(destination: SubscriptionITPView(),
isActive: $isShowingITP,
label: {
SettingsCellView(label: UserText.settingsPProITRTitle,
subtitle: UserText.settingsPProITRSubTitle)

}

if viewModel.state.subscription.entitlements.contains(.identityTheftRestoration) {
NavigationLink(
destination: SubscriptionITPView(),
isActive: $isShowingITP,
label: {
SettingsCellView(
label: UserText.settingsPProITRTitle,
subtitle: UserText.settingsPProITRSubTitle)
})

}

NavigationLink(destination: SubscriptionSettingsView().environmentObject(subscriptionNavigationCoordinator)) {
SettingsCustomCell(content: { manageSubscriptionView })

}


NavigationLink(
destination: SubscriptionSettingsView().environmentObject(subscriptionNavigationCoordinator)
) {
SettingsCustomCell(content: { manageSubscriptionView })
}

}

var body: some View {
if viewModel.state.subscription.enabled && viewModel.state.subscription.canPurchase {

Section(header: Text(UserText.settingsPProSection)) {
if viewModel.state.subscription.hasActiveSubscription {

// Allow managing the subscription if we have some entitlements
if !viewModel.state.subscription.entitlements.isEmpty {
subscriptionDetailsView

// If no entitlements it should mean the backend is still out of sync
} else {
noEntitlementsAvailableView
}

switch (
viewModel.state.subscription.isSignedIn,
viewModel.state.subscription.hasActiveSubscription,
viewModel.state.subscription.entitlements.isEmpty
) {

// Signed In, Subscription Expired
case (true, false, _):
subscriptionExpiredView

// Signed in, Subscription Active, Valid entitlements
case (true, true, false):
subscriptionDetailsView // View for valid subscription details

} else if viewModel.state.subscription.isSubscriptionPendingActivation {
noEntitlementsAvailableView
} else {
purchaseSubscriptionView
// Signed in, Subscription Active, Empty Entitlements
case (true, true, true):
noEntitlementsAvailableView // View for no entitlements

// Signed out
case (false, _, _):
purchaseSubscriptionView // View for signing up or purchasing a subscription
}
}

Expand All @@ -191,7 +245,7 @@ struct SettingsSubscriptionView: View {
isShowingSubscribeFlow = false
}
}

}
}
}
2 changes: 1 addition & 1 deletion DuckDuckGo/SettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ struct SettingsView: View {
}

.onDisappear {
viewModel.onDissapear()
viewModel.onDisappear()
}

// MARK: Deeplink Modifiers
Expand Down