From fa0211c62d8e20a149c345ece82eff2424330dad Mon Sep 17 00:00:00 2001 From: Michal Smaga Date: Tue, 27 Feb 2024 19:57:44 +0100 Subject: [PATCH] Enable Stripe purchase flow for internal users in non-App Store target (#2228) Task/Issue URL: https://app.asana.com/0/1199230911884351/1206534283150250/f CC: @samsymons @Bunn **Description**: Enable Stripe purchase flow for internal users in non-App Store target. Following the requirements listed in the linked task we do want to enable the Subscription flow for users that are authenticated as internal users and have not started waitlist interaction with VPN or DBP. --- ###### 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) --------- Co-authored-by: Diego Rey Mendez --- Configuration/App/DuckDuckGo.xcconfig | 2 +- .../App/DuckDuckGoPrivacyPro.xcconfig | 2 +- Configuration/Tests/UnitTests.xcconfig | 2 +- DuckDuckGo.xcodeproj/project.pbxproj | 35 ++++- .../xcshareddata/swiftpm/Package.resolved | 8 +- DuckDuckGo/Application/AppDelegate.swift | 7 + .../Images/ITR-Icon.imageset/Contents.json | 16 ++ .../ITR-Icon.imageset/CreditCard-16.pdf | Bin 0 -> 1852 bytes DuckDuckGo/Common/Localizables/UserText.swift | 2 + .../Utilities/UserDefaultsWrapper.swift | 5 + DuckDuckGo/Menus/MainMenu.swift | 13 +- .../View/AddressBarTextField.swift | 10 +- .../NavigationBar/View/MoreOptionsMenu.swift | 25 +++- .../View/NavigationBarViewController.swift | 18 ++- .../Model/PreferencesSection.swift | 22 +-- .../View/PreferencesRootView.swift | 12 ++ .../SubscriptionFeatureAvailability.swift | 69 +++++++++ .../SubscriptionPagesUserScript.swift | 3 +- DuckDuckGo/Tab/UserScripts/UserScripts.swift | 10 +- .../DataBrokerProtection/Package.swift | 2 +- LocalPackages/LoginItems/Package.swift | 2 +- .../NetworkProtectionMac/Package.swift | 2 +- LocalPackages/PixelKit/Package.swift | 2 +- LocalPackages/SubscriptionUI/Package.swift | 2 +- .../DebugMenu/SubscriptionDebugMenu.swift | 141 ++++++++++++++---- .../PreferencesSubscriptionModel.swift | 14 +- LocalPackages/SwiftUIExtensions/Package.swift | 2 +- LocalPackages/SyncUI/Package.swift | 2 +- .../SystemExtensionManager/Package.swift | 2 +- LocalPackages/XPCHelper/Package.swift | 2 +- .../CapturingOptionsButtonMenuDelegate.swift | 7 + 31 files changed, 353 insertions(+), 88 deletions(-) create mode 100644 DuckDuckGo/Assets.xcassets/Images/ITR-Icon.imageset/Contents.json create mode 100644 DuckDuckGo/Assets.xcassets/Images/ITR-Icon.imageset/CreditCard-16.pdf create mode 100644 DuckDuckGo/Subscription/SubscriptionFeatureAvailability.swift diff --git a/Configuration/App/DuckDuckGo.xcconfig b/Configuration/App/DuckDuckGo.xcconfig index 6fd934d750..710327265e 100644 --- a/Configuration/App/DuckDuckGo.xcconfig +++ b/Configuration/App/DuckDuckGo.xcconfig @@ -26,7 +26,7 @@ CODE_SIGN_IDENTITY[sdk=macosx*] = Developer ID Application CODE_SIGN_IDENTITY[config=Debug][sdk=macosx*] = Apple Development CODE_SIGN_IDENTITY[config=CI][sdk=macosx*] = -FEATURE_FLAGS = FEEDBACK NETWORK_PROTECTION SPARKLE DBP +FEATURE_FLAGS = FEEDBACK NETWORK_PROTECTION SPARKLE DBP SUBSCRIPTION STRIPE PRODUCT_NAME_PREFIX = DuckDuckGo diff --git a/Configuration/App/DuckDuckGoPrivacyPro.xcconfig b/Configuration/App/DuckDuckGoPrivacyPro.xcconfig index 2df4ce5a2a..3c4e68369f 100644 --- a/Configuration/App/DuckDuckGoPrivacyPro.xcconfig +++ b/Configuration/App/DuckDuckGoPrivacyPro.xcconfig @@ -21,6 +21,6 @@ #include "DuckDuckGo.xcconfig" -FEATURE_FLAGS = FEEDBACK NETWORK_PROTECTION SPARKLE SUBSCRIPTION DBP STRIPE +FEATURE_FLAGS = FEEDBACK NETWORK_PROTECTION SPARKLE SUBSCRIPTION DBP STRIPE SUBSCRIPTION_OVERRIDE_ENABLED PRODUCT_NAME = $(PRODUCT_NAME_PREFIX) Privacy Pro PRODUCT_MODULE_NAME = $(PRIVACY_PRO_PRODUCT_MODULE_NAME_OVERRIDE:default=$(DEFAULT_PRODUCT_MODULE_NAME)) diff --git a/Configuration/Tests/UnitTests.xcconfig b/Configuration/Tests/UnitTests.xcconfig index de9f4010a0..b09dee6dc1 100644 --- a/Configuration/Tests/UnitTests.xcconfig +++ b/Configuration/Tests/UnitTests.xcconfig @@ -17,7 +17,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES -FEATURE_FLAGS = FEEDBACK NETWORK_PROTECTION DBP +FEATURE_FLAGS = FEEDBACK NETWORK_PROTECTION DBP SUBSCRIPTION INFOPLIST_FILE = UnitTests/Info.plist PRODUCT_BUNDLE_IDENTIFIER = com.duckduckgo.macos.browser.DuckDuckGoTests diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 1f4048af8f..8cc3fcf61c 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -114,6 +114,11 @@ 1E950E3F2912A10D0051A99B /* ContentBlocking in Frameworks */ = {isa = PBXBuildFile; productRef = 1E950E3E2912A10D0051A99B /* ContentBlocking */; }; 1E950E412912A10D0051A99B /* PrivacyDashboard in Frameworks */ = {isa = PBXBuildFile; productRef = 1E950E402912A10D0051A99B /* PrivacyDashboard */; }; 1E950E432912A10D0051A99B /* UserScript in Frameworks */ = {isa = PBXBuildFile; productRef = 1E950E422912A10D0051A99B /* UserScript */; }; + 1EA7B8D32B7E078C000330A4 /* SubscriptionUI in Frameworks */ = {isa = PBXBuildFile; productRef = 1EA7B8D22B7E078C000330A4 /* SubscriptionUI */; }; + 1EA7B8D52B7E078C000330A4 /* Subscription in Frameworks */ = {isa = PBXBuildFile; productRef = 1EA7B8D42B7E078C000330A4 /* Subscription */; }; + 1EA7B8D82B7E1283000330A4 /* SubscriptionFeatureAvailability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EA7B8D72B7E1283000330A4 /* SubscriptionFeatureAvailability.swift */; }; + 1EA7B8D92B7E1283000330A4 /* SubscriptionFeatureAvailability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EA7B8D72B7E1283000330A4 /* SubscriptionFeatureAvailability.swift */; }; + 1EA7B8DA2B7E1283000330A4 /* SubscriptionFeatureAvailability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EA7B8D72B7E1283000330A4 /* SubscriptionFeatureAvailability.swift */; }; 1ED910D52B63BFB300936947 /* IdentityTheftRestorationPagesUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED910D42B63BFB300936947 /* IdentityTheftRestorationPagesUserScript.swift */; }; 1ED910D62B63BFB300936947 /* IdentityTheftRestorationPagesUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED910D42B63BFB300936947 /* IdentityTheftRestorationPagesUserScript.swift */; }; 1ED910D72B63BFB300936947 /* IdentityTheftRestorationPagesUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ED910D42B63BFB300936947 /* IdentityTheftRestorationPagesUserScript.swift */; }; @@ -3355,6 +3360,7 @@ 1E7E2E8F29029A2A00C01B54 /* ContentBlockingRulesUpdateObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentBlockingRulesUpdateObserver.swift; sourceTree = ""; }; 1E7E2E932902AC0E00C01B54 /* PrivacyDashboardPermissionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyDashboardPermissionHandler.swift; sourceTree = ""; }; 1E862A882A9FC01200F84D4B /* SubscriptionUI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = SubscriptionUI; sourceTree = ""; }; + 1EA7B8D72B7E1283000330A4 /* SubscriptionFeatureAvailability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionFeatureAvailability.swift; sourceTree = ""; }; 1ED910D42B63BFB300936947 /* IdentityTheftRestorationPagesUserScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityTheftRestorationPagesUserScript.swift; sourceTree = ""; }; 310E79BE294A19A8007C49E8 /* FireproofingReferenceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FireproofingReferenceTests.swift; sourceTree = ""; }; 311B262628E73E0A00FD181A /* TabShadowConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabShadowConfig.swift; sourceTree = ""; }; @@ -4684,6 +4690,7 @@ buildActionMask = 2147483647; files = ( 373FB4B12B4D6C42004C88D6 /* PreferencesViews in Frameworks */, + 1EA7B8D32B7E078C000330A4 /* SubscriptionUI in Frameworks */, B6F7128129F681EB00594A45 /* QuickLookUI.framework in Frameworks */, 9DB6E7242AA0DC5800A17F3C /* LoginItems in Frameworks */, EE7295E32A545B9A008C0991 /* NetworkProtection in Frameworks */, @@ -4699,6 +4706,7 @@ 37269EFB2B332F9E005E8E46 /* Common in Frameworks */, 4B2AAAF529E70DEA0026AFC0 /* Lottie in Frameworks */, AA06B6B72672AF8100F541C5 /* Sparkle in Frameworks */, + 1EA7B8D52B7E078C000330A4 /* Subscription in Frameworks */, B6B77BE8297973D4001E68A1 /* Navigation in Frameworks */, 3739326729AE4B42009346AE /* DDGSync in Frameworks */, 7BA59C9B2AE18B49009A97B1 /* SystemExtensionManager in Frameworks */, @@ -4908,6 +4916,14 @@ path = Services; sourceTree = ""; }; + 1EA7B8D62B7E124E000330A4 /* Subscription */ = { + isa = PBXGroup; + children = ( + 1EA7B8D72B7E1283000330A4 /* SubscriptionFeatureAvailability.swift */, + ); + path = Subscription; + sourceTree = ""; + }; 3171D6DC2889B6700068632A /* CookieManaged */ = { isa = PBXGroup; children = ( @@ -6788,6 +6804,7 @@ 4B677422255DBEB800025BD8 /* SmarterEncryption */, B68458AE25C7E75100DC17B6 /* StateRestoration */, B6A9E44E26142AF90067D1B9 /* Statistics */, + 1EA7B8D62B7E124E000330A4 /* Subscription */, AACB8E7224A4C8BC005F2218 /* Suggestions */, 3775913429AB99DA00E26367 /* Sync */, AA86491B24D837DE001BABEE /* Tab */, @@ -8779,6 +8796,8 @@ 3722177F2B3337FE00B8E9C2 /* TestUtils */, 373FB4B02B4D6C42004C88D6 /* PreferencesViews */, 7BA076BA2B65D61400D7FB72 /* NetworkProtectionProxy */, + 1EA7B8D22B7E078C000330A4 /* SubscriptionUI */, + 1EA7B8D42B7E078C000330A4 /* Subscription */, ); productName = DuckDuckGo; productReference = AA585D7E248FD31100E9A3E2 /* DuckDuckGo.app */; @@ -9979,6 +9998,7 @@ B66260E829ACD0C900E9E3EE /* DuckPlayerTabExtension.swift in Sources */, 3706FBAA293F65D500E42796 /* HoverUserScript.swift in Sources */, 3706FBAC293F65D500E42796 /* MainMenuActions.swift in Sources */, + 1EA7B8D92B7E1283000330A4 /* SubscriptionFeatureAvailability.swift in Sources */, 4BF97AD92B43C5C000EB4240 /* Bundle+VPN.swift in Sources */, 3706FBAE293F65D500E42796 /* DataImport.swift in Sources */, 3706FBAF293F65D500E42796 /* FireproofDomains.xcdatamodeld in Sources */, @@ -11221,6 +11241,7 @@ 4B957AF12AC7AE700062CA31 /* AppStateRestorationManager.swift in Sources */, 4B957AF22AC7AE700062CA31 /* DailyPixel.swift in Sources */, 4B957AF32AC7AE700062CA31 /* NavigationHotkeyHandler.swift in Sources */, + 1EA7B8DA2B7E1283000330A4 /* SubscriptionFeatureAvailability.swift in Sources */, 4B957AF42AC7AE700062CA31 /* ClickToLoadUserScript.swift in Sources */, 4B957AF52AC7AE700062CA31 /* WindowControllersManager.swift in Sources */, 4B957AF62AC7AE700062CA31 /* FireAnimationView.swift in Sources */, @@ -12252,6 +12273,7 @@ 4BA1A6C2258B0A1300F6F690 /* ContiguousBytesExtension.swift in Sources */, B6A22B622B1E29D000ECD2BA /* DataImportSummaryViewModel.swift in Sources */, 37534CA8281198CD002621E7 /* AdjacentItemEnumerator.swift in Sources */, + 1EA7B8D82B7E1283000330A4 /* SubscriptionFeatureAvailability.swift in Sources */, 987799F62999996B005D8EB6 /* BookmarkDatabase.swift in Sources */, 4BE53374286E39F10019DBFD /* ChromiumKeychainPrompt.swift in Sources */, B6553692268440D700085A79 /* WKProcessPool+GeolocationProvider.swift in Sources */, @@ -13507,7 +13529,7 @@ repositoryURL = "https://github.com/duckduckgo/OpenSSL-XCFramework"; requirement = { kind = exactVersion; - version = 3.1.2000; + version = 3.1.4000; }; }; 4B2AAAF329E70DEA0026AFC0 /* XCRemoteSwiftPackageReference "lottie-ios" */ = { @@ -13555,7 +13577,7 @@ repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 111.0.2; + version = 111.1.1; }; }; AA06B6B52672AF8100F541C5 /* XCRemoteSwiftPackageReference "Sparkle" */ = { @@ -13617,6 +13639,15 @@ package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = UserScript; }; + 1EA7B8D22B7E078C000330A4 /* SubscriptionUI */ = { + isa = XCSwiftPackageProductDependency; + productName = SubscriptionUI; + }; + 1EA7B8D42B7E078C000330A4 /* Subscription */ = { + isa = XCSwiftPackageProductDependency; + package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = Subscription; + }; 312978892B64131200B67619 /* DataBrokerProtection */ = { isa = XCSwiftPackageProductDependency; productName = DataBrokerProtection; diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1a0ae4a0e6..ef6b11ab0e 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/BrowserServicesKit", "state" : { - "revision" : "04c35220aa94bd005171086acccadd677400e7d5", - "version" : "111.0.2" + "revision" : "e3140633373b14b3d1fca25b098869cdaa753913", + "version" : "111.1.1" } }, { @@ -68,8 +68,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/OpenSSL-XCFramework", "state" : { - "revision" : "bb7bfc010ef4d2e7913c343663b167e2a984ac79", - "version" : "3.1.2000" + "revision" : "b75ab2c0405860bb2616db71b9a456acb118c21a", + "version" : "3.1.4000" } }, { diff --git a/DuckDuckGo/Application/AppDelegate.swift b/DuckDuckGo/Application/AppDelegate.swift index 95f1317564..1fea2a1b84 100644 --- a/DuckDuckGo/Application/AppDelegate.swift +++ b/DuckDuckGo/Application/AppDelegate.swift @@ -188,6 +188,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, FileDownloadManagerDel featureFlagger = DefaultFeatureFlagger(internalUserDecider: internalUserDecider, privacyConfig: AppPrivacyFeatures.shared.contentBlocking.privacyConfigurationManager.privacyConfig) + } func applicationWillFinishLaunching(_ notification: Notification) { @@ -281,6 +282,12 @@ final class AppDelegate: NSObject, NSApplicationDelegate, FileDownloadManagerDel #if SUBSCRIPTION Task { + var defaultEnvironment = SubscriptionPurchaseEnvironment.ServiceEnvironment.default + + let currentEnvironment = UserDefaultsWrapper(key: .subscriptionEnvironment, + defaultValue: defaultEnvironment).wrappedValue + SubscriptionPurchaseEnvironment.currentServiceEnvironment = currentEnvironment + #if STRIPE SubscriptionPurchaseEnvironment.current = .stripe #else diff --git a/DuckDuckGo/Assets.xcassets/Images/ITR-Icon.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/Images/ITR-Icon.imageset/Contents.json new file mode 100644 index 0000000000..b7b6f95c5f --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/Images/ITR-Icon.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "CreditCard-16.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/DuckDuckGo/Assets.xcassets/Images/ITR-Icon.imageset/CreditCard-16.pdf b/DuckDuckGo/Assets.xcassets/Images/ITR-Icon.imageset/CreditCard-16.pdf new file mode 100644 index 0000000000000000000000000000000000000000..31b7892f479121360481a5989ee6fd38d77d7734 GIT binary patch literal 1852 zcmZvd-*3|}5XayBSKP~_4QWZvPMk!VCb5no1c=e`Ht`TrZ!1a@XfibT>p45l*;|)~ z%K7yCy`Q}vU0+_EVp0l$kiqcdrvNxRgY$D?`i*@RGUn+g)89`Ipa`z5)i~`jM0^~rU{)@^x|q3;G_X9Ia}-Qk%E^bz ztRv>LV#d`d=dUzn?hDT`SS`=EW}SEts25B{s>*_FsFo3xiO#aBssVF%sxy1?QYe=@ zfmu%|NeCmuj%GpCHDV08DlQLl@Yrn^AnTgAA4#Ln?y{Bu1@p zF*}uDNa4-dQJ#Tfk&rX%&pW9g6voF=0V%#xLs#28aRtpFUxcW%3`4=4dvA)E)R+}2 z2UL8Li%?W>W^ut8c0wjp(OlE0GsML9#Wp55VH6dd?Kti@!U)p*P&#>0QH^AjBZ=ln zewexbW0lLCSSBUip#v(+OPZ&|b+r!tViLEDA&IG^(p4dCXZ&5;zzT~}2M4dj*3!t| zy%D|mD(s^3TWnc*cDv215G5jS!kjT}$nms<_}WP0*w7xDtHn>{foJc`z~MZC__kJ}A} z3O0i^EPhtQBi+S=_(vjzxuQ{cOrvQ%zMIqf-VYG0Ac?i z{o3sVJtG?W=Ll)l9Uz*a7gl*)zC=YCE>8Pq+nK$v-TQD! { case syncDidPresentFaviconsFetcherOnboarding = "sync.did-present-favicons-fetcher-onboarding" case syncDidMigrateToImprovedListsHandling = "sync.did-migrate-to-improved-lists-handling" case syncDidShowSyncPausedByFeatureFlagAlert = "sync.did-show-sync-paused-by-feature-flag-alert" + + // Subscription + + case subscriptionInternalTesting = "subscription.internal-testing-enabled" + case subscriptionEnvironment = "subscription.environment" } enum RemovedKeys: String, CaseIterable { diff --git a/DuckDuckGo/Menus/MainMenu.swift b/DuckDuckGo/Menus/MainMenu.swift index 7cf629775f..7d97cd4284 100644 --- a/DuckDuckGo/Menus/MainMenu.swift +++ b/DuckDuckGo/Menus/MainMenu.swift @@ -30,6 +30,7 @@ import NetworkProtection #endif #if SUBSCRIPTION +import Subscription import SubscriptionUI #endif @@ -591,7 +592,17 @@ import SubscriptionUI NSMenuItem(title: "Trigger Fatal Error", action: #selector(MainViewController.triggerFatalError)) #if SUBSCRIPTION - SubscriptionDebugMenu(currentViewController: { + let currentEnvironmentWrapper = UserDefaultsWrapper(key: .subscriptionEnvironment, defaultValue: SubscriptionPurchaseEnvironment.ServiceEnvironment.default) + let isInternalTestingWrapper = UserDefaultsWrapper(key: .subscriptionInternalTesting, defaultValue: false) + + SubscriptionDebugMenu(currentEnvironment: { currentEnvironmentWrapper.wrappedValue.rawValue }, + updateEnvironment: { + guard let newEnvironment = SubscriptionPurchaseEnvironment.ServiceEnvironment(rawValue: $0) else { return } + currentEnvironmentWrapper.wrappedValue = newEnvironment + SubscriptionPurchaseEnvironment.currentServiceEnvironment = newEnvironment }, + isInternalTestingEnabled: { isInternalTestingWrapper.wrappedValue }, + updateInternalTestingFlag: { isInternalTestingWrapper.wrappedValue = $0 }, + currentViewController: { WindowControllersManager.shared.lastKeyMainWindowController?.mainViewController }) #endif diff --git a/DuckDuckGo/NavigationBar/View/AddressBarTextField.swift b/DuckDuckGo/NavigationBar/View/AddressBarTextField.swift index 39f964117c..9db691f061 100644 --- a/DuckDuckGo/NavigationBar/View/AddressBarTextField.swift +++ b/DuckDuckGo/NavigationBar/View/AddressBarTextField.swift @@ -317,10 +317,12 @@ final class AddressBarTextField: NSTextField { #endif #if SUBSCRIPTION - if providedUrl.isChild(of: URL.subscriptionBaseURL) || providedUrl.isChild(of: URL.identityTheftRestoration) { - selectedTabViewModel.updateAddressBarStrings() - self.window?.makeFirstResponder(nil) - return + if DefaultSubscriptionFeatureAvailability().isFeatureAvailable() { + if providedUrl.isChild(of: URL.subscriptionBaseURL) || providedUrl.isChild(of: URL.identityTheftRestoration) { + selectedTabViewModel.updateAddressBarStrings() + self.window?.makeFirstResponder(nil) + return + } } #endif diff --git a/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift b/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift index 45e2f941a4..86a7559e38 100644 --- a/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift +++ b/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift @@ -48,6 +48,7 @@ protocol OptionsButtonMenuDelegate: AnyObject { #endif #if SUBSCRIPTION func optionsButtonMenuRequestedSubscriptionPurchasePage(_ menu: NSMenu) + func optionsButtonMenuRequestedIdentityTheftRestoration(_ menu: NSMenu) #endif } @@ -244,6 +245,10 @@ final class MoreOptionsMenu: NSMenu { @objc func openSubscriptionPurchasePage(_ sender: NSMenuItem) { actionDelegate?.optionsButtonMenuRequestedSubscriptionPurchasePage(self) } + + @objc func openIdentityTheftRestoration(_ sender: NSMenuItem) { + actionDelegate?.optionsButtonMenuRequestedIdentityTheftRestoration(self) + } #endif @objc func findInPage(_ sender: NSMenuItem) { @@ -304,13 +309,13 @@ final class MoreOptionsMenu: NSMenu { var items: [NSMenuItem] = [] #if SUBSCRIPTION - if AccountManager().isUserAuthenticated { - items.append(contentsOf: makeActiveSubscriptionItems()) - } else if SubscriptionPurchaseEnvironment.canPurchase { + if DefaultSubscriptionFeatureAvailability().isFeatureAvailable() && !AccountManager().isUserAuthenticated { items.append(contentsOf: makeInactiveSubscriptionItems()) + } else { + items.append(contentsOf: makeActiveSubscriptionItems()) // this adds NETP and DBP only if conditionally enabled } #else - items.append(contentsOf: makeActiveSubscriptionItems()) // this only adds NETP and DBP (if enabled) + items.append(contentsOf: makeActiveSubscriptionItems()) // this adds NETP and DBP only if conditionally enabled #endif if !items.isEmpty { @@ -355,9 +360,19 @@ final class MoreOptionsMenu: NSMenu { } else { DefaultDataBrokerProtectionFeatureVisibility().disableAndDeleteForWaitlistUsers() } - #endif // DBP +#if SUBSCRIPTION + if AccountManager().isUserAuthenticated { + let identityTheftRestorationItem = NSMenuItem(title: UserText.identityTheftRestorationOptionsMenuItem, + action: #selector(openIdentityTheftRestoration), + keyEquivalent: "") + .targetting(self) + .withImage(NSImage(named: "ITR-Icon")) + items.append(identityTheftRestorationItem) + } +#endif + return items } diff --git a/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift b/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift index c2facfea75..09d8a11a56 100644 --- a/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift +++ b/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift @@ -330,12 +330,14 @@ final class NavigationBarViewController: NSViewController { } #if SUBSCRIPTION - let accountManager = AccountManager() - let networkProtectionTokenStorage = NetworkProtectionKeychainTokenStore() + if DefaultSubscriptionFeatureAvailability().isFeatureAvailable() { + let accountManager = AccountManager() + let networkProtectionTokenStorage = NetworkProtectionKeychainTokenStore() - if accountManager.accessToken != nil && (try? networkProtectionTokenStorage.fetchToken()) == nil { - print("[NetP Subscription] Got access token but not auth token, meaning token exchange failed") - return + if accountManager.accessToken != nil && (try? networkProtectionTokenStorage.fetchToken()) == nil { + print("[NetP Subscription] Got access token but not auth token, meaning token exchange failed") + return + } } #endif @@ -370,7 +372,7 @@ final class NavigationBarViewController: NSViewController { #if NETWORK_PROTECTION func listenToVPNToggleNotifications() { - vpnToggleCancellable = NotificationCenter.default.publisher(for: .ToggleNetworkProtectionInMainWindow).sink { [weak self] _ in + vpnToggleCancellable = NotificationCenter.default.publisher(for: .ToggleNetworkProtectionInMainWindow).receive(on: DispatchQueue.main).sink { [weak self] _ in guard self?.view.window?.isKeyWindow == true else { return } @@ -1033,6 +1035,10 @@ extension NavigationBarViewController: OptionsButtonMenuDelegate { func optionsButtonMenuRequestedSubscriptionPurchasePage(_ menu: NSMenu) { WindowControllersManager.shared.showTab(with: .subscription(.subscriptionPurchase)) } + + func optionsButtonMenuRequestedIdentityTheftRestoration(_ menu: NSMenu) { + WindowControllersManager.shared.showTab(with: .subscription(.identityTheftRestoration)) + } #endif } diff --git a/DuckDuckGo/Preferences/Model/PreferencesSection.swift b/DuckDuckGo/Preferences/Model/PreferencesSection.swift index 1ffe35666c..90c1030f82 100644 --- a/DuckDuckGo/Preferences/Model/PreferencesSection.swift +++ b/DuckDuckGo/Preferences/Model/PreferencesSection.swift @@ -30,27 +30,21 @@ struct PreferencesSection: Hashable, Identifiable { @MainActor static func defaultSections(includingDuckPlayer: Bool, includingSync: Bool, includingVPN: Bool) -> [PreferencesSection] { let regularPanes: [PreferencePaneIdentifier] = { + + var panes: [PreferencePaneIdentifier] = [.general, .appearance, .privacy, .autofill, .downloads] + + if DefaultSubscriptionFeatureAvailability().isFeatureAvailable() { #if SUBSCRIPTION - var panes: [PreferencePaneIdentifier] = [.privacy, .subscription, .general, .appearance, .autofill, .downloads] + panes = [.privacy, .subscription, .general, .appearance, .autofill, .downloads] +#endif + } - if NSApp.delegateTyped.internalUserDecider.isInternalUser { + if includingSync { if let generalIndex = panes.firstIndex(of: .general) { panes.insert(.sync, at: generalIndex + 1) } } - if !AccountManager().isUserAuthenticated && !SubscriptionPurchaseEnvironment.canPurchase { - if let subscriptionIndex = panes.firstIndex(of: .subscription) { - panes.remove(at: subscriptionIndex) - } - } -#else - var panes: [PreferencePaneIdentifier] = [.general, .appearance, .privacy, .autofill, .downloads] - - if includingSync { - panes.insert(.sync, at: 1) - } -#endif if includingDuckPlayer { panes.append(.duckPlayer) } diff --git a/DuckDuckGo/Preferences/View/PreferencesRootView.swift b/DuckDuckGo/Preferences/View/PreferencesRootView.swift index 2e298e833d..517ac73e9e 100644 --- a/DuckDuckGo/Preferences/View/PreferencesRootView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesRootView.swift @@ -117,11 +117,23 @@ enum Preferences { } } + let openVPN: () -> Void = { + NotificationCenter.default.post(name: .ToggleNetworkProtectionInMainWindow, object: self, userInfo: nil) + } + + let openDBP: () -> Void = { + DispatchQueue.main.async { + WindowControllersManager.shared.showTab(with: .dataBrokerProtection) + } + } + let sheetActionHandler = SubscriptionAccessActionHandlers(restorePurchases: { SubscriptionPagesUseSubscriptionFeature.startAppStoreRestoreFlow() }, openURLHandler: openURL, goToSyncPreferences: { self.model.selectPane(.sync) }) return PreferencesSubscriptionModel(openURLHandler: openURL, + openVPNHandler: openVPN, + openDBPHandler: openDBP, sheetActionHandler: sheetActionHandler) } #endif diff --git a/DuckDuckGo/Subscription/SubscriptionFeatureAvailability.swift b/DuckDuckGo/Subscription/SubscriptionFeatureAvailability.swift new file mode 100644 index 0000000000..6599bd0ee9 --- /dev/null +++ b/DuckDuckGo/Subscription/SubscriptionFeatureAvailability.swift @@ -0,0 +1,69 @@ +// +// SubscriptionFeatureAvailability.swift +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import AppKit + +#if SUBSCRIPTION +import Subscription +#endif + +#if NETWORK_PROTECTION +import NetworkProtection +#endif + +protocol SubscriptionFeatureAvailability { + func isFeatureAvailable() -> Bool +} + +struct DefaultSubscriptionFeatureAvailability: SubscriptionFeatureAvailability { + + func isFeatureAvailable() -> Bool { +#if SUBSCRIPTION_OVERRIDE_ENABLED + return true +#elseif SUBSCRIPTION + print("isUserAuthenticated: [\(AccountManager().isUserAuthenticated)] | isSubscriptionInternalTestingEnabled: [\(isSubscriptionInternalTestingEnabled)] isInternalUser: [\(isInternalUser)] | isVPNActivated: [\(isVPNActivated)] | isDBPActivated: [\(isDBPActivated)]") + return AccountManager().isUserAuthenticated || (isSubscriptionInternalTestingEnabled && isInternalUser && !isVPNActivated && !isDBPActivated) +#else + return false +#endif + } + + private var isSubscriptionInternalTestingEnabled: Bool { + UserDefaultsWrapper(key: .subscriptionInternalTesting, defaultValue: false).wrappedValue + } + + private var isInternalUser: Bool { + NSApp.delegateTyped.internalUserDecider.isInternalUser + } + + private var isVPNActivated: Bool { +#if NETWORK_PROTECTION + return NetworkProtectionKeychainTokenStore().isFeatureActivated +#else + return false +#endif + } + + private var isDBPActivated: Bool { +#if DBP + return DataBrokerProtectionManager.shared.dataManager.fetchProfile(ignoresCache: true) != nil +#else + return false +#endif + } +} diff --git a/DuckDuckGo/Tab/UserScripts/SubscriptionPagesUserScript.swift b/DuckDuckGo/Tab/UserScripts/SubscriptionPagesUserScript.swift index e534521982..2c2ce64d58 100644 --- a/DuckDuckGo/Tab/UserScripts/SubscriptionPagesUserScript.swift +++ b/DuckDuckGo/Tab/UserScripts/SubscriptionPagesUserScript.swift @@ -302,9 +302,10 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature { case .appTrackingProtection: NotificationCenter.default.post(name: .openAppTrackingProtection, object: self, userInfo: nil) case .vpn: - NotificationCenter.default.post(name: .openVPN, object: self, userInfo: nil) + NotificationCenter.default.post(name: .ToggleNetworkProtectionInMainWindow, object: self, userInfo: nil) case .personalInformationRemoval: NotificationCenter.default.post(name: .openPersonalInformationRemoval, object: self, userInfo: nil) + await WindowControllersManager.shared.showTab(with: .dataBrokerProtection) case .identityTheftRestoration: await WindowControllersManager.shared.showTab(with: .subscription(.identityTheftRestoration)) } diff --git a/DuckDuckGo/Tab/UserScripts/UserScripts.swift b/DuckDuckGo/Tab/UserScripts/UserScripts.swift index c85ecf43ba..4cde694a07 100644 --- a/DuckDuckGo/Tab/UserScripts/UserScripts.swift +++ b/DuckDuckGo/Tab/UserScripts/UserScripts.swift @@ -87,11 +87,13 @@ final class UserScripts: UserScriptsProvider { } #if SUBSCRIPTION - subscriptionPagesUserScript.registerSubfeature(delegate: SubscriptionPagesUseSubscriptionFeature()) - userScripts.append(subscriptionPagesUserScript) + if DefaultSubscriptionFeatureAvailability().isFeatureAvailable() { + subscriptionPagesUserScript.registerSubfeature(delegate: SubscriptionPagesUseSubscriptionFeature()) + userScripts.append(subscriptionPagesUserScript) - identityTheftRestorationPagesUserScript.registerSubfeature(delegate: IdentityTheftRestorationPagesFeature()) - userScripts.append(identityTheftRestorationPagesUserScript) + identityTheftRestorationPagesUserScript.registerSubfeature(delegate: IdentityTheftRestorationPagesFeature()) + userScripts.append(identityTheftRestorationPagesUserScript) + } #endif } diff --git a/LocalPackages/DataBrokerProtection/Package.swift b/LocalPackages/DataBrokerProtection/Package.swift index f27728497f..4d25c04c82 100644 --- a/LocalPackages/DataBrokerProtection/Package.swift +++ b/LocalPackages/DataBrokerProtection/Package.swift @@ -29,7 +29,7 @@ let package = Package( targets: ["DataBrokerProtection"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "111.0.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "111.1.1"), .package(path: "../PixelKit"), .package(path: "../SwiftUIExtensions"), .package(path: "../XPCHelper") diff --git a/LocalPackages/LoginItems/Package.swift b/LocalPackages/LoginItems/Package.swift index 614324a607..ce9f3e231b 100644 --- a/LocalPackages/LoginItems/Package.swift +++ b/LocalPackages/LoginItems/Package.swift @@ -13,7 +13,7 @@ let package = Package( ), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "111.0.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "111.1.1"), ], targets: [ .target( diff --git a/LocalPackages/NetworkProtectionMac/Package.swift b/LocalPackages/NetworkProtectionMac/Package.swift index 08093c2623..d7a03b3840 100644 --- a/LocalPackages/NetworkProtectionMac/Package.swift +++ b/LocalPackages/NetworkProtectionMac/Package.swift @@ -31,7 +31,7 @@ let package = Package( .library(name: "NetworkProtectionUI", targets: ["NetworkProtectionUI"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "111.0.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "111.1.1"), .package(path: "../XPCHelper"), .package(path: "../SwiftUIExtensions"), .package(path: "../LoginItems") diff --git a/LocalPackages/PixelKit/Package.swift b/LocalPackages/PixelKit/Package.swift index 1dabea6b8c..b67d4426dd 100644 --- a/LocalPackages/PixelKit/Package.swift +++ b/LocalPackages/PixelKit/Package.swift @@ -20,7 +20,7 @@ let package = Package( ) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "111.0.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "111.1.1"), ], targets: [ .target( diff --git a/LocalPackages/SubscriptionUI/Package.swift b/LocalPackages/SubscriptionUI/Package.swift index 16ee741162..fecf8da6f9 100644 --- a/LocalPackages/SubscriptionUI/Package.swift +++ b/LocalPackages/SubscriptionUI/Package.swift @@ -12,7 +12,7 @@ let package = Package( targets: ["SubscriptionUI"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "111.0.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "111.1.1"), .package(path: "../SwiftUIExtensions") ], targets: [ diff --git a/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/DebugMenu/SubscriptionDebugMenu.swift b/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/DebugMenu/SubscriptionDebugMenu.swift index fcba4739e3..b3c8823cc2 100644 --- a/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/DebugMenu/SubscriptionDebugMenu.swift +++ b/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/DebugMenu/SubscriptionDebugMenu.swift @@ -21,6 +21,11 @@ import Subscription public final class SubscriptionDebugMenu: NSMenuItem { + var currentEnvironment: () -> String + var updateEnvironment: (String) -> Void + var isInternalTestingEnabled: () -> Bool + var updateInternalTestingFlag: (Bool) -> Void + var currentViewController: () -> NSViewController? private let accountManager = AccountManager() @@ -38,13 +43,22 @@ public final class SubscriptionDebugMenu: NSMenuItem { fatalError("init(coder:) has not been implemented") } - public init(currentViewController: @escaping () -> NSViewController?) { + public init(currentEnvironment: @escaping () -> String, + updateEnvironment: @escaping (String) -> Void, + isInternalTestingEnabled: @escaping () -> Bool, + updateInternalTestingFlag: @escaping (Bool) -> Void, + currentViewController: @escaping () -> NSViewController?) { + self.currentEnvironment = currentEnvironment + self.updateEnvironment = updateEnvironment + self.isInternalTestingEnabled = isInternalTestingEnabled + self.updateInternalTestingFlag = updateInternalTestingFlag self.currentViewController = currentViewController + super.init(title: "Subscription", action: nil, keyEquivalent: "") - self.submenu = submenuItem + self.submenu = makeSubmenu() } - private lazy var submenuItem: NSMenu = { + private func makeSubmenu() -> NSMenu { let menu = NSMenu(title: "") menu.addItem(NSMenuItem(title: "Simulate Subscription Active State (fake token)", action: #selector(simulateSubscriptionActiveState), target: self)) @@ -60,11 +74,49 @@ public final class SubscriptionDebugMenu: NSMenuItem { menu.addItem(NSMenuItem(title: "Sync App Store AppleID Account (re- sign-in)", action: #selector(syncAppleIDAccount), target: self)) menu.addItem(NSMenuItem(title: "Purchase Subscription from App Store", action: #selector(showPurchaseView), target: self)) } + + let environmentItem = NSMenuItem(title: "Environment", action: nil, target: nil) + environmentItem.submenu = makeEnvironmentSubmenu() + menu.addItem(environmentItem) + menu.addItem(.separator()) - menu.addItem(NSMenuItem(title: "Error message #1", action: #selector(testError1), target: self)) - menu.addItem(NSMenuItem(title: "Error message #2", action: #selector(testError2), target: self)) + + let internalTestingItem = NSMenuItem(title: "Internal testing", action: #selector(toggleInternalTesting), target: self) + internalTestingItem.state = isInternalTestingEnabled() ? .on : .off + menu.addItem(internalTestingItem) + + return menu + } + + private func makeEnvironmentSubmenu() -> NSMenu { + let menu = NSMenu(title: "Select environment:") + + let currentEnvironment = currentEnvironment() + + let stagingItem = NSMenuItem(title: "Staging", action: #selector(setEnvironmentToStaging), target: self) + stagingItem.state = currentEnvironment == "staging" ? .on : .off + if currentEnvironment == "staging" { + stagingItem.isEnabled = false + stagingItem.action = nil + stagingItem.target = nil + } + menu.addItem(stagingItem) + + let productionItem = NSMenuItem(title: "Production", action: #selector(setEnvironmentToProduction), target: self) + productionItem.state = currentEnvironment == "production" ? .on : .off + if currentEnvironment == "production" { + productionItem.isEnabled = false + productionItem.action = nil + productionItem.target = nil + } + menu.addItem(productionItem) + return menu - }() + } + + private func refreshSubmenu() { + self.submenu = makeSubmenu() + } @objc func simulateSubscriptionActiveState() { @@ -137,6 +189,32 @@ public final class SubscriptionDebugMenu: NSMenuItem { } } + @IBAction func showPurchaseView(_ sender: Any?) { + if #available(macOS 12.0, *) { + currentViewController()?.presentAsSheet(DebugPurchaseViewController()) + } + } + + @IBAction func setEnvironmentToStaging(_ sender: Any?) { + askAndUpdateEnvironment(to: "staging") + } + + @IBAction func setEnvironmentToProduction(_ sender: Any?) { + askAndUpdateEnvironment(to: "production") + } + + private func askAndUpdateEnvironment(to newEnvironmentString: String) { + let alert = makeAlert(title: "Are you sure you want to change the environment to \(newEnvironmentString.capitalized)", + message: "Please make sure you have manually removed your current active Subscription and reset all related features. \nYou may also need to change environment of related features e.g. Network Protection's to a matching one.", + buttonNames: ["Yes", "No"]) + let response = alert.runModal() + + guard case .alertFirstButtonReturn = response else { return } + + updateEnvironment(newEnvironmentString) + refreshSubmenu() + } + @objc func restorePurchases(_ sender: Any?) { if #available(macOS 12.0, *) { @@ -147,44 +225,43 @@ public final class SubscriptionDebugMenu: NSMenuItem { } @objc - func testError1(_ sender: Any?) { + func toggleInternalTesting(_ sender: Any?) { Task { @MainActor in - let alert = NSAlert() - alert.messageText = "Something Went Wrong" - alert.informativeText = "The App Store was not able to process your purchase. Please try again later." - alert.addButton(withTitle: "OK") - alert.runModal() + let currentValue = isInternalTestingEnabled() + let shouldShowAlert = currentValue == false + + if shouldShowAlert { + let alert = makeAlert(title: "Are you sure you want to enable internal testing", + message: "Only enable this option if you are participating in internal testing and have been requested to do so.", + buttonNames: ["Yes", "No"]) + let response = alert.runModal() + + guard case .alertFirstButtonReturn = response else { return } + } + + updateInternalTestingFlag(!currentValue) + self.refreshSubmenu() } } - @objc - func testError2(_ sender: Any?) { + private func showAlert(title: String, message: String? = nil) { Task { @MainActor in - let alert = NSAlert() - alert.messageText = "Subscription Not Found" - alert.informativeText = "The subscription associated with this Apple ID is no longer active." - alert.addButton(withTitle: "View Plans") - alert.addButton(withTitle: "Cancel") + let alert = makeAlert(title: title, message: message) alert.runModal() } } - @IBAction func showPurchaseView(_ sender: Any?) { - if #available(macOS 12.0, *) { - currentViewController()?.presentAsSheet(DebugPurchaseViewController()) + private func makeAlert(title: String, message: String? = nil, buttonNames: [String] = ["Ok"]) -> NSAlert{ + let alert = NSAlert() + alert.messageText = title + if let message = message { + alert.informativeText = message } - } - private func showAlert(title: String, message: String? = nil) { - Task { @MainActor in - let alert = NSAlert() - alert.messageText = title - if let message = message { - alert.informativeText = message - } - alert.addButton(withTitle: "OK") - alert.runModal() + for buttonName in buttonNames { + alert.addButton(withTitle: buttonName) } + return alert } } diff --git a/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/Preferences/PreferencesSubscriptionModel.swift b/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/Preferences/PreferencesSubscriptionModel.swift index 22ab992e79..2364975249 100644 --- a/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/Preferences/PreferencesSubscriptionModel.swift +++ b/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/Preferences/PreferencesSubscriptionModel.swift @@ -31,6 +31,8 @@ public final class PreferencesSubscriptionModel: ObservableObject { private let accountManager: AccountManager private let openURLHandler: (URL) -> Void + private let openVPNHandler: () -> Void + private let openDBPHandler: () -> Void private let sheetActionHandler: SubscriptionAccessActionHandlers private var fetchSubscriptionDetailsTask: Task<(), Never>? @@ -38,9 +40,15 @@ public final class PreferencesSubscriptionModel: ObservableObject { private var signInObserver: Any? private var signOutObserver: Any? - public init(accountManager: AccountManager = AccountManager(), openURLHandler: @escaping (URL) -> Void, sheetActionHandler: SubscriptionAccessActionHandlers) { + public init(accountManager: AccountManager = AccountManager(), + openURLHandler: @escaping (URL) -> Void, + openVPNHandler: @escaping () -> Void, + openDBPHandler: @escaping () -> Void, + sheetActionHandler: SubscriptionAccessActionHandlers) { self.accountManager = accountManager self.openURLHandler = openURLHandler + self.openVPNHandler = openVPNHandler + self.openDBPHandler = openDBPHandler self.sheetActionHandler = sheetActionHandler self.isUserAuthenticated = accountManager.isUserAuthenticated @@ -151,12 +159,12 @@ public final class PreferencesSubscriptionModel: ObservableObject { @MainActor func openVPN() { - NotificationCenter.default.post(name: .openVPN, object: self, userInfo: nil) + openVPNHandler() } @MainActor func openPersonalInformationRemoval() { - NotificationCenter.default.post(name: .openPersonalInformationRemoval, object: self, userInfo: nil) + openDBPHandler() } @MainActor diff --git a/LocalPackages/SwiftUIExtensions/Package.swift b/LocalPackages/SwiftUIExtensions/Package.swift index 8e91cb3a94..d9c2ead7e0 100644 --- a/LocalPackages/SwiftUIExtensions/Package.swift +++ b/LocalPackages/SwiftUIExtensions/Package.swift @@ -11,7 +11,7 @@ let package = Package( .library(name: "PreferencesViews", targets: ["PreferencesViews"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "111.0.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "111.1.1"), ], targets: [ .target( diff --git a/LocalPackages/SyncUI/Package.swift b/LocalPackages/SyncUI/Package.swift index 39495c5fe5..6e815be716 100644 --- a/LocalPackages/SyncUI/Package.swift +++ b/LocalPackages/SyncUI/Package.swift @@ -14,7 +14,7 @@ let package = Package( ], dependencies: [ .package(path: "../SwiftUIExtensions"), - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "111.0.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "111.1.1"), ], targets: [ .target( diff --git a/LocalPackages/SystemExtensionManager/Package.swift b/LocalPackages/SystemExtensionManager/Package.swift index d3ec7edb2b..69bdfde1b8 100644 --- a/LocalPackages/SystemExtensionManager/Package.swift +++ b/LocalPackages/SystemExtensionManager/Package.swift @@ -16,7 +16,7 @@ let package = Package( ), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "111.0.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "111.1.1"), ], targets: [ // Targets are the basic building blocks of a package, defining a module or a test suite. diff --git a/LocalPackages/XPCHelper/Package.swift b/LocalPackages/XPCHelper/Package.swift index b2db3d7597..d909c2db38 100644 --- a/LocalPackages/XPCHelper/Package.swift +++ b/LocalPackages/XPCHelper/Package.swift @@ -30,7 +30,7 @@ let package = Package( .library(name: "XPCHelper", targets: ["XPCHelper"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "111.0.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "111.1.1"), ], targets: [ .target( diff --git a/UnitTests/Menus/Mocks/CapturingOptionsButtonMenuDelegate.swift b/UnitTests/Menus/Mocks/CapturingOptionsButtonMenuDelegate.swift index f0ab2bc3d8..e37582c4fa 100644 --- a/UnitTests/Menus/Mocks/CapturingOptionsButtonMenuDelegate.swift +++ b/UnitTests/Menus/Mocks/CapturingOptionsButtonMenuDelegate.swift @@ -84,4 +84,11 @@ class CapturingOptionsButtonMenuDelegate: OptionsButtonMenuDelegate { } + func optionsButtonMenuRequestedSubscriptionPurchasePage(_ menu: NSMenu) { + + } + + func optionsButtonMenuRequestedIdentityTheftRestoration(_ menu: NSMenu) { + + } }