Skip to content
This repository was archived by the owner on Feb 24, 2025. It is now read-only.

Commit 90e645c

Browse files
authored
Client displays correct subscription (#3581)
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)
1 parent 3a48d6a commit 90e645c

27 files changed

+449
-225
lines changed

DuckDuckGo.xcodeproj/project.pbxproj

+29-1
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,10 @@
205205
1E2BEAE52C8B00B5002741A3 /* SubscriptionPagesUseSubscriptionFeatureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E2BEAE32C8B00B5002741A3 /* SubscriptionPagesUseSubscriptionFeatureTests.swift */; };
206206
1E559BB12BBCA9F1002B4AF6 /* RedirectNavigationResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E559BB02BBCA9F1002B4AF6 /* RedirectNavigationResponder.swift */; };
207207
1E559BB22BBCA9F1002B4AF6 /* RedirectNavigationResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E559BB02BBCA9F1002B4AF6 /* RedirectNavigationResponder.swift */; };
208+
1E5921BF2CF479E600E15CCA /* FeatureFlags in Frameworks */ = {isa = PBXBuildFile; productRef = 1E5921BE2CF479E600E15CCA /* FeatureFlags */; };
209+
1E5921C12CF479EE00E15CCA /* FeatureFlags in Frameworks */ = {isa = PBXBuildFile; productRef = 1E5921C02CF479EE00E15CCA /* FeatureFlags */; };
210+
1E5921C32CF47A0700E15CCA /* FeatureFlags in Frameworks */ = {isa = PBXBuildFile; productRef = 1E5921C22CF47A0700E15CCA /* FeatureFlags */; };
211+
1E5921C52CF47A0F00E15CCA /* FeatureFlags in Frameworks */ = {isa = PBXBuildFile; productRef = 1E5921C42CF47A0F00E15CCA /* FeatureFlags */; };
208212
1E7E2E9029029A2A00C01B54 /* ContentBlockingRulesUpdateObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E7E2E8F29029A2A00C01B54 /* ContentBlockingRulesUpdateObserver.swift */; };
209213
1E7E2E942902AC0E00C01B54 /* PrivacyDashboardPermissionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E7E2E932902AC0E00C01B54 /* PrivacyDashboardPermissionHandler.swift */; };
210214
1E950E3F2912A10D0051A99B /* ContentBlocking in Frameworks */ = {isa = PBXBuildFile; productRef = 1E950E3E2912A10D0051A99B /* ContentBlocking */; };
@@ -5085,6 +5089,7 @@
50855089
buildActionMask = 2147483647;
50865090
files = (
50875091
4B5235452C7BB14D00AFAF64 /* WireGuard in Frameworks */,
5092+
1E5921C12CF479EE00E15CCA /* FeatureFlags in Frameworks */,
50885093
37269F012B332FC8005E8E46 /* Common in Frameworks */,
50895094
9D9DE57B2C63AA1F00D20B15 /* AppKitExtensions in Frameworks */,
50905095
EE7295E92A545BC4008C0991 /* NetworkProtection in Frameworks */,
@@ -5160,6 +5165,7 @@
51605165
isa = PBXFrameworksBuildPhase;
51615166
buildActionMask = 2147483647;
51625167
files = (
5168+
1E5921BF2CF479E600E15CCA /* FeatureFlags in Frameworks */,
51635169
02589D9F2C88E8270093940D /* Persistence in Frameworks */,
51645170
F1DF95E52BD1807C0045E591 /* Subscription in Frameworks */,
51655171
4B5235472C7BB15700AFAF64 /* WireGuard in Frameworks */,
@@ -5225,6 +5231,7 @@
52255231
9DC5FAC52C6B8A010011F068 /* AppKitExtensions in Frameworks */,
52265232
020807B22C6CFF95006F94C4 /* Configuration in Frameworks */,
52275233
F1D0428E2BFB9F9C00A31506 /* Subscription in Frameworks */,
5234+
1E5921C32CF47A0700E15CCA /* FeatureFlags in Frameworks */,
52285235
C18BF9D02C736C9100ED6B8A /* Freemium in Frameworks */,
52295236
9D9AE8F92AAA3AD00026E7DC /* DataBrokerProtection in Frameworks */,
52305237
);
@@ -5239,6 +5246,7 @@
52395246
315A023F2B6421AE00BFA577 /* Networking in Frameworks */,
52405247
9DC5FAC72C6B8A080011F068 /* AppKitExtensions in Frameworks */,
52415248
F1D042902BFB9FA300A31506 /* Subscription in Frameworks */,
5249+
1E5921C52CF47A0F00E15CCA /* FeatureFlags in Frameworks */,
52425250
C18BF9D22C736C9700ED6B8A /* Freemium in Frameworks */,
52435251
9D9AE8FB2AAA3AD90026E7DC /* DataBrokerProtection in Frameworks */,
52445252
);
@@ -10039,6 +10047,7 @@
1003910047
4B5235442C7BB14D00AFAF64 /* WireGuard */,
1004010048
02589DA02C88EB570093940D /* Configuration */,
1004110049
02589DA22C88EB5D0093940D /* Persistence */,
10050+
1E5921C02CF479EE00E15CCA /* FeatureFlags */,
1004210051
);
1004310052
productName = NetworkProtectionSystemExtension;
1004410053
productReference = 4B25375A2A11BE7300610219 /* com.duckduckgo.macos.vpn.network-extension.debug.systemextension */;
@@ -10172,6 +10181,7 @@
1017210181
4B5235462C7BB15700AFAF64 /* WireGuard */,
1017310182
02589D9C2C88E8210093940D /* Configuration */,
1017410183
02589D9E2C88E8270093940D /* Persistence */,
10184+
1E5921BE2CF479E600E15CCA /* FeatureFlags */,
1017510185
);
1017610186
productName = NetworkProtectionAppExtension;
1017710187
productReference = 4B4D603D2A0B290200BCD287 /* NetworkProtectionAppExtension.appex */;
@@ -10288,6 +10298,7 @@
1028810298
020807B12C6CFF95006F94C4 /* Configuration */,
1028910299
C18BF9CF2C736C9100ED6B8A /* Freemium */,
1029010300
02A15D8F2C88D773001A4237 /* Persistence */,
10301+
1E5921C22CF47A0700E15CCA /* FeatureFlags */,
1029110302
);
1029210303
productName = DuckDuckGoAgent;
1029310304
productReference = 9D9AE8D12AAA39A70026E7DC /* DuckDuckGo Personal Information Removal.app */;
@@ -10315,6 +10326,7 @@
1031510326
C18BF9D12C736C9700ED6B8A /* Freemium */,
1031610327
02A15D8B2C88D763001A4237 /* Configuration */,
1031710328
02A15D8D2C88D76A001A4237 /* Persistence */,
10329+
1E5921C42CF47A0F00E15CCA /* FeatureFlags */,
1031810330
);
1031910331
productName = DuckDuckGoAgent;
1032010332
productReference = 9D9AE8F22AAA39D30026E7DC /* DuckDuckGo Personal Information Removal App Store.app */;
@@ -15205,7 +15217,7 @@
1520515217
repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit";
1520615218
requirement = {
1520715219
kind = exactVersion;
15208-
version = 211.1.3;
15220+
version = "211.1.3-1";
1520915221
};
1521015222
};
1521115223
9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */ = {
@@ -15360,6 +15372,22 @@
1536015372
package = FAE06B199CA1F209B55B34E9 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */;
1536115373
productName = Crashes;
1536215374
};
15375+
1E5921BE2CF479E600E15CCA /* FeatureFlags */ = {
15376+
isa = XCSwiftPackageProductDependency;
15377+
productName = FeatureFlags;
15378+
};
15379+
1E5921C02CF479EE00E15CCA /* FeatureFlags */ = {
15380+
isa = XCSwiftPackageProductDependency;
15381+
productName = FeatureFlags;
15382+
};
15383+
1E5921C22CF47A0700E15CCA /* FeatureFlags */ = {
15384+
isa = XCSwiftPackageProductDependency;
15385+
productName = FeatureFlags;
15386+
};
15387+
1E5921C42CF47A0F00E15CCA /* FeatureFlags */ = {
15388+
isa = XCSwiftPackageProductDependency;
15389+
productName = FeatureFlags;
15390+
};
1536315391
1E950E3E2912A10D0051A99B /* ContentBlocking */ = {
1536415392
isa = XCSwiftPackageProductDependency;
1536515393
package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */;

DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

+3-3
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232
"kind" : "remoteSourceControl",
3333
"location" : "https://github.com/duckduckgo/BrowserServicesKit",
3434
"state" : {
35-
"revision" : "f83b1f5ebd328bc2447d1a3793149bb21037d685",
36-
"version" : "211.1.3"
35+
"revision" : "114cdbfcfae15ad8c7d5e502832e94061aef7cff",
36+
"version" : "211.1.3-1"
3737
}
3838
},
3939
{
@@ -75,7 +75,7 @@
7575
{
7676
"identity" : "lottie-spm",
7777
"kind" : "remoteSourceControl",
78-
"location" : "https://github.com/airbnb/lottie-spm.git",
78+
"location" : "https://github.com/airbnb/lottie-spm",
7979
"state" : {
8080
"revision" : "1d29eccc24cc8b75bff9f6804155112c0ffc9605",
8181
"version" : "4.4.3"

DuckDuckGo/Application/AppDelegate.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
285285
onboardingStateMachine = ContextualOnboardingStateMachine()
286286

287287
// Configure Subscription
288-
subscriptionManager = DefaultSubscriptionManager()
288+
subscriptionManager = DefaultSubscriptionManager(featureFlagger: featureFlagger)
289289
subscriptionUIHandler = SubscriptionUIHandler(windowControllersManagerProvider: {
290290
return WindowControllersManager.shared
291291
})

DuckDuckGo/Menus/MainMenu.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,8 @@ final class MainMenu: NSMenu {
726726
updatePurchasingPlatform: updatePurchasingPlatform,
727727
currentViewController: { WindowControllersManager.shared.lastKeyMainWindowController?.mainViewController },
728728
openSubscriptionTab: { WindowControllersManager.shared.showTab(with: .subscription($0)) },
729-
subscriptionManager: Application.appDelegate.subscriptionManager)
729+
subscriptionManager: Application.appDelegate.subscriptionManager,
730+
subscriptionUserDefaults: subscriptionUserDefaults)
730731

731732
NSMenuItem(title: "Logging").submenu(setupLoggingMenu())
732733
NSMenuItem(title: "AI Chat").submenu(AIChatDebugMenu())

DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift

+25-12
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ final class MoreOptionsMenu: NSMenu, NSMenuDelegate {
431431
} else {
432432
privacyProItem.submenu = SubscriptionSubMenu(targeting: self,
433433
subscriptionFeatureAvailability: DefaultSubscriptionFeatureAvailability(),
434-
accountManager: accountManager)
434+
subscriptionManager: subscriptionManager)
435435
addItem(privacyProItem)
436436
}
437437
}
@@ -878,7 +878,7 @@ final class HelpSubMenu: NSMenu {
878878
final class SubscriptionSubMenu: NSMenu, NSMenuDelegate {
879879

880880
var subscriptionFeatureAvailability: SubscriptionFeatureAvailability
881-
var accountManager: AccountManager
881+
var subscriptionManager: SubscriptionManager
882882

883883
var networkProtectionItem: NSMenuItem!
884884
var dataBrokerProtectionItem: NSMenuItem!
@@ -887,10 +887,10 @@ final class SubscriptionSubMenu: NSMenu, NSMenuDelegate {
887887

888888
init(targeting target: AnyObject,
889889
subscriptionFeatureAvailability: SubscriptionFeatureAvailability,
890-
accountManager: AccountManager) {
890+
subscriptionManager: SubscriptionManager) {
891891

892892
self.subscriptionFeatureAvailability = subscriptionFeatureAvailability
893-
self.accountManager = accountManager
893+
self.subscriptionManager = subscriptionManager
894894

895895
super.init(title: "")
896896

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

902902
delegate = self
903903

904-
addMenuItems()
904+
Task {
905+
await addMenuItems()
906+
}
905907
}
906908

907909
required init(coder: NSCoder) {
908910
fatalError("init(coder:) has not been implemented")
909911
}
910912

911-
private func addMenuItems() {
912-
addItem(networkProtectionItem)
913-
addItem(dataBrokerProtectionItem)
914-
addItem(identityTheftRestorationItem)
913+
private func addMenuItems() async {
914+
let features = await subscriptionManager.currentSubscriptionFeatures()
915+
916+
if features.contains(.networkProtection) {
917+
addItem(networkProtectionItem)
918+
}
919+
if features.contains(.dataBrokerProtection) {
920+
addItem(dataBrokerProtectionItem)
921+
}
922+
if features.contains(.identityTheftRestoration) || features.contains(.identityTheftRestorationGlobal) {
923+
addItem(identityTheftRestorationItem)
924+
}
915925
addItem(NSMenuItem.separator())
916926
addItem(subscriptionSettingsItem)
917927
}
@@ -948,10 +958,10 @@ final class SubscriptionSubMenu: NSMenu, NSMenuDelegate {
948958
}
949959

950960
private func refreshAvailabilityBasedOnEntitlements() {
951-
guard subscriptionFeatureAvailability.isFeatureAvailable, accountManager.isUserAuthenticated else { return }
961+
guard subscriptionFeatureAvailability.isFeatureAvailable, subscriptionManager.accountManager.isUserAuthenticated else { return }
952962

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

965975
let isNetworkProtectionItemEnabled = await hasEntitlement(for: .networkProtection)
966976
let isDataBrokerProtectionItemEnabled = await hasEntitlement(for: .dataBrokerProtection)
967-
let isIdentityTheftRestorationItemEnabled = await hasEntitlement(for: .identityTheftRestoration)
977+
978+
let hasIdentityTheftRestoration = await hasEntitlement(for: .identityTheftRestoration)
979+
let hasIdentityTheftRestorationGlobal = await hasEntitlement(for: .identityTheftRestorationGlobal)
980+
let isIdentityTheftRestorationItemEnabled = hasIdentityTheftRestoration || hasIdentityTheftRestorationGlobal
968981

969982
Task { @MainActor in
970983
self.networkProtectionItem.isEnabled = isNetworkProtectionItemEnabled

DuckDuckGo/Preferences/View/PreferencesRootView.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,8 @@ enum Preferences {
201201
return PreferencesSubscriptionModel(openURLHandler: openURL,
202202
userEventHandler: handleUIEvent,
203203
sheetActionHandler: sheetActionHandler,
204-
subscriptionManager: subscriptionManager)
204+
subscriptionManager: subscriptionManager,
205+
featureFlagger: NSApp.delegateTyped.featureFlagger)
205206
}
206207
}
207208
}

DuckDuckGo/Subscription/SubscriptionManager+StandardConfiguration.swift

+36-4
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@ import Foundation
2020
import Subscription
2121
import Common
2222
import PixelKit
23+
import BrowserServicesKit
24+
import FeatureFlags
2325

2426
extension DefaultSubscriptionManager {
2527

2628
// Init the SubscriptionManager using the standard dependencies and configuration, to be used only in the dependencies tree root
27-
public convenience init() {
29+
public convenience init(featureFlagger: FeatureFlagger? = nil) {
2830
// MARK: - Configure Subscription
2931
let subscriptionAppGroup = Bundle.main.appGroup(bundle: .subs)
3032
let subscriptionUserDefaults = UserDefaults(suiteName: subscriptionAppGroup)!
@@ -36,23 +38,53 @@ extension DefaultSubscriptionManager {
3638
let accessTokenStorage = SubscriptionTokenKeychainStorage(keychainType: .dataProtection(.named(subscriptionAppGroup)))
3739
let subscriptionEndpointService = DefaultSubscriptionEndpointService(currentServiceEnvironment: subscriptionEnvironment.serviceEnvironment)
3840
let authEndpointService = DefaultAuthEndpointService(currentServiceEnvironment: subscriptionEnvironment.serviceEnvironment)
41+
let subscriptionFeatureMappingCache = DefaultSubscriptionFeatureMappingCache(subscriptionEndpointService: subscriptionEndpointService,
42+
userDefaults: subscriptionUserDefaults)
43+
3944
let accountManager = DefaultAccountManager(accessTokenStorage: accessTokenStorage,
4045
entitlementsCache: entitlementsCache,
4146
subscriptionEndpointService: subscriptionEndpointService,
4247
authEndpointService: authEndpointService)
4348

49+
let subscriptionFeatureFlagger: FeatureFlaggerMapping<SubscriptionFeatureFlags> = FeatureFlaggerMapping { feature in
50+
guard let featureFlagger else {
51+
// With no featureFlagger provided there is no gating of features
52+
return feature.defaultState
53+
}
54+
55+
switch feature {
56+
case .isLaunchedROW:
57+
return featureFlagger.isFeatureOn(.isPrivacyProLaunchedROW)
58+
case .isLaunchedROWOverride:
59+
return featureFlagger.isFeatureOn(.isPrivacyProLaunchedROWOverride)
60+
case .usePrivacyProUSARegionOverride:
61+
return (featureFlagger.internalUserDecider.isInternalUser &&
62+
subscriptionEnvironment.serviceEnvironment == .staging &&
63+
subscriptionUserDefaults.storefrontRegionOverride == .usa)
64+
case .usePrivacyProROWRegionOverride:
65+
return (featureFlagger.internalUserDecider.isInternalUser &&
66+
subscriptionEnvironment.serviceEnvironment == .staging &&
67+
subscriptionUserDefaults.storefrontRegionOverride == .restOfWorld)
68+
}
69+
}
70+
4471
if #available(macOS 12.0, *) {
45-
let storePurchaseManager = DefaultStorePurchaseManager()
72+
let storePurchaseManager = DefaultStorePurchaseManager(subscriptionFeatureMappingCache: subscriptionFeatureMappingCache,
73+
subscriptionFeatureFlagger: subscriptionFeatureFlagger)
4674
self.init(storePurchaseManager: storePurchaseManager,
4775
accountManager: accountManager,
4876
subscriptionEndpointService: subscriptionEndpointService,
4977
authEndpointService: authEndpointService,
50-
subscriptionEnvironment: subscriptionEnvironment)
78+
subscriptionFeatureMappingCache: subscriptionFeatureMappingCache,
79+
subscriptionEnvironment: subscriptionEnvironment,
80+
subscriptionFeatureFlagger: subscriptionFeatureFlagger)
5181
} else {
5282
self.init(accountManager: accountManager,
5383
subscriptionEndpointService: subscriptionEndpointService,
5484
authEndpointService: authEndpointService,
55-
subscriptionEnvironment: subscriptionEnvironment)
85+
subscriptionFeatureMappingCache: subscriptionFeatureMappingCache,
86+
subscriptionEnvironment: subscriptionEnvironment,
87+
subscriptionFeatureFlagger: subscriptionFeatureFlagger)
5688
}
5789

5890
accountManager.delegate = self

0 commit comments

Comments
 (0)