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

Subscriptions: 20 - Subscription Caching #2569

Merged
merged 37 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
7d7b7f7
Entitlements caching
afterxleep Mar 7, 2024
0cd9e25
Update entitlements check
afterxleep Mar 8, 2024
201d093
Display errors on Subscription and Restore Flow
afterxleep Mar 8, 2024
23fa003
Avoid showing the back button in the root view
afterxleep Mar 8, 2024
663cc01
DIsmiss the Subscription Stack from restore (if required)
afterxleep Mar 8, 2024
f8da0bc
Remove internal BSK
afterxleep Mar 8, 2024
461aa58
Fix PIR sheet view
afterxleep Mar 8, 2024
226f4e1
Merge branch 'main' into daniel/subscriptions/19.subscription.errors
afterxleep Mar 8, 2024
4068456
Update to use AccessToken (not authToken)
afterxleep Mar 9, 2024
00ad77a
Replace accessToken with AuthToken
afterxleep Mar 9, 2024
a84c442
Use accessToken instead of authToken
afterxleep Mar 9, 2024
56f07ca
Revert Certificate match for Alpha
afterxleep Mar 9, 2024
1f1f259
Hide Back button in root Webview
afterxleep Mar 9, 2024
5502011
Display errors when WebViews Fail to load
afterxleep Mar 9, 2024
d78c038
Missin Subscription Flag
afterxleep Mar 9, 2024
85bae13
Subscription Caching
afterxleep Mar 9, 2024
0b875bd
Push canceled purchase update
afterxleep Mar 10, 2024
aad3e2d
Remove local caching from Settings
afterxleep Mar 10, 2024
486f9df
Remove manual Caching from SettingsViewModel
afterxleep Mar 10, 2024
c2ff0fd
Fix minor issue with view presentation
afterxleep Mar 10, 2024
eff5495
Remove BSK embed
afterxleep Mar 10, 2024
decda35
Fix issue with Sheet presentation for ITP and DBP
afterxleep Mar 10, 2024
bea6ad5
Revert Presentation Changes for sheets
afterxleep Mar 10, 2024
03491f8
Refresh settings on dismiss
afterxleep Mar 10, 2024
46ef260
Cleanup
afterxleep Mar 10, 2024
ddbba04
Fix linter
afterxleep Mar 10, 2024
7f90cb1
Merge branch 'daniel/subscriptions/19.subscription.errors' into danie…
afterxleep Mar 10, 2024
501b9f2
Removed unnecesary endif
afterxleep Mar 10, 2024
7848425
Removed unneded SUBSCRIPTION
afterxleep Mar 11, 2024
fce57fe
Move activeSubscriptionCache outside of compiler flag
afterxleep Mar 11, 2024
d92dd36
Move wrapped value out
afterxleep Mar 11, 2024
6fb68dd
Merge branch 'main' into daniel/subscriptions/19.subscription.errors
afterxleep Mar 11, 2024
ae5a427
Conflict merge fix
afterxleep Mar 11, 2024
00dd83a
Merge branch 'daniel/subscriptions/19.subscription.errors' into danie…
afterxleep Mar 11, 2024
9f87596
Merge branch 'main' into daniel/subscriptions/20.caching
miasma13 Mar 11, 2024
857f0be
Bump BSK
afterxleep Mar 11, 2024
e328502
Merge branch 'main' into daniel/subscriptions/20.caching
afterxleep Mar 11, 2024
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
4 changes: 2 additions & 2 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10043,8 +10043,8 @@
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit";
requirement = {
kind = exactVersion;
version = 122.0.0;
branch = daniel/subscription.cache;
kind = branch;
};
};
B6F997C22B8F374300476735 /* XCRemoteSwiftPackageReference "apple-toolbox" */ = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/DuckDuckGo/BrowserServicesKit",
"state" : {
"revision" : "83bcbbf0dace717db6e518e4d867d617c846a3b5",
"version" : "122.0.0"
"branch" : "daniel/subscription.cache",
"revision" : "844a9e49d7bd8aa253a4bd00efcc73b8594086f2"
}
},
{
Expand Down
22 changes: 10 additions & 12 deletions DuckDuckGo/SettingsSubscriptionView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ struct SettingsSubscriptionView: View {
action: { isShowingsubScriptionFlow = true },
isButton: true )

// Subscription Restore
.sheet(isPresented: $isShowingsubScriptionFlow,
onDismiss: { Task { viewModel.onAppear() } },
content: { SubscriptionFlowView(viewModel: subscriptionFlowViewModel).interactiveDismissDisabled() })

SettingsCustomCell(content: { iHaveASubscriptionView },
action: {
isShowingsubScriptionFlow = true
Expand Down Expand Up @@ -138,25 +143,30 @@ struct SettingsSubscriptionView: View {
SettingsCellView(label: UserText.settingsPProDBPTitle,
subtitle: UserText.settingsPProDBPSubTitle,
action: { isShowingDBP.toggle() }, isButton: true)

.sheet(isPresented: $isShowingDBP) {
SubscriptionPIRView()
}

}

if viewModel.shouldShowITP {
SettingsCellView(label: UserText.settingsPProITRTitle,
subtitle: UserText.settingsPProITRSubTitle,
action: { isShowingITP.toggle() }, isButton: true)

.sheet(isPresented: $isShowingITP) {
SubscriptionITPView()
}

}

NavigationLink(destination: SubscriptionSettingsView()) {
SettingsCustomCell(content: { manageSubscriptionView })
}

}

}

var body: some View {
Expand All @@ -179,18 +189,6 @@ struct SettingsSubscriptionView: View {
}

}
// Subscription Restore
.sheet(isPresented: $isShowingsubScriptionFlow) {
SubscriptionFlowView(viewModel: subscriptionFlowViewModel).interactiveDismissDisabled()
}


// Refresh subscription when dismissing the Subscription Flow
.onChange(of: isShowingsubScriptionFlow, perform: { value in
if !value {
Task { viewModel.onAppear() }
}
})

.onChange(of: viewModel.shouldNavigateToDBP, perform: { value in
if value {
Expand Down
62 changes: 27 additions & 35 deletions DuckDuckGo/SettingsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import Common
import Combine
import SyncUI


#if SUBSCRIPTION
import Subscription
#endif
Expand All @@ -47,12 +48,20 @@ final class SettingsViewModel: ObservableObject {
private var legacyViewProvider: SettingsLegacyViewProvider
private lazy var versionProvider: AppVersion = AppVersion.shared
private let voiceSearchHelper: VoiceSearchHelperProtocol

#if SUBSCRIPTION
private var accountManager: AccountManager
private var signOutObserver: Any?

// Sheet Presentation & Navigation
@Published var isRestoringSubscription: Bool = false
@Published var shouldDisplayRestoreSubscriptionError: Bool = false
@Published var shouldShowNetP = false
@Published var shouldShowDBP = false
@Published var shouldShowITP = false
#endif
@UserDefaultsWrapper(key: .subscriptionIsActive, defaultValue: false)
static private var cachedHasActiveSubscription: Bool


#if NETWORK_PROTECTION
Expand All @@ -63,10 +72,6 @@ final class SettingsViewModel: ObservableObject {
private lazy var isPad = UIDevice.current.userInterfaceIdiom == .pad
private var cancellables = Set<AnyCancellable>()

// Defaults
@UserDefaultsWrapper(key: .subscriptionIsActive, defaultValue: false)
static private var cachedHasActiveSubscription: Bool

// Closures to interact with legacy view controllers through the container
var onRequestPushLegacyView: ((UIViewController) -> Void)?
var onRequestPresentLegacyView: ((UIViewController, _ modal: Bool) -> Void)?
Expand All @@ -78,10 +83,6 @@ final class SettingsViewModel: ObservableObject {
@Published var shouldNavigateToDBP = false
@Published var shouldNavigateToITP = false
@Published var shouldNavigateToSubscriptionFlow = false

@Published var shouldShowNetP = false
@Published var shouldShowDBP = false
@Published var shouldShowITP = false

// Our View State
@Published private(set) var state: SettingsState
Expand Down Expand Up @@ -247,8 +248,9 @@ extension SettingsViewModel {
// This manual (re)initialization will go away once appSettings and
// other dependencies are observable (Such as AppIcon and netP)
// and we can use subscribers (Currently called from the view onAppear)
private func initState() {
self.state = SettingsState(
@MainActor
private func initState() async {
self.state = await SettingsState(
appTheme: appSettings.currentThemeName,
appIcon: AppIconManager.shared.appIcon,
fireButtonAnimation: appSettings.currentFireButtonAnimation,
Expand All @@ -274,15 +276,6 @@ extension SettingsViewModel {

setupSubscribers()

#if SUBSCRIPTION
if #available(iOS 15, *) {
Task {
if state.subscription.enabled {
await setupSubscriptionEnvironment()
}
}
}
#endif
}

private func getNetworkProtectionState() -> SettingsState.NetworkProtection {
Expand All @@ -296,14 +289,24 @@ extension SettingsViewModel {
return SettingsState.NetworkProtection(enabled: enabled, status: "")
}

private func getSubscriptionState() -> SettingsState.Subscription {
private func getSubscriptionState() async -> SettingsState.Subscription {
var enabled = false
var canPurchase = false
let hasActiveSubscription = Self.cachedHasActiveSubscription
#if SUBSCRIPTION
var hasActiveSubscription = false

#if SUBSCRIPTION
if #available(iOS 15, *) {
enabled = featureFlagger.isFeatureOn(.subscription)
canPurchase = SubscriptionPurchaseEnvironment.canPurchase
#endif
await setupSubscriptionEnvironment()
if let token = AccountManager().accessToken {
let subscriptionResult = await SubscriptionService.getSubscription(accessToken: token)
if case .success(let subscription) = subscriptionResult {
hasActiveSubscription = subscription.isActive
}
}
}
#endif
return SettingsState.Subscription(enabled: enabled,
canPurchase: canPurchase,
hasActiveSubscription: hasActiveSubscription)
Expand Down Expand Up @@ -353,9 +356,6 @@ extension SettingsViewModel {

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

// Cache Subscription state
cacheSubscriptionState(active: true)

// Check entitlements and update UI accordingly
let entitlements: [Entitlement.ProductName] = [.identityTheftRestoration, .dataBrokerProtection, .networkProtection]
for entitlement in entitlements {
Expand All @@ -372,7 +372,6 @@ extension SettingsViewModel {
}
}
}

default:
// Account is active but there's not a valid subscription / entitlements
signOutUser()
Expand All @@ -382,18 +381,11 @@ extension SettingsViewModel {
@available(iOS 15.0, *)
private func signOutUser() {
AccountManager().signOut()
cacheSubscriptionState(active: false)
setupSubscriptionPurchaseOptions()
}

private func cacheSubscriptionState(active: Bool) {
self.state.subscription.hasActiveSubscription = active
Self.cachedHasActiveSubscription = active
}

@available(iOS 15.0, *)
private func setupSubscriptionPurchaseOptions() {
cacheSubscriptionState(active: false)
PurchaseManager.shared.$availableProducts
.receive(on: RunLoop.main)
.sink { [weak self] products in
Expand Down Expand Up @@ -470,7 +462,7 @@ extension SettingsViewModel {
extension SettingsViewModel {

func onAppear() {
initState()
Task { await initState() }
Task { await MainActor.run { navigateOnAppear() } }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ struct AsyncHeadlessWebView: View {
onContentType: { value in
viewModel.contentType = value
},
onNavigationError: { value in
viewModel.navigationError = value
},
navigationCoordinator: viewModel.navigationCoordinator
)
.frame(width: geometry.size.width, height: geometry.size.height)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ final class AsyncHeadlessWebViewViewModel: ObservableObject {
@Published var canGoBack: Bool = false
@Published var canGoForward: Bool = false
@Published var contentType: String = ""
@Published var navigationError: Error?
@Published var allowedDomains: [String]?

var navigationCoordinator = HeadlessWebViewNavCoordinator(webView: nil)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ struct HeadlessWebView: UIViewRepresentable {
var onCanGoBack: ((Bool) -> Void)?
var onCanGoForward: ((Bool) -> Void)?
var onContentType: ((String) -> Void)?
var onNavigationError: ((Error?) -> Void)?
var navigationCoordinator: HeadlessWebViewNavCoordinator

func makeUIView(context: Context) -> WKWebView {
Expand Down Expand Up @@ -73,6 +74,7 @@ struct HeadlessWebView: UIViewRepresentable {
onCanGoBack: onCanGoBack,
onCanGoForward: onCanGoForward,
onContentType: onContentType,
onNavigationError: onNavigationError,
settings: settings
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ final class HeadlessWebViewCoordinator: NSObject {
var onCanGoBack: ((Bool) -> Void)?
var onCanGoForward: ((Bool) -> Void)?
var onContentType: ((String) -> Void)?
var onNavigationError: ((Error?) -> Void)?
var settings: AsyncHeadlessWebViewSettings

var size: CGSize = .zero
Expand All @@ -52,6 +53,7 @@ final class HeadlessWebViewCoordinator: NSObject {
onCanGoBack: ((Bool) -> Void)?,
onCanGoForward: ((Bool) -> Void)?,
onContentType: ((String) -> Void)?,
onNavigationError: ((Error?) -> Void)?,
allowedDomains: [String]? = nil,
settings: AsyncHeadlessWebViewSettings = AsyncHeadlessWebViewSettings()) {
self.parent = parent
Expand All @@ -60,6 +62,7 @@ final class HeadlessWebViewCoordinator: NSObject {
self.onURLChange = onURLChange
self.onCanGoBack = onCanGoBack
self.onCanGoForward = onCanGoForward
self.onNavigationError = onNavigationError
self.onContentType = onContentType
self.settings = settings
}
Expand Down Expand Up @@ -106,6 +109,7 @@ final class HeadlessWebViewCoordinator: NSObject {
onCanGoBack = nil
onCanGoForward = nil
onContentType = nil
onNavigationError = nil
}

}
Expand All @@ -128,6 +132,7 @@ extension HeadlessWebViewCoordinator: WKNavigationDelegate {
}

func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
onNavigationError?(nil)
if let url = webView.url, url != lastURL {
onURLChange?(url)
lastURL = url
Expand Down Expand Up @@ -182,8 +187,12 @@ extension HeadlessWebViewCoordinator: WKNavigationDelegate {
decisionHandler(.allow)
}

func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: any Error) {
onNavigationError?(error)
}

func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
// NOOP
onNavigationError?(error)
}

// Javascript Confirm dialogs Delegate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,13 @@ final class IdentityTheftRestorationPagesFeature: Subfeature, ObservableObject {
return nil
}
}

func getAccessToken(params: Any, original: WKScriptMessage) async throws -> Encodable? {
let authToken = AccountManager().authToken ?? ""
return [Constants.token: authToken]
if let accessToken = AccountManager().accessToken {
return [Constants.token: accessToken]
} else {
return [String: String]()
}
}

deinit {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,8 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec
switch error {
case .cancelledByUser:
setTransactionError(.cancelledByUser)
await pushPurchaseUpdate(originalMessage: message, purchaseUpdate: PurchaseUpdate(type: "canceled"))
return nil
case .accountCreationFailed:
setTransactionError(.accountCreationFailed)
case .activeSubscriptionAlreadyPresent:
Expand Down
12 changes: 12 additions & 0 deletions DuckDuckGo/Subscription/ViewModel/SubscriptionEmailViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ final class SubscriptionEmailViewModel: ObservableObject {
@Published var activateSubscription = false
@Published var managingSubscriptionEmail = false
@Published var transactionError: SubscriptionRestoreError?
@Published var navigationError: Bool = false
@Published var shouldDisplayInactiveError: Bool = false
var webViewModel: AsyncHeadlessWebViewViewModel

Expand Down Expand Up @@ -101,6 +102,17 @@ final class SubscriptionEmailViewModel: ObservableObject {
}
}
.store(in: &cancellables)

webViewModel.$navigationError
.receive(on: DispatchQueue.main)
.sink { [weak self] error in
guard let strongSelf = self else { return }
DispatchQueue.main.async {
strongSelf.navigationError = error != nil ? true : false
}

}
.store(in: &cancellables)
}

private func handleTransactionError(error: SubscriptionPagesUseSubscriptionFeature.UseSubscriptionError) {
Expand Down
Loading
Loading