Skip to content

Commit

Permalink
Client displays correct subscription (#3581)
Browse files Browse the repository at this point in the history
Task/Issue URL:
https://app.asana.com/0/1208524871249522/1208379950230747/f

**Description**:
See https://app.asana.com/0/1208524871249522/1208799981662317/f

**Steps to test this PR**:
See https://app.asana.com/0/0/1208836865988482/f and its parent task.

<!--
Tagging instructions
If this PR isn't ready to be merged for whatever reason it should be
marked with the `DO NOT MERGE` label (particularly if it's a draft)
If it's pending Product Review/PFR, please add the `Pending Product
Review` label.

If at any point it isn't actively being worked on/ready for
review/otherwise moving forward (besides the above PR/PFR exception)
strongly consider closing it (or not opening it in the first place). If
you decide not to close it, make sure it's labelled to make it clear the
PRs state and comment with more information.
-->

**Definition of Done**:

* [ ] Does this PR satisfy our [Definition of
Done](https://app.asana.com/0/1202500774821704/1207634633537039/f)?

---
###### Internal references:
[Pull Request Review
Checklist](https://app.asana.com/0/1202500774821704/1203764234894239/f)
[Software Engineering
Expectations](https://app.asana.com/0/59792373528535/199064865822552)
[Technical Design
Template](https://app.asana.com/0/59792373528535/184709971311943)
[Pull Request
Documentation](https://app.asana.com/0/1202500774821704/1204012835277482/f)
  • Loading branch information
miasma13 authored Nov 29, 2024
1 parent 3a48d6a commit 90e645c
Show file tree
Hide file tree
Showing 27 changed files with 449 additions and 225 deletions.
30 changes: 29 additions & 1 deletion DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,10 @@
1E2BEAE52C8B00B5002741A3 /* SubscriptionPagesUseSubscriptionFeatureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E2BEAE32C8B00B5002741A3 /* SubscriptionPagesUseSubscriptionFeatureTests.swift */; };
1E559BB12BBCA9F1002B4AF6 /* RedirectNavigationResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E559BB02BBCA9F1002B4AF6 /* RedirectNavigationResponder.swift */; };
1E559BB22BBCA9F1002B4AF6 /* RedirectNavigationResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E559BB02BBCA9F1002B4AF6 /* RedirectNavigationResponder.swift */; };
1E5921BF2CF479E600E15CCA /* FeatureFlags in Frameworks */ = {isa = PBXBuildFile; productRef = 1E5921BE2CF479E600E15CCA /* FeatureFlags */; };
1E5921C12CF479EE00E15CCA /* FeatureFlags in Frameworks */ = {isa = PBXBuildFile; productRef = 1E5921C02CF479EE00E15CCA /* FeatureFlags */; };
1E5921C32CF47A0700E15CCA /* FeatureFlags in Frameworks */ = {isa = PBXBuildFile; productRef = 1E5921C22CF47A0700E15CCA /* FeatureFlags */; };
1E5921C52CF47A0F00E15CCA /* FeatureFlags in Frameworks */ = {isa = PBXBuildFile; productRef = 1E5921C42CF47A0F00E15CCA /* FeatureFlags */; };
1E7E2E9029029A2A00C01B54 /* ContentBlockingRulesUpdateObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E7E2E8F29029A2A00C01B54 /* ContentBlockingRulesUpdateObserver.swift */; };
1E7E2E942902AC0E00C01B54 /* PrivacyDashboardPermissionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E7E2E932902AC0E00C01B54 /* PrivacyDashboardPermissionHandler.swift */; };
1E950E3F2912A10D0051A99B /* ContentBlocking in Frameworks */ = {isa = PBXBuildFile; productRef = 1E950E3E2912A10D0051A99B /* ContentBlocking */; };
Expand Down Expand Up @@ -5085,6 +5089,7 @@
buildActionMask = 2147483647;
files = (
4B5235452C7BB14D00AFAF64 /* WireGuard in Frameworks */,
1E5921C12CF479EE00E15CCA /* FeatureFlags in Frameworks */,
37269F012B332FC8005E8E46 /* Common in Frameworks */,
9D9DE57B2C63AA1F00D20B15 /* AppKitExtensions in Frameworks */,
EE7295E92A545BC4008C0991 /* NetworkProtection in Frameworks */,
Expand Down Expand Up @@ -5160,6 +5165,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
1E5921BF2CF479E600E15CCA /* FeatureFlags in Frameworks */,
02589D9F2C88E8270093940D /* Persistence in Frameworks */,
F1DF95E52BD1807C0045E591 /* Subscription in Frameworks */,
4B5235472C7BB15700AFAF64 /* WireGuard in Frameworks */,
Expand Down Expand Up @@ -5225,6 +5231,7 @@
9DC5FAC52C6B8A010011F068 /* AppKitExtensions in Frameworks */,
020807B22C6CFF95006F94C4 /* Configuration in Frameworks */,
F1D0428E2BFB9F9C00A31506 /* Subscription in Frameworks */,
1E5921C32CF47A0700E15CCA /* FeatureFlags in Frameworks */,
C18BF9D02C736C9100ED6B8A /* Freemium in Frameworks */,
9D9AE8F92AAA3AD00026E7DC /* DataBrokerProtection in Frameworks */,
);
Expand All @@ -5239,6 +5246,7 @@
315A023F2B6421AE00BFA577 /* Networking in Frameworks */,
9DC5FAC72C6B8A080011F068 /* AppKitExtensions in Frameworks */,
F1D042902BFB9FA300A31506 /* Subscription in Frameworks */,
1E5921C52CF47A0F00E15CCA /* FeatureFlags in Frameworks */,
C18BF9D22C736C9700ED6B8A /* Freemium in Frameworks */,
9D9AE8FB2AAA3AD90026E7DC /* DataBrokerProtection in Frameworks */,
);
Expand Down Expand Up @@ -10039,6 +10047,7 @@
4B5235442C7BB14D00AFAF64 /* WireGuard */,
02589DA02C88EB570093940D /* Configuration */,
02589DA22C88EB5D0093940D /* Persistence */,
1E5921C02CF479EE00E15CCA /* FeatureFlags */,
);
productName = NetworkProtectionSystemExtension;
productReference = 4B25375A2A11BE7300610219 /* com.duckduckgo.macos.vpn.network-extension.debug.systemextension */;
Expand Down Expand Up @@ -10172,6 +10181,7 @@
4B5235462C7BB15700AFAF64 /* WireGuard */,
02589D9C2C88E8210093940D /* Configuration */,
02589D9E2C88E8270093940D /* Persistence */,
1E5921BE2CF479E600E15CCA /* FeatureFlags */,
);
productName = NetworkProtectionAppExtension;
productReference = 4B4D603D2A0B290200BCD287 /* NetworkProtectionAppExtension.appex */;
Expand Down Expand Up @@ -10288,6 +10298,7 @@
020807B12C6CFF95006F94C4 /* Configuration */,
C18BF9CF2C736C9100ED6B8A /* Freemium */,
02A15D8F2C88D773001A4237 /* Persistence */,
1E5921C22CF47A0700E15CCA /* FeatureFlags */,
);
productName = DuckDuckGoAgent;
productReference = 9D9AE8D12AAA39A70026E7DC /* DuckDuckGo Personal Information Removal.app */;
Expand Down Expand Up @@ -10315,6 +10326,7 @@
C18BF9D12C736C9700ED6B8A /* Freemium */,
02A15D8B2C88D763001A4237 /* Configuration */,
02A15D8D2C88D76A001A4237 /* Persistence */,
1E5921C42CF47A0F00E15CCA /* FeatureFlags */,
);
productName = DuckDuckGoAgent;
productReference = 9D9AE8F22AAA39D30026E7DC /* DuckDuckGo Personal Information Removal App Store.app */;
Expand Down Expand Up @@ -15205,7 +15217,7 @@
repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit";
requirement = {
kind = exactVersion;
version = 211.1.3;
version = "211.1.3-1";
};
};
9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */ = {
Expand Down Expand Up @@ -15360,6 +15372,22 @@
package = FAE06B199CA1F209B55B34E9 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */;
productName = Crashes;
};
1E5921BE2CF479E600E15CCA /* FeatureFlags */ = {
isa = XCSwiftPackageProductDependency;
productName = FeatureFlags;
};
1E5921C02CF479EE00E15CCA /* FeatureFlags */ = {
isa = XCSwiftPackageProductDependency;
productName = FeatureFlags;
};
1E5921C22CF47A0700E15CCA /* FeatureFlags */ = {
isa = XCSwiftPackageProductDependency;
productName = FeatureFlags;
};
1E5921C42CF47A0F00E15CCA /* FeatureFlags */ = {
isa = XCSwiftPackageProductDependency;
productName = FeatureFlags;
};
1E950E3E2912A10D0051A99B /* ContentBlocking */ = {
isa = XCSwiftPackageProductDependency;
package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */;
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" : "f83b1f5ebd328bc2447d1a3793149bb21037d685",
"version" : "211.1.3"
"revision" : "114cdbfcfae15ad8c7d5e502832e94061aef7cff",
"version" : "211.1.3-1"
}
},
{
Expand Down Expand Up @@ -75,7 +75,7 @@
{
"identity" : "lottie-spm",
"kind" : "remoteSourceControl",
"location" : "https://github.com/airbnb/lottie-spm.git",
"location" : "https://github.com/airbnb/lottie-spm",
"state" : {
"revision" : "1d29eccc24cc8b75bff9f6804155112c0ffc9605",
"version" : "4.4.3"
Expand Down
2 changes: 1 addition & 1 deletion DuckDuckGo/Application/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
onboardingStateMachine = ContextualOnboardingStateMachine()

// Configure Subscription
subscriptionManager = DefaultSubscriptionManager()
subscriptionManager = DefaultSubscriptionManager(featureFlagger: featureFlagger)
subscriptionUIHandler = SubscriptionUIHandler(windowControllersManagerProvider: {
return WindowControllersManager.shared
})
Expand Down
3 changes: 2 additions & 1 deletion DuckDuckGo/Menus/MainMenu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -726,7 +726,8 @@ final class MainMenu: NSMenu {
updatePurchasingPlatform: updatePurchasingPlatform,
currentViewController: { WindowControllersManager.shared.lastKeyMainWindowController?.mainViewController },
openSubscriptionTab: { WindowControllersManager.shared.showTab(with: .subscription($0)) },
subscriptionManager: Application.appDelegate.subscriptionManager)
subscriptionManager: Application.appDelegate.subscriptionManager,
subscriptionUserDefaults: subscriptionUserDefaults)

NSMenuItem(title: "Logging").submenu(setupLoggingMenu())
NSMenuItem(title: "AI Chat").submenu(AIChatDebugMenu())
Expand Down
37 changes: 25 additions & 12 deletions DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ final class MoreOptionsMenu: NSMenu, NSMenuDelegate {
} else {
privacyProItem.submenu = SubscriptionSubMenu(targeting: self,
subscriptionFeatureAvailability: DefaultSubscriptionFeatureAvailability(),
accountManager: accountManager)
subscriptionManager: subscriptionManager)
addItem(privacyProItem)
}
}
Expand Down Expand Up @@ -878,7 +878,7 @@ final class HelpSubMenu: NSMenu {
final class SubscriptionSubMenu: NSMenu, NSMenuDelegate {

var subscriptionFeatureAvailability: SubscriptionFeatureAvailability
var accountManager: AccountManager
var subscriptionManager: SubscriptionManager

var networkProtectionItem: NSMenuItem!
var dataBrokerProtectionItem: NSMenuItem!
Expand All @@ -887,10 +887,10 @@ final class SubscriptionSubMenu: NSMenu, NSMenuDelegate {

init(targeting target: AnyObject,
subscriptionFeatureAvailability: SubscriptionFeatureAvailability,
accountManager: AccountManager) {
subscriptionManager: SubscriptionManager) {

self.subscriptionFeatureAvailability = subscriptionFeatureAvailability
self.accountManager = accountManager
self.subscriptionManager = subscriptionManager

super.init(title: "")

Expand All @@ -901,17 +901,27 @@ final class SubscriptionSubMenu: NSMenu, NSMenuDelegate {

delegate = self

addMenuItems()
Task {
await addMenuItems()
}
}

required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

private func addMenuItems() {
addItem(networkProtectionItem)
addItem(dataBrokerProtectionItem)
addItem(identityTheftRestorationItem)
private func addMenuItems() async {
let features = await subscriptionManager.currentSubscriptionFeatures()

if features.contains(.networkProtection) {
addItem(networkProtectionItem)
}
if features.contains(.dataBrokerProtection) {
addItem(dataBrokerProtectionItem)
}
if features.contains(.identityTheftRestoration) || features.contains(.identityTheftRestorationGlobal) {
addItem(identityTheftRestorationItem)
}
addItem(NSMenuItem.separator())
addItem(subscriptionSettingsItem)
}
Expand Down Expand Up @@ -948,10 +958,10 @@ final class SubscriptionSubMenu: NSMenu, NSMenuDelegate {
}

private func refreshAvailabilityBasedOnEntitlements() {
guard subscriptionFeatureAvailability.isFeatureAvailable, accountManager.isUserAuthenticated else { return }
guard subscriptionFeatureAvailability.isFeatureAvailable, subscriptionManager.accountManager.isUserAuthenticated else { return }

@Sendable func hasEntitlement(for productName: Entitlement.ProductName) async -> Bool {
switch await self.accountManager.hasEntitlement(forProductName: productName) {
switch await self.subscriptionManager.accountManager.hasEntitlement(forProductName: productName) {
case let .success(result):
return result
case .failure:
Expand All @@ -964,7 +974,10 @@ final class SubscriptionSubMenu: NSMenu, NSMenuDelegate {

let isNetworkProtectionItemEnabled = await hasEntitlement(for: .networkProtection)
let isDataBrokerProtectionItemEnabled = await hasEntitlement(for: .dataBrokerProtection)
let isIdentityTheftRestorationItemEnabled = await hasEntitlement(for: .identityTheftRestoration)

let hasIdentityTheftRestoration = await hasEntitlement(for: .identityTheftRestoration)
let hasIdentityTheftRestorationGlobal = await hasEntitlement(for: .identityTheftRestorationGlobal)
let isIdentityTheftRestorationItemEnabled = hasIdentityTheftRestoration || hasIdentityTheftRestorationGlobal

Task { @MainActor in
self.networkProtectionItem.isEnabled = isNetworkProtectionItemEnabled
Expand Down
3 changes: 2 additions & 1 deletion DuckDuckGo/Preferences/View/PreferencesRootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,8 @@ enum Preferences {
return PreferencesSubscriptionModel(openURLHandler: openURL,
userEventHandler: handleUIEvent,
sheetActionHandler: sheetActionHandler,
subscriptionManager: subscriptionManager)
subscriptionManager: subscriptionManager,
featureFlagger: NSApp.delegateTyped.featureFlagger)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ import Foundation
import Subscription
import Common
import PixelKit
import BrowserServicesKit
import FeatureFlags

extension DefaultSubscriptionManager {

// Init the SubscriptionManager using the standard dependencies and configuration, to be used only in the dependencies tree root
public convenience init() {
public convenience init(featureFlagger: FeatureFlagger? = nil) {
// MARK: - Configure Subscription
let subscriptionAppGroup = Bundle.main.appGroup(bundle: .subs)
let subscriptionUserDefaults = UserDefaults(suiteName: subscriptionAppGroup)!
Expand All @@ -36,23 +38,53 @@ extension DefaultSubscriptionManager {
let accessTokenStorage = SubscriptionTokenKeychainStorage(keychainType: .dataProtection(.named(subscriptionAppGroup)))
let subscriptionEndpointService = DefaultSubscriptionEndpointService(currentServiceEnvironment: subscriptionEnvironment.serviceEnvironment)
let authEndpointService = DefaultAuthEndpointService(currentServiceEnvironment: subscriptionEnvironment.serviceEnvironment)
let subscriptionFeatureMappingCache = DefaultSubscriptionFeatureMappingCache(subscriptionEndpointService: subscriptionEndpointService,
userDefaults: subscriptionUserDefaults)

let accountManager = DefaultAccountManager(accessTokenStorage: accessTokenStorage,
entitlementsCache: entitlementsCache,
subscriptionEndpointService: subscriptionEndpointService,
authEndpointService: authEndpointService)

let subscriptionFeatureFlagger: FeatureFlaggerMapping<SubscriptionFeatureFlags> = FeatureFlaggerMapping { feature in
guard let featureFlagger else {
// With no featureFlagger provided there is no gating of features
return feature.defaultState
}

switch feature {
case .isLaunchedROW:
return featureFlagger.isFeatureOn(.isPrivacyProLaunchedROW)
case .isLaunchedROWOverride:
return featureFlagger.isFeatureOn(.isPrivacyProLaunchedROWOverride)
case .usePrivacyProUSARegionOverride:
return (featureFlagger.internalUserDecider.isInternalUser &&
subscriptionEnvironment.serviceEnvironment == .staging &&
subscriptionUserDefaults.storefrontRegionOverride == .usa)
case .usePrivacyProROWRegionOverride:
return (featureFlagger.internalUserDecider.isInternalUser &&
subscriptionEnvironment.serviceEnvironment == .staging &&
subscriptionUserDefaults.storefrontRegionOverride == .restOfWorld)
}
}

if #available(macOS 12.0, *) {
let storePurchaseManager = DefaultStorePurchaseManager()
let storePurchaseManager = DefaultStorePurchaseManager(subscriptionFeatureMappingCache: subscriptionFeatureMappingCache,
subscriptionFeatureFlagger: subscriptionFeatureFlagger)
self.init(storePurchaseManager: storePurchaseManager,
accountManager: accountManager,
subscriptionEndpointService: subscriptionEndpointService,
authEndpointService: authEndpointService,
subscriptionEnvironment: subscriptionEnvironment)
subscriptionFeatureMappingCache: subscriptionFeatureMappingCache,
subscriptionEnvironment: subscriptionEnvironment,
subscriptionFeatureFlagger: subscriptionFeatureFlagger)
} else {
self.init(accountManager: accountManager,
subscriptionEndpointService: subscriptionEndpointService,
authEndpointService: authEndpointService,
subscriptionEnvironment: subscriptionEnvironment)
subscriptionFeatureMappingCache: subscriptionFeatureMappingCache,
subscriptionEnvironment: subscriptionEnvironment,
subscriptionFeatureFlagger: subscriptionFeatureFlagger)
}

accountManager.delegate = self
Expand Down
Loading

0 comments on commit 90e645c

Please sign in to comment.