diff --git a/DuckDuckGo-macOS.xcodeproj/project.pbxproj b/DuckDuckGo-macOS.xcodeproj/project.pbxproj index 3c1c0021df..04be4a306f 100644 --- a/DuckDuckGo-macOS.xcodeproj/project.pbxproj +++ b/DuckDuckGo-macOS.xcodeproj/project.pbxproj @@ -1839,6 +1839,10 @@ 7B0694982B6E980F00FA4DBA /* VPNProxyLauncher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B0694972B6E980F00FA4DBA /* VPNProxyLauncher.swift */; }; 7B09CBA92BA4BE8100CF245B /* NetworkProtectionPixelEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B09CBA72BA4BE7000CF245B /* NetworkProtectionPixelEventTests.swift */; }; 7B09CBAA2BA4BE8200CF245B /* NetworkProtectionPixelEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B09CBA72BA4BE7000CF245B /* NetworkProtectionPixelEventTests.swift */; }; + 7B0A6DFA2D47CCDF00FDFDC2 /* ExcludedAppsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B0A6DF82D47CCDF00FDFDC2 /* ExcludedAppsViewController.swift */; }; + 7B0A6DFB2D47CCDF00FDFDC2 /* ExcludedApps.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7B0A6DF72D47CCDF00FDFDC2 /* ExcludedApps.storyboard */; }; + 7B0A6DFC2D47CCDF00FDFDC2 /* ExcludedAppsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B0A6DF82D47CCDF00FDFDC2 /* ExcludedAppsViewController.swift */; }; + 7B0A6DFD2D47CCDF00FDFDC2 /* ExcludedApps.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7B0A6DF72D47CCDF00FDFDC2 /* ExcludedApps.storyboard */; }; 7B1459552B7D438F00047F2C /* VPNProxyLauncher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B0694972B6E980F00FA4DBA /* VPNProxyLauncher.swift */; }; 7B1459572B7D43E500047F2C /* NetworkProtectionProxy in Frameworks */ = {isa = PBXBuildFile; productRef = 7B1459562B7D43E500047F2C /* NetworkProtectionProxy */; }; 7B1E819E27C8874900FF0E60 /* ContentOverlayPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B1E819B27C8874900FF0E60 /* ContentOverlayPopover.swift */; }; @@ -1862,6 +1866,8 @@ 7B31FD8C2AD125620086AA24 /* NetworkProtectionIPC in Frameworks */ = {isa = PBXBuildFile; productRef = 7B31FD8B2AD125620086AA24 /* NetworkProtectionIPC */; }; 7B3618C22ADE75C8000D6154 /* NetworkProtectionNavBarPopoverManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B3618C12ADE75C8000D6154 /* NetworkProtectionNavBarPopoverManager.swift */; }; 7B37C7A52BAA32A50062546A /* Subscription in Frameworks */ = {isa = PBXBuildFile; productRef = 7B37C7A42BAA32A50062546A /* Subscription */; }; + 7B3A16422D4B7A9600D4C869 /* AppInfoRetriever in Frameworks */ = {isa = PBXBuildFile; productRef = 7B3A16412D4B7A9600D4C869 /* AppInfoRetriever */; }; + 7B3A16442D4B7A9C00D4C869 /* AppInfoRetriever in Frameworks */ = {isa = PBXBuildFile; productRef = 7B3A16432D4B7A9C00D4C869 /* AppInfoRetriever */; }; 7B430EA12A71411A00BAC4A1 /* NetworkProtectionSimulateFailureMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B430EA02A71411A00BAC4A1 /* NetworkProtectionSimulateFailureMenu.swift */; }; 7B430EA22A71411A00BAC4A1 /* NetworkProtectionSimulateFailureMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B430EA02A71411A00BAC4A1 /* NetworkProtectionSimulateFailureMenu.swift */; }; 7B4C5CF52BE51D640007A164 /* VPNUninstallerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B4C5CF42BE51D640007A164 /* VPNUninstallerTests.swift */; }; @@ -1900,6 +1906,8 @@ 7B8FDD242CDD88D300720907 /* FeatureFlags in Frameworks */ = {isa = PBXBuildFile; productRef = 7B8FDD232CDD88D300720907 /* FeatureFlags */; }; 7B8FDD262CDD88D900720907 /* FeatureFlags in Frameworks */ = {isa = PBXBuildFile; productRef = 7B8FDD252CDD88D900720907 /* FeatureFlags */; }; 7B934C412A866DD400FC8F9C /* UserDefaults+NetworkProtectionShared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B934C402A866DD400FC8F9C /* UserDefaults+NetworkProtectionShared.swift */; }; + 7B93A68B2D4A5AF200E9FFC1 /* ExcludedAppsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B93A68A2D4A5AEC00E9FFC1 /* ExcludedAppsModel.swift */; }; + 7B93A68C2D4A5AF200E9FFC1 /* ExcludedAppsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B93A68A2D4A5AEC00E9FFC1 /* ExcludedAppsModel.swift */; }; 7B97CD592B7E0B57004FEF43 /* NetworkProtectionProxy in Frameworks */ = {isa = PBXBuildFile; productRef = 7B97CD582B7E0B57004FEF43 /* NetworkProtectionProxy */; }; 7B97CD5B2B7E0B85004FEF43 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 7B97CD5A2B7E0B85004FEF43 /* Common */; }; 7B97CD5C2B7E0BBB004FEF43 /* UserDefaultsWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85C6A29525CC1FFD00EEB5F1 /* UserDefaultsWrapper.swift */; }; @@ -4247,6 +4255,8 @@ 7B05829D2A812AC000AC3F7C /* NetworkProtectionOnboardingMenu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkProtectionOnboardingMenu.swift; sourceTree = ""; }; 7B0694972B6E980F00FA4DBA /* VPNProxyLauncher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNProxyLauncher.swift; sourceTree = ""; }; 7B09CBA72BA4BE7000CF245B /* NetworkProtectionPixelEventTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionPixelEventTests.swift; sourceTree = ""; }; + 7B0A6DF72D47CCDF00FDFDC2 /* ExcludedApps.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ExcludedApps.storyboard; sourceTree = ""; }; + 7B0A6DF82D47CCDF00FDFDC2 /* ExcludedAppsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExcludedAppsViewController.swift; sourceTree = ""; }; 7B1E819B27C8874900FF0E60 /* ContentOverlayPopover.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentOverlayPopover.swift; sourceTree = ""; }; 7B1E819C27C8874900FF0E60 /* ContentOverlay.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = ContentOverlay.storyboard; sourceTree = ""; }; 7B1E819D27C8874900FF0E60 /* ContentOverlayViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentOverlayViewController.swift; sourceTree = ""; }; @@ -4255,6 +4265,7 @@ 7B2DDCF72A93A8BB0039D884 /* NetworkProtectionAppEvents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionAppEvents.swift; sourceTree = ""; }; 7B2E52242A5FEC09000C6D39 /* NetworkProtectionAgentNotificationsPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkProtectionAgentNotificationsPresenter.swift; sourceTree = ""; }; 7B3618C12ADE75C8000D6154 /* NetworkProtectionNavBarPopoverManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionNavBarPopoverManager.swift; sourceTree = ""; }; + 7B3A16402D4B798000D4C869 /* AppInfoRetriever */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = AppInfoRetriever; sourceTree = ""; }; 7B430EA02A71411A00BAC4A1 /* NetworkProtectionSimulateFailureMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionSimulateFailureMenu.swift; sourceTree = ""; }; 7B4C5CF42BE51D640007A164 /* VPNUninstallerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNUninstallerTests.swift; sourceTree = ""; }; 7B4CE8DA26F02108009134B1 /* UI Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "UI Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -4280,6 +4291,7 @@ 7B9167A82C09E88800322310 /* AppLauncher */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = AppLauncher; sourceTree = ""; }; 7B934C3D2A866CFF00FC8F9C /* NetworkProtectionOnboardingMenu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkProtectionOnboardingMenu.swift; sourceTree = ""; }; 7B934C402A866DD400FC8F9C /* UserDefaults+NetworkProtectionShared.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserDefaults+NetworkProtectionShared.swift"; sourceTree = ""; }; + 7B93A68A2D4A5AEC00E9FFC1 /* ExcludedAppsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExcludedAppsModel.swift; sourceTree = ""; }; 7BA7CC0B2AD11D1E0042E5CE /* DuckDuckGoVPNAppStore.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DuckDuckGoVPNAppStore.xcconfig; sourceTree = ""; }; 7BA7CC0C2AD11D1E0042E5CE /* DuckDuckGoVPN.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DuckDuckGoVPN.xcconfig; sourceTree = ""; }; 7BA7CC0E2AD11DC80042E5CE /* DuckDuckGoVPNAppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DuckDuckGoVPNAppDelegate.swift; sourceTree = ""; }; @@ -5196,6 +5208,7 @@ 3706FCAA293F65D500E42796 /* UserScript in Frameworks */, 5641734D2CFE169400F4B716 /* PixelExperimentKit in Frameworks */, 85E2BBD02B8F534A00DBEC7A /* History in Frameworks */, + 7B3A16422D4B7A9600D4C869 /* AppInfoRetriever in Frameworks */, 4BF97AD52B43C43F00EB4240 /* NetworkProtection in Frameworks */, 4B94B79B2D4731F00014AAB8 /* SyncUI-macOS in Frameworks */, 560EB9372C78974C0080DBC8 /* Onboarding in Frameworks */, @@ -5454,6 +5467,7 @@ CBC83E3629B63D380008E19C /* Configuration in Frameworks */, 7B31FD8C2AD125620086AA24 /* NetworkProtectionIPC in Frameworks */, 371209232C232E66003ADF3D /* RemoteMessaging in Frameworks */, + 7B3A16442D4B7A9C00D4C869 /* AppInfoRetriever in Frameworks */, 37269EFB2B332F9E005E8E46 /* Common in Frameworks */, AA06B6B72672AF8100F541C5 /* Sparkle in Frameworks */, 37EE81412D00DBC40068034A /* WebKitExtensions in Frameworks */, @@ -6190,6 +6204,7 @@ 378E279C2970217400FCADA2 /* LocalPackages */ = { isa = PBXGroup; children = ( + 7B3A16402D4B798000D4C869 /* AppInfoRetriever */, 9D9DE5712C63A96400D20B15 /* AppKitExtensions */, 7B9167A82C09E88800322310 /* AppLauncher */, 378E279D2970217400FCADA2 /* BuildToolPlugins */, @@ -6552,6 +6567,7 @@ 4B4D60572A0B29FA00BCD287 /* AppAndExtensionTargets */, 4B4D60602A0B29FA00BCD287 /* AppTargets */, 4B4D60742A0B29FA00BCD287 /* NetworkExtensionTargets */, + 7B0A6DF92D47CCDF00FDFDC2 /* ExcludedApps */, 7B5A236D2C46A0DA007213AC /* ExcludedDomains */, ); path = NetworkProtection; @@ -7353,6 +7369,16 @@ path = DuckSchemeHandler; sourceTree = ""; }; + 7B0A6DF92D47CCDF00FDFDC2 /* ExcludedApps */ = { + isa = PBXGroup; + children = ( + 7B0A6DF72D47CCDF00FDFDC2 /* ExcludedApps.storyboard */, + 7B0A6DF82D47CCDF00FDFDC2 /* ExcludedAppsViewController.swift */, + 7B93A68A2D4A5AEC00E9FFC1 /* ExcludedAppsModel.swift */, + ); + path = ExcludedApps; + sourceTree = ""; + }; 7B1E819A27C8874900FF0E60 /* Autofill */ = { isa = PBXGroup; children = ( @@ -10236,6 +10262,7 @@ 379230952D3852860019E130 /* HistoryView */, 4B94B79A2D4731F00014AAB8 /* SyncUI-macOS */, 7BECF1AA2D4905E000C1D691 /* PreferencesUI-macOS */, + 7B3A16412D4B7A9600D4C869 /* AppInfoRetriever */, ); productName = DuckDuckGo; productReference = 3706FD05293F65D500E42796 /* DuckDuckGo App Store.app */; @@ -10723,6 +10750,7 @@ 379230932D38527E0019E130 /* HistoryView */, 4B94B7982D4731E80014AAB8 /* SyncUI-macOS */, 7BECF1A82D4905DA00C1D691 /* PreferencesUI-macOS */, + 7B3A16432D4B7A9C00D4C869 /* AppInfoRetriever */, ); productName = DuckDuckGo; productReference = AA585D7E248FD31100E9A3E2 /* DuckDuckGo.app */; @@ -10979,6 +11007,7 @@ 3706FCE7293F65D500E42796 /* shield-dot-mouse-over.json in Resources */, BD384ACB2BBC821B00EF3735 /* vpn-dark-mode.json in Resources */, 3706FCEA293F65D500E42796 /* PasswordManager.storyboard in Resources */, + 7B0A6DFB2D47CCDF00FDFDC2 /* ExcludedApps.storyboard in Resources */, BD384ACC2BBC821B00EF3735 /* vpn-light-mode.json in Resources */, 3706FCEB293F65D500E42796 /* dark-flame-mouse-over.json in Resources */, 3706FCEC293F65D500E42796 /* flame-mouse-over.json in Resources */, @@ -11182,6 +11211,7 @@ AA7EB6E927E880A600036718 /* shield-dot-mouse-over.json in Resources */, BD384AC92BBC821A00EF3735 /* vpn-dark-mode.json in Resources */, 85625994269C8F9600EE44BC /* PasswordManager.storyboard in Resources */, + 7B0A6DFD2D47CCDF00FDFDC2 /* ExcludedApps.storyboard in Resources */, BD384ACA2BBC821A00EF3735 /* vpn-light-mode.json in Resources */, AA7EB6E327E7D05500036718 /* dark-flame-mouse-over.json in Resources */, AA7EB6E227E7D05500036718 /* flame-mouse-over.json in Resources */, @@ -11985,6 +12015,7 @@ F18826932BC0105900D9AC4F /* PixelDataStore.swift in Sources */, 7BBD45B22A691AB500C83CA9 /* NetworkProtectionDebugUtilities.swift in Sources */, 3706FB87293F65D500E42796 /* ProcessExtension.swift in Sources */, + 7B0A6DFA2D47CCDF00FDFDC2 /* ExcludedAppsViewController.swift in Sources */, F18826812BBEB58100D9AC4F /* PrivacyProPixel.swift in Sources */, 31521AC42CC01BC700248E6F /* AIChatTabOpener.swift in Sources */, 3706FB88293F65D500E42796 /* PermissionAuthorizationQuery.swift in Sources */, @@ -12074,6 +12105,7 @@ 3706FBB6293F65D500E42796 /* ChromiumPreferences.swift in Sources */, 1D6860542D370A40006FC53E /* WebExtensionPathsCache.swift in Sources */, 85B49AFB2CD1B7C5007FAA2A /* SystemInfo.swift in Sources */, + 7B93A68B2D4A5AF200E9FFC1 /* ExcludedAppsModel.swift in Sources */, 3706FBB7293F65D500E42796 /* FirePopoverViewController.swift in Sources */, 3706FBB8293F65D500E42796 /* SavePaymentMethodPopover.swift in Sources */, B6CC266D2BAD9CD800F53F8D /* FileProgressPresenter.swift in Sources */, @@ -13647,6 +13679,7 @@ AA5FA69D275F945C00DCE9C9 /* FaviconStore.swift in Sources */, 4B9DB0352A983B24000927DB /* WaitlistTermsAndConditionsView.swift in Sources */, AAB8203C26B2DE0D00788AC3 /* SuggestionListCharacteristics.swift in Sources */, + 7B93A68C2D4A5AF200E9FFC1 /* ExcludedAppsModel.swift in Sources */, 3173072A2CD248EA00C492AB /* AutofillToolbarOnboardingViewController.swift in Sources */, 4B6785472AA8DE68008A5004 /* VPNUninstaller.swift in Sources */, 4B9292D42667123700AD2C21 /* BookmarkListViewController.swift in Sources */, @@ -14113,6 +14146,7 @@ C126B35A2C820924005DC2A3 /* FreemiumDebugMenu.swift in Sources */, B6A9E46B2614618A0067D1B9 /* OperatingSystemVersionExtension.swift in Sources */, 4BDFA4AE27BF19E500648192 /* ToggleableScrollView.swift in Sources */, + 7B0A6DFC2D47CCDF00FDFDC2 /* ExcludedAppsViewController.swift in Sources */, 1D36F4242A3B85C50052B527 /* TabCleanupPreparer.swift in Sources */, B6ABD0CE2BC042CE0000EB69 /* NSURL+sandboxExtensionRetainCount.m in Sources */, 4B4D60DF2A0C875F00BCD287 /* NetworkProtectionOptionKeyExtension.swift in Sources */, @@ -16184,6 +16218,14 @@ package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = Subscription; }; + 7B3A16412D4B7A9600D4C869 /* AppInfoRetriever */ = { + isa = XCSwiftPackageProductDependency; + productName = AppInfoRetriever; + }; + 7B3A16432D4B7A9C00D4C869 /* AppInfoRetriever */ = { + isa = XCSwiftPackageProductDependency; + productName = AppInfoRetriever; + }; 7B624F162BA25C1F00A6C544 /* NetworkProtectionUI */ = { isa = XCSwiftPackageProductDependency; productName = NetworkProtectionUI; diff --git a/DuckDuckGo-macOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo-macOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index a68233817a..a54595442c 100644 --- a/DuckDuckGo-macOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo-macOS.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -33,7 +33,7 @@ "location" : "https://github.com/duckduckgo/BrowserServicesKit", "state" : { "branch" : "dominik/htmlntp-recent-activity", - "revision" : "045b2b69fed3b4bdcc6d093edf25f74899392418" + "revision" : "6db339cb036fcfa212bfd00eaef20ca0e6bff774" } }, { @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/content-scope-scripts", "state" : { - "branch" : "pr-releases/pr-1419", - "revision" : "bea00136b648496edee3f073be759ffc106379e7" + "branch" : "pr-releases/pr-1459", + "revision" : "3191584bfb6efdb68f2290db8b26285a4f1d2a48" } }, { diff --git a/DuckDuckGo/Common/Localizables/UserText+NetworkProtection.swift b/DuckDuckGo/Common/Localizables/UserText+NetworkProtection.swift index 8b1a41dead..bdfa7f5298 100644 --- a/DuckDuckGo/Common/Localizables/UserText+NetworkProtection.swift +++ b/DuckDuckGo/Common/Localizables/UserText+NetworkProtection.swift @@ -222,6 +222,12 @@ extension UserText { static let vpnSettingsManageExclusionsButtonTitle = NSLocalizedString("vpn.setting.exclusions.manage.button.title", value: "Manage...", comment: "Title for the button to manage exclusions") + static let vpnNoExclusionsFoundText = NSLocalizedString("vpn.no.exclusions.found.text", value: "None", comment: "Text shown in VPN settings when no exclusions are configured") + + // MARK: - Excluded Apps + + static let vpnExcludedAppsAddApp = NSLocalizedString("vpn.excluded.apps.add.app", value: "Add Application", comment: "Add Application button for the excluded apps view") + // MARK: - Excluded Domains static let vpnExcludedDomainsDescription = NSLocalizedString("vpn.setting.excluded.domains.description", value: "Excluded websites will bypass the VPN.", comment: "Excluded Sites description") diff --git a/DuckDuckGo/DBP/DBPHomeViewController.swift b/DuckDuckGo/DBP/DBPHomeViewController.swift index 5b73cd4af3..a3e0c3e974 100644 --- a/DuckDuckGo/DBP/DBPHomeViewController.swift +++ b/DuckDuckGo/DBP/DBPHomeViewController.swift @@ -168,18 +168,6 @@ final class DBPHomeViewController: NSViewController { } } -extension DBPHomeViewController: DataBrokerProtectionInviteDialogsViewModelDelegate { - func dataBrokerProtectionInviteDialogsViewModelDidReedemSuccessfully(_ viewModel: DataBrokerProtectionInviteDialogsViewModel) { - presentedWindowController?.window?.close() - presentedWindowController = nil - setupUIWithCurrentStatus() - } - - func dataBrokerProtectionInviteDialogsViewModelDidCancel(_ viewModel: DataBrokerProtectionInviteDialogsViewModel) { - closeUI() - } -} - // MARK: - Error UI extension DBPHomeViewController { diff --git a/DuckDuckGo/HomePage/Model/HomePageContinueSetUpModel.swift b/DuckDuckGo/HomePage/Model/HomePageContinueSetUpModel.swift index e0fa73fab8..ae3c662128 100644 --- a/DuckDuckGo/HomePage/Model/HomePageContinueSetUpModel.swift +++ b/DuckDuckGo/HomePage/Model/HomePageContinueSetUpModel.swift @@ -23,6 +23,7 @@ import Common import Foundation import PixelKit import Subscription +import FeatureFlags import NetworkProtection import NetworkProtectionUI @@ -77,6 +78,7 @@ extension HomePage.Models { } } + private var cancellables: Set = [] let shouldShowAllFeaturesPublisher: AnyPublisher private let shouldShowAllFeaturesSubject = PassthroughSubject() @@ -160,7 +162,36 @@ extension HomePage.Models { NotificationCenter.default.addObserver(self, selector: #selector(refreshFeaturesForHTMLNewTabPage(_:)), name: .newTabPageWebViewDidAppear, object: nil) // This is just temporarily here to run an A/A test to check the new experiment framework works as expected - _ = Application.appDelegate.featureFlagger.getCohortIfEnabled(for: CredentialsSavingFlag()) + guard let cohort = Application.appDelegate.featureFlagger.resolveCohort(for: FeatureFlag.testExperiment) as? FeatureFlag.TestExperimentCohort else { return } + switch cohort { + + case .control: + print("COHORT A") + case .treatment: + print("COHORT B") + } + subscribeToTestExperimentFeatureFlagChanges() + + } + + private func subscribeToTestExperimentFeatureFlagChanges() { + guard let overridesHandler = Application.appDelegate.featureFlagger.localOverrides?.actionHandler as? FeatureFlagOverridesPublishingHandler else { + return + } + + overridesHandler.experimentFlagDidChangePublisher + .filter { $0.0 == .testExperiment } + .sink { (_, cohort) in + guard let newCohort = FeatureFlag.TestExperimentCohort.cohort(for: cohort) else { return } + switch newCohort { + case .control: + print("COHORT A") + case .treatment: + print("COHORT B") + } + } + + .store(in: &cancellables) } @MainActor func performAction(for featureType: FeatureType) { @@ -448,19 +479,3 @@ extension AppVersion { return "\(components[0]).\(components[1])" } } - -// This is just temporarily here to run an A/A test to check the new experiment framework works as expected -public struct CredentialsSavingFlag: FeatureFlagExperimentDescribing { - public init() {} - - public typealias CohortType = Cohort - - public var rawValue = "credentialSaving" - - public var source: FeatureFlagSource = .remoteReleasable(.subfeature(ExperimentTestSubfeatures.experimentTestAA)) - - public enum Cohort: String, FlagCohort { - case control - case blue - } -} diff --git a/DuckDuckGo/InternalUserDecider/FeatureFlagOverridesMenu.swift b/DuckDuckGo/InternalUserDecider/FeatureFlagOverridesMenu.swift index 3506d97984..0a39162593 100644 --- a/DuckDuckGo/InternalUserDecider/FeatureFlagOverridesMenu.swift +++ b/DuckDuckGo/InternalUserDecider/FeatureFlagOverridesMenu.swift @@ -19,88 +19,122 @@ import AppKit import BrowserServicesKit import FeatureFlags - final class FeatureFlagOverridesMenu: NSMenu { - let featureFlagger: FeatureFlagger - let setInternalUserStateItem: NSMenuItem = { let item = NSMenuItem(title: "Set Internal User State First") item.isEnabled = false return item }() - init(featureFlagOverrides: FeatureFlagger) { self.featureFlagger = featureFlagOverrides super.init(title: "") buildItems { - setInternalUserStateItem + internalUserStateMenuItem() + NSMenuItem.separator() + + sectionHeader(title: "Feature Flags") + featureFlagMenuItems() + NSMenuItem.separator() + + sectionHeader(title: "Experiments") + experimentFeatureMenuItems() NSMenuItem.separator() + resetAllOverridesMenuItem() + } + } + + required init(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Menu Item Builders + + private func internalUserStateMenuItem() -> NSMenuItem { + return setInternalUserStateItem + } - FeatureFlag.allCases.filter(\.supportsLocalOverriding).map { flag in + private func featureFlagMenuItems() -> [NSMenuItem] { + return FeatureFlag.allCases + .filter { $0.supportsLocalOverriding && $0.cohortType == nil } + .map { flag in NSMenuItem( - title: "\(flag.rawValue) (default: \(featureFlagger.isFeatureOn(for: flag, allowOverride: false) ? "on" : "off"))", + title: menuItemTitle(for: flag), action: #selector(toggleFeatureFlag(_:)), target: self, representedObject: flag ) } + } - NSMenuItem.separator() - NSMenuItem(title: "Remove All Overrides", action: #selector(resetAllOverrides(_:))).targetting(self) - } + private func experimentFeatureMenuItems() -> [NSMenuItem] { + return FeatureFlag.allCases + .filter { $0.supportsLocalOverriding && $0.cohortType != nil } + .map { experiment in + let experimentMenuItem = NSMenuItem( + title: menuItemTitle(for: experiment), + action: nil, + target: self, + representedObject: experiment + ) + experimentMenuItem.submenu = cohortSubmenu(for: experiment) + return experimentMenuItem + } } - required init(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") + private func resetAllOverridesMenuItem() -> NSMenuItem { + return NSMenuItem( + title: "Remove All Overrides", + action: #selector(resetAllOverrides(_:)), + target: self + ) } + // MARK: - Menu Updates + override func update() { super.update() items.forEach { item in - guard let flag = item.representedObject as? FeatureFlag else { - return - } + guard let flag = item.representedObject as? FeatureFlag else { return } item.isHidden = !featureFlagger.internalUserDecider.isInternalUser - item.title = "\(flag.rawValue) (default: \(defaultValue(for: flag)), override: \(overrideValue(for: flag)))" - let override = featureFlagger.localOverrides?.override(for: flag) - item.state = override == true ? .on : .off - - if override != nil { - item.submenu = NSMenu(items: [ - NSMenuItem( - title: "Remove Override", - action: #selector(resetOverride(_:)), - target: self, - representedObject: flag - ) - ]) + item.title = menuItemTitle(for: flag) + + if flag.cohortType == nil { + updateFeatureFlagItem(item, flag: flag) } else { - item.submenu = nil + updateExperimentFeatureItem(item, flag: flag) } } - setInternalUserStateItem.isHidden = featureFlagger.internalUserDecider.isInternalUser } - private func defaultValue(for flag: FeatureFlag) -> String { - featureFlagger.isFeatureOn(for: flag, allowOverride: false) ? "on" : "off" + private func updateFeatureFlagItem(_ item: NSMenuItem, flag: FeatureFlag) { + let override = featureFlagger.localOverrides?.override(for: flag) + item.state = override == true ? .on : .off + item.submenu = override != nil ? cohortSubmenu(for: flag) : nil } - private func overrideValue(for flag: FeatureFlag) -> String { - guard let override = featureFlagger.localOverrides?.override(for: flag) else { - return "none" - } - return override ? "on" : "off" + private func updateExperimentFeatureItem(_ item: NSMenuItem, flag: FeatureFlag) { + let override = featureFlagger.localOverrides?.experimentOverride(for: flag) + item.state = override != nil ? .on : .off + item.submenu = override != nil ? cohortSubmenu(for: flag) : cohortSubmenu(for: flag) } + // MARK: - Actions + @objc func toggleFeatureFlag(_ sender: NSMenuItem) { guard let featureFlag = sender.representedObject as? FeatureFlag else { return } featureFlagger.localOverrides?.toggleOverride(for: featureFlag) } + @objc func toggleExperimentFeatureFlag(_ sender: NSMenuItem) { + guard let representedObject = sender.representedObject as? (FeatureFlag, String) else { return } + let (experimentFeature, cohort) = representedObject + featureFlagger.localOverrides?.setExperimentCohortOverride(for: experimentFeature, cohort: cohort) + } + @objc func resetOverride(_ sender: NSMenuItem) { guard let featureFlag = sender.representedObject as? FeatureFlag else { return } featureFlagger.localOverrides?.clearOverride(for: featureFlag) @@ -109,4 +143,82 @@ final class FeatureFlagOverridesMenu: NSMenu { @objc func resetAllOverrides(_ sender: NSMenuItem) { featureFlagger.localOverrides?.clearAllOverrides(for: FeatureFlag.self) } + + // MARK: - Helpers + + private func menuItemTitle(for flag: FeatureFlag) -> String { + return "\(flag.rawValue) (default: \(defaultValue(for: flag)), override: \(overrideValue(for: flag)))" + } + + private func cohortSubmenu(for flag: FeatureFlag) -> NSMenu { + let submenu = NSMenu() + + // Get the current override cohort + let currentOverride = featureFlagger.localOverrides?.experimentOverride(for: flag) + + // Get all possible cohorts for this flag + let cohorts = cohorts(for: flag) + + // Add cohort options + for cohort in cohorts { + let cohortItem = NSMenuItem( + title: "Cohort: \(cohort.rawValue)", + action: #selector(toggleExperimentFeatureFlag(_:)), + target: self + ) + cohortItem.representedObject = (flag, cohort.rawValue) + + // Mark the selected override with a checkmark + cohortItem.state = (cohort.rawValue == currentOverride) ? .on : .off + + submenu.addItem(cohortItem) + } + + submenu.addItem(NSMenuItem.separator()) + + // "Remove Override" only if an override exists + let removeOverrideItem = NSMenuItem( + title: "Remove Override", + action: #selector(resetOverride(_:)), + target: self + ) + removeOverrideItem.representedObject = flag + removeOverrideItem.isHidden = currentOverride == nil + + submenu.addItem(removeOverrideItem) + + return submenu + } + + private func cohorts(for featureFlag: Flag) -> [any FeatureFlagCohortDescribing] { + return featureFlag.cohortType?.cohorts ?? [] + } + + private func defaultValue(for flag: FeatureFlag) -> String { + if flag.cohortType == nil { + return featureFlagger.isFeatureOn(for: flag, allowOverride: false) ? "on" : "off" + } else { + return featureFlagger.localOverrides?.currentExperimentCohort(for: flag)?.rawValue ?? "unassigned" + } + } + + private func overrideValue(for flag: FeatureFlag) -> String { + if flag.cohortType == nil { + guard let override = featureFlagger.localOverrides?.override(for: flag) else { + return "none" + } + return override ? "on" : "off" + } else { + guard let override = featureFlagger.localOverrides?.experimentOverride(for: flag) else { + return "none" + } + return override + } + } + + private func sectionHeader(title: String) -> NSMenuItem { + let headerItem = NSMenuItem(title: title) + headerItem.isEnabled = false + return headerItem + } } diff --git a/DuckDuckGo/Localizable.xcstrings b/DuckDuckGo/Localizable.xcstrings index 6d6c793704..6de97c1737 100644 --- a/DuckDuckGo/Localizable.xcstrings +++ b/DuckDuckGo/Localizable.xcstrings @@ -69057,6 +69057,66 @@ } } }, + "vpn.excluded.apps.add.app" : { + "comment" : "Add Application button for the excluded apps view", + "extractionState" : "extracted_with_value", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anwendung hinzufügen" + } + }, + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Add Application" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Añadir aplicación" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajouter une application" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aggiungi applicazione" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Applicatie toevoegen" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dodaj aplikację" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adicionar aplicação" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Добавить приложение" + } + } + } + }, "vpn.excluded.apps.title" : { "comment" : "Excluded Apps title in VPN settings", "extractionState" : "extracted_with_value", @@ -71277,6 +71337,66 @@ } } }, + "vpn.no.exclusions.found.text" : { + "comment" : "Text shown in VPN settings when no exclusions are configured", + "extractionState" : "extracted_with_value", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gar nichts" + } + }, + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "None" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ninguna" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Niente" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brak" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nenhum" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ничего" + } + } + } + }, "vpn.notifications.connection.drops.or.status.changes.title" : { "comment" : "Title of the VPN notification option", "extractionState" : "extracted_with_value", diff --git a/DuckDuckGo/NetworkProtection/ExcludedApps/ExcludedApps.storyboard b/DuckDuckGo/NetworkProtection/ExcludedApps/ExcludedApps.storyboard new file mode 100644 index 0000000000..85e7822093 --- /dev/null +++ b/DuckDuckGo/NetworkProtection/ExcludedApps/ExcludedApps.storyboard @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DuckDuckGo/NetworkProtection/ExcludedApps/ExcludedAppsModel.swift b/DuckDuckGo/NetworkProtection/ExcludedApps/ExcludedAppsModel.swift new file mode 100644 index 0000000000..b487386cde --- /dev/null +++ b/DuckDuckGo/NetworkProtection/ExcludedApps/ExcludedAppsModel.swift @@ -0,0 +1,76 @@ +// +// ExcludedAppsModel.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 AppInfoRetriever +import Foundation +import NetworkProtectionProxy +import NetworkProtectionUI +import PixelKit + +protocol ExcludedAppsModel { + var excludedApps: [String] { get } + func getAppInfo(bundleID: String) -> AppInfo + + func add(appURL: URL) -> AppInfo? + func remove(bundleID: String) +} + +final class DefaultExcludedAppsModel { + private let appInfoRetriever: AppInfoRetrieveing = AppInfoRetriever() + let proxySettings = TransparentProxySettings(defaults: .netP) + private let pixelKit: PixelFiring? + + init(pixelKit: PixelFiring? = PixelKit.shared) { + self.pixelKit = pixelKit + } +} + +extension DefaultExcludedAppsModel: ExcludedAppsModel { + var excludedApps: [String] { + proxySettings.excludedApps + } + + func add(appURL: URL) -> AppInfo? { + guard let appInfo = appInfoRetriever.getAppInfo(appURL: appURL) else { + return nil + } + + proxySettings.appRoutingRules[appInfo.bundleID] = .exclude + return appInfo + } + + func remove(bundleID: String) { + guard proxySettings.appRoutingRules[bundleID] == .exclude else { + return + } + + proxySettings.appRoutingRules.removeValue(forKey: bundleID) + } + + /// Provides AppInfo for the specified bundleID for the scope of presenting the information to the user. + /// + /// Since this method is specific to show app information to the user, it's IMPORTANT to make sure + /// we always return AppInfo for the bundleID provided. This ensures that the user can always remove + /// an exclusion through the UI, even if the app has been deleted from the system. For this purpose + /// when the app information cannot be retrieved, this method will return AppInfor with the bundleID + /// as the app's name. + /// + func getAppInfo(bundleID: String) -> AppInfo { + appInfoRetriever.getAppInfo(bundleID: bundleID) ?? AppInfo(bundleID: bundleID, name: bundleID, icon: NSImage.window16) + } +} diff --git a/DuckDuckGo/NetworkProtection/ExcludedApps/ExcludedAppsViewController.swift b/DuckDuckGo/NetworkProtection/ExcludedApps/ExcludedAppsViewController.swift new file mode 100644 index 0000000000..3f85200251 --- /dev/null +++ b/DuckDuckGo/NetworkProtection/ExcludedApps/ExcludedAppsViewController.swift @@ -0,0 +1,190 @@ +// +// ExcludedAppsViewController.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 +import AppInfoRetriever + +final class ExcludedAppsViewController: NSViewController { + typealias Model = ExcludedAppsModel + + enum Constants { + static let storyboardName = "ExcludedApps" + static let identifier = "ExcludedAppsViewController" + static let cellIdentifier = NSUserInterfaceItemIdentifier(rawValue: "ExcludedAppCell") + } + + static func create(model: Model = DefaultExcludedAppsModel()) -> ExcludedAppsViewController { + let storyboard = loadStoryboard() + + return storyboard.instantiateController(identifier: Constants.identifier) { coder in + ExcludedAppsViewController(model: model, coder: coder) + } + } + + static func loadStoryboard() -> NSStoryboard { + NSStoryboard(name: Constants.storyboardName, bundle: nil) + } + + @IBOutlet var tableView: NSTableView! + @IBOutlet var addAppButton: NSButton! + @IBOutlet var removeAppButton: NSButton! + @IBOutlet var doneButton: NSButton! + @IBOutlet var titleLabel: NSTextField! + + private let faviconManagement: FaviconManagement = FaviconManager.shared + + private var allApps = [AppInfo]() + private var filteredApps: [AppInfo]? + + private var visibleApps: [AppInfo] { + return filteredApps ?? allApps + } + + private let model: Model + + init?(model: Model, coder: NSCoder) { + self.model = model + + super.init(coder: coder) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + applyModalWindowStyleIfNeeded() + reloadData() + setUpStrings() + } + + private func setUpStrings() { + addAppButton.title = UserText.vpnExcludedAppsAddApp + removeAppButton.title = UserText.remove + doneButton.title = UserText.done + titleLabel.stringValue = UserText.vpnExcludedAppsTitle + } + + private func updateRemoveButtonState() { + removeAppButton.isEnabled = tableView.selectedRow > -1 + } + + fileprivate func reloadData() { + allApps = model.excludedApps.sorted { (lhs, rhs) -> Bool in + return lhs < rhs + }.map { bundleID in + model.getAppInfo(bundleID: bundleID) + } + + tableView.reloadData() + updateRemoveButtonState() + } + + @IBAction func doneButtonClicked(_ sender: NSButton) { + dismiss() + } + + @IBAction func addApp(_ sender: NSButton) { + let panel = NSOpenPanel() + panel.allowedContentTypes = [.applicationBundle] + panel.allowsMultipleSelection = false + panel.canChooseDirectories = false + panel.canChooseFiles = true + panel.directoryURL = URL(fileURLWithPath: "/Applications") + + guard panel.runModal() == .OK, + let appURL = panel.url else { + return + } + + add(appURL: appURL) + } + + private func add(appURL: URL) { + Task { + guard let appInfo = model.add(appURL: appURL) else { + return + } + reloadData() + + if let newRowIndex = allApps.firstIndex(of: appInfo) { + tableView.scrollRowToVisible(newRowIndex) + } + } + } + + @IBAction func removeSelected(_ sender: NSButton) { + guard tableView.selectedRow > -1 else { + updateRemoveButtonState() + return + } + + let appInfo = visibleApps[tableView.selectedRow] + model.remove(bundleID: appInfo.bundleID) + reloadData() + } +} + +extension ExcludedAppsViewController: NSTableViewDataSource, NSTableViewDelegate { + + func numberOfRows(in tableView: NSTableView) -> Int { + return visibleApps.count + } + + func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? { + return visibleApps[row] + } + + func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { + guard let cell = tableView.makeView(withIdentifier: Constants.cellIdentifier, owner: nil) as? NSTableCellView else { + + return nil + } + + let appInfo = visibleApps[row] + + cell.textField?.stringValue = appInfo.name + cell.imageView?.image = appInfo.icon + cell.imageView?.applyFaviconStyle() + + return cell + } + + func tableViewSelectionDidChange(_ notification: Notification) { + updateRemoveButtonState() + } +} + +extension ExcludedAppsViewController: NSTextFieldDelegate { + + func controlTextDidChange(_ notification: Notification) { + guard let field = notification.object as? NSSearchField else { return } + + if field.stringValue.isEmpty { + filteredApps = nil + } else { + filteredApps = allApps.filter { + $0.name.contains(field.stringValue) || $0.bundleID.contains(field.stringValue) + } + } + + reloadData() + } +} diff --git a/DuckDuckGo/NetworkProtection/ExcludedDomains/ExcludedDomains.storyboard b/DuckDuckGo/NetworkProtection/ExcludedDomains/ExcludedDomains.storyboard index 86029818d9..629ce22a26 100644 --- a/DuckDuckGo/NetworkProtection/ExcludedDomains/ExcludedDomains.storyboard +++ b/DuckDuckGo/NetworkProtection/ExcludedDomains/ExcludedDomains.storyboard @@ -1,7 +1,7 @@ - + - + diff --git a/DuckDuckGo/Preferences/Model/VPNPreferencesModel.swift b/DuckDuckGo/Preferences/Model/VPNPreferencesModel.swift index e50d77948f..9e74dea8b3 100644 --- a/DuckDuckGo/Preferences/Model/VPNPreferencesModel.swift +++ b/DuckDuckGo/Preferences/Model/VPNPreferencesModel.swift @@ -17,13 +17,13 @@ // import AppKit +import BrowserServicesKit import Combine import Foundation import NetworkProtection import NetworkProtectionIPC import NetworkProtectionProxy import NetworkProtectionUI -import BrowserServicesKit final class VPNPreferencesModel: ObservableObject { @@ -94,15 +94,11 @@ final class VPNPreferencesModel: ObservableObject { isExclusionsFeatureAvailableInBuild && isAppExclusionsFeatureEnabled } - var excludedDomainsCount: Int { - proxySettings.excludedDomains.count - } + @Published + private(set) var excludedDomainsCount: Int - var excludedAppsCount: Int { - proxySettings.appRoutingRules.filter { (_, rule) in - rule == .exclude - }.count - } + @Published + private(set) var excludedAppsCount: Int @Published var notifyStatusChanges: Bool { didSet { @@ -143,6 +139,10 @@ final class VPNPreferencesModel: ObservableObject { self.featureFlagger = featureFlagger connectOnLogin = settings.connectOnLogin + excludedAppsCount = proxySettings.appRoutingRules.filter { (_, rule) in + rule == .exclude + }.count + excludedDomainsCount = proxySettings.excludedDomains.count excludeLocalNetworks = settings.excludeLocalNetworks notifyStatusChanges = settings.notifyStatusChanges showInMenuBar = settings.showInMenuBar @@ -151,8 +151,10 @@ final class VPNPreferencesModel: ObservableObject { onboardingStatus = defaults.networkProtectionOnboardingStatus locationItem = VPNLocationPreferenceItemModel(selectedLocation: settings.selectedLocation) + subscribeToAppRoutingRulesChanges() subscribeToOnboardingStatusChanges(defaults: defaults) subscribeToConnectOnLoginSettingChanges() + subscribeToExcludedDomainsCountChanges() subscribeToExcludeLocalNetworksSettingChanges() subscribeToShowInMenuBarSettingChanges() subscribeToShowInBrowserToolbarSettingsChanges() @@ -160,32 +162,50 @@ final class VPNPreferencesModel: ObservableObject { subscribeToDNSSettingsChanges() } - func subscribeToOnboardingStatusChanges(defaults: UserDefaults) { + private func subscribeToAppRoutingRulesChanges() { + proxySettings.appRoutingRulesPublisher + .map { rules in + rules.filter { (_, rule) in + rule == .exclude + }.count + } + .assign(to: \.excludedAppsCount, onWeaklyHeld: self) + .store(in: &cancellables) + } + + private func subscribeToOnboardingStatusChanges(defaults: UserDefaults) { defaults.networkProtectionOnboardingStatusPublisher .assign(to: \.onboardingStatus, onWeaklyHeld: self) .store(in: &cancellables) } - func subscribeToConnectOnLoginSettingChanges() { + private func subscribeToConnectOnLoginSettingChanges() { settings.connectOnLoginPublisher .assign(to: \.connectOnLogin, onWeaklyHeld: self) .store(in: &cancellables) } - func subscribeToExcludeLocalNetworksSettingChanges() { + private func subscribeToExcludedDomainsCountChanges() { + proxySettings.excludedDomainsPublisher + .map { $0.count } + .assign(to: \.excludedDomainsCount, onWeaklyHeld: self) + .store(in: &cancellables) + } + + private func subscribeToExcludeLocalNetworksSettingChanges() { settings.excludeLocalNetworksPublisher .assign(to: \.excludeLocalNetworks, onWeaklyHeld: self) .store(in: &cancellables) } - func subscribeToShowInMenuBarSettingChanges() { + private func subscribeToShowInMenuBarSettingChanges() { settings.showInMenuBarPublisher .removeDuplicates() .assign(to: \.showInMenuBar, onWeaklyHeld: self) .store(in: &cancellables) } - func subscribeToShowInBrowserToolbarSettingsChanges() { + private func subscribeToShowInBrowserToolbarSettingsChanges() { NotificationCenter.default.publisher(for: .PinnedViewsChanged).sink { [weak self] notification in guard let self = self else { return @@ -203,14 +223,14 @@ final class VPNPreferencesModel: ObservableObject { .store(in: &cancellables) } - func subscribeToLocationSettingChanges() { + private func subscribeToLocationSettingChanges() { settings.selectedLocationPublisher .map(VPNLocationPreferenceItemModel.init(selectedLocation:)) .assign(to: \.locationItem, onWeaklyHeld: self) .store(in: &cancellables) } - func subscribeToDNSSettingsChanges() { + private func subscribeToDNSSettingsChanges() { settings.dnsSettingsPublisher .assign(to: \.dnsSettings, onWeaklyHeld: self) .store(in: &cancellables) @@ -251,7 +271,21 @@ final class VPNPreferencesModel: ObservableObject { return alert } - // MARK: - Excluded Sites + // MARK: - Actions + + @MainActor + func manageExcludedApps() { + let windowController = ExcludedAppsViewController.create().wrappedInWindowController() + + guard let window = windowController.window, + let parentWindowController = WindowControllersManager.shared.lastKeyMainWindowController + else { + assertionFailure("DataClearingPreferences: Failed to present ExcludedAppsViewController") + return + } + + parentWindowController.window?.beginSheet(window) + } @MainActor func manageExcludedSites() { diff --git a/DuckDuckGo/Preferences/View/PreferencesVPNView.swift b/DuckDuckGo/Preferences/View/PreferencesVPNView.swift index 0acb01dc88..e5d0764a2c 100644 --- a/DuckDuckGo/Preferences/View/PreferencesVPNView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesVPNView.swift @@ -134,7 +134,7 @@ extension Preferences { title: UserText.vpnExcludedAppsTitle, description: exclusionCountString(value: model.excludedAppsCount), buttonName: UserText.vpnSettingsManageExclusionsButtonTitle, - buttonAction: { /* will be implemented in follow-up PR */ }, + buttonAction: { model.manageExcludedApps() }, enabled: true) } } @@ -207,7 +207,7 @@ extension Preferences { /// Resolves the text to be used for exclusion counts /// private func exclusionCountString(value: Int) -> String { - value > 0 ? String(value) : "None" + value > 0 ? String(value) : UserText.vpnNoExclusionsFoundText } } } diff --git a/DuckDuckGoDBPBackgroundAgent/DataBrokerAuthenticationManagerBuilder.swift b/DuckDuckGoDBPBackgroundAgent/DataBrokerAuthenticationManagerBuilder.swift index 0fb44dfb1c..0c4c289275 100644 --- a/DuckDuckGoDBPBackgroundAgent/DataBrokerAuthenticationManagerBuilder.swift +++ b/DuckDuckGoDBPBackgroundAgent/DataBrokerAuthenticationManagerBuilder.swift @@ -22,11 +22,9 @@ import Subscription final public class DataBrokerAuthenticationManagerBuilder { - static func buildAuthenticationManager(redeemUseCase: RedeemUseCase = RedeemUseCase(), - subscriptionManager: SubscriptionManager) -> DataBrokerProtectionAuthenticationManager { + static func buildAuthenticationManager(subscriptionManager: SubscriptionManager) -> DataBrokerProtectionAuthenticationManager { let subscriptionManager = DataBrokerProtectionSubscriptionManager(subscriptionManager: subscriptionManager) - return DataBrokerProtectionAuthenticationManager(redeemUseCase: redeemUseCase, - subscriptionManager: subscriptionManager) + return DataBrokerProtectionAuthenticationManager(subscriptionManager: subscriptionManager) } } diff --git a/DuckDuckGoDBPBackgroundAgent/DuckDuckGoDBPBackgroundAgentAppDelegate.swift b/DuckDuckGoDBPBackgroundAgent/DuckDuckGoDBPBackgroundAgentAppDelegate.swift index 130516c81f..bfe22780fe 100644 --- a/DuckDuckGoDBPBackgroundAgent/DuckDuckGoDBPBackgroundAgentAppDelegate.swift +++ b/DuckDuckGoDBPBackgroundAgent/DuckDuckGoDBPBackgroundAgentAppDelegate.swift @@ -99,10 +99,7 @@ final class DuckDuckGoDBPBackgroundAgentAppDelegate: NSObject, NSApplicationDele func applicationDidFinishLaunching(_ aNotification: Notification) { Logger.dbpBackgroundAgent.log("DuckDuckGoAgent started") - let redeemUseCase = RedeemUseCase(authenticationService: AuthenticationService(), - authenticationRepository: KeychainAuthenticationData()) - let authenticationManager = DataBrokerAuthenticationManagerBuilder.buildAuthenticationManager(redeemUseCase: redeemUseCase, - subscriptionManager: subscriptionManager) + let authenticationManager = DataBrokerAuthenticationManagerBuilder.buildAuthenticationManager(subscriptionManager: subscriptionManager) manager = DataBrokerProtectionAgentManagerProvider.agentManager(authenticationManager: authenticationManager, accountManager: subscriptionManager.accountManager) manager?.agentFinishedLaunching() diff --git a/DuckDuckGoVPN/VPNProxyLauncher.swift b/DuckDuckGoVPN/VPNProxyLauncher.swift index 7467ccfecd..f7840fa5a5 100644 --- a/DuckDuckGoVPN/VPNProxyLauncher.swift +++ b/DuckDuckGoVPN/VPNProxyLauncher.swift @@ -50,8 +50,9 @@ final class VPNProxyLauncher { private func subscribeToStatusChanges() { notificationCenter.publisher(for: .NEVPNStatusDidChange) .receive(on: DispatchQueue.main) - .removeDuplicates() - .sink(receiveValue: statusChanged(notification:)) + .sink { [weak self] notification in + self?.statusChanged(notification: notification) + } .store(in: &cancellables) } @@ -67,7 +68,9 @@ final class VPNProxyLauncher { private func subscribeToProxySettingChanges() { proxyController.settings.changePublisher - .sink(receiveValue: proxySettingChanged(_:)) + .sink { [weak self] notification in + self?.proxySettingChanged(notification) + } .store(in: &cancellables) } diff --git a/IntegrationTests/MaliciousSiteProtection/MaliciousSiteProtectionIntegrationTests.swift b/IntegrationTests/MaliciousSiteProtection/MaliciousSiteProtectionIntegrationTests.swift index 65096197e7..42434ba883 100644 --- a/IntegrationTests/MaliciousSiteProtection/MaliciousSiteProtectionIntegrationTests.swift +++ b/IntegrationTests/MaliciousSiteProtection/MaliciousSiteProtectionIntegrationTests.swift @@ -342,15 +342,11 @@ class MockFeatureFlagger: FeatureFlagger { return true } - func getCohortIfEnabled(_ subfeature: any PrivacySubfeature) -> CohortID? { + func resolveCohort(for featureFlag: Flag, allowOverride: Bool) -> (any FeatureFlagCohortDescribing)? where Flag: FeatureFlagDescribing { return nil } - func getCohortIfEnabled(for featureFlag: Flag) -> (any FlagCohort)? where Flag: FeatureFlagExperimentDescribing { - return nil - } - - func getAllActiveExperiments() -> Experiments { + var allActiveExperiments: Experiments { return [:] } } diff --git a/LocalPackages/AppInfoRetriever/.gitignore b/LocalPackages/AppInfoRetriever/.gitignore new file mode 100644 index 0000000000..0023a53406 --- /dev/null +++ b/LocalPackages/AppInfoRetriever/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/LocalPackages/AppInfoRetriever/Package.swift b/LocalPackages/AppInfoRetriever/Package.swift new file mode 100644 index 0000000000..4384d6edba --- /dev/null +++ b/LocalPackages/AppInfoRetriever/Package.swift @@ -0,0 +1,20 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "AppInfoRetriever", + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "AppInfoRetriever", + targets: ["AppInfoRetriever"]), + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "AppInfoRetriever"), + ] +) diff --git a/LocalPackages/AppInfoRetriever/README.md b/LocalPackages/AppInfoRetriever/README.md new file mode 100644 index 0000000000..04a35dded3 --- /dev/null +++ b/LocalPackages/AppInfoRetriever/README.md @@ -0,0 +1,4 @@ +# AppInfo + +A package that provides convenience mechanisms to obtain information from other Apps at runtime. +This can be useful for example to show the App's name or icon within our UI. diff --git a/LocalPackages/AppInfoRetriever/Sources/AppInfoRetriever/AppInfo.swift b/LocalPackages/AppInfoRetriever/Sources/AppInfoRetriever/AppInfo.swift new file mode 100644 index 0000000000..a27288c38a --- /dev/null +++ b/LocalPackages/AppInfoRetriever/Sources/AppInfoRetriever/AppInfo.swift @@ -0,0 +1,35 @@ +// +// AppInfo.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 + +public struct AppInfo: Equatable { + public let bundleID: String + public let name: String + public let icon: NSImage? + + public init(bundleID: String, name: String, icon: NSImage?) { + self.bundleID = bundleID + self.name = name + self.icon = icon + } + + public static func == (lhs: Self, rhs: Self) -> Bool { + lhs.bundleID == rhs.bundleID + } +} diff --git a/LocalPackages/AppInfoRetriever/Sources/AppInfoRetriever/AppInfoRetriever.swift b/LocalPackages/AppInfoRetriever/Sources/AppInfoRetriever/AppInfoRetriever.swift new file mode 100644 index 0000000000..4124a2fe65 --- /dev/null +++ b/LocalPackages/AppInfoRetriever/Sources/AppInfoRetriever/AppInfoRetriever.swift @@ -0,0 +1,97 @@ +// +// AppInfoRetriever.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 +import Foundation + +public protocol AppInfoRetrieveing { + + /// Provides a structure featuring commonly-used app info. + /// + /// It's also possible to retrieve the individual information directly by calling other methods in this class. + /// + func getAppInfo(bundleID: String) -> AppInfo? + func getAppInfo(appURL: URL) -> AppInfo? + func getAppIcon(bundleID: String) -> NSImage? + func getAppName(bundleID: String) -> String? + func getBundleID(appURL: URL) -> String? + +} + +public class AppInfoRetriever: AppInfoRetrieveing { + + public init() {} + + public func getAppInfo(bundleID: String) -> AppInfo? { + guard let appName = getAppName(bundleID: bundleID) else { + return nil + } + + let appIcon = getAppIcon(bundleID: bundleID) + return AppInfo(bundleID: bundleID, name: appName, icon: appIcon) + } + + public func getAppInfo(appURL: URL) -> AppInfo? { + guard let bundleID = getBundleID(appURL: appURL) else { + return nil + } + + return getAppInfo(bundleID: bundleID) + } + + public func getAppIcon(bundleID: String) -> NSImage? { + guard let appURL = NSWorkspace.shared.urlForApplication(withBundleIdentifier: bundleID) else { + return nil + } + + let infoPlistURL = appURL.appendingPathComponent("Contents/Info.plist") + guard let plist = NSDictionary(contentsOf: infoPlistURL), + let iconFileName = plist["CFBundleIconFile"] as? String else { + return nil + } + + // Ensure the icon has the correct extension + let iconFile = iconFileName.hasSuffix(".icns") ? iconFileName : "\(iconFileName).icns" + let iconURL = appURL.appendingPathComponent("Contents/Resources/\(iconFile)") + + return NSImage(contentsOf: iconURL) + } + + public func getAppName(bundleID: String) -> String? { + if let appURL = NSWorkspace.shared.urlForApplication(withBundleIdentifier: bundleID) { + // Try reading from Info.plist + let infoPlistURL = appURL.appendingPathComponent("Contents/Info.plist") + if let plist = NSDictionary(contentsOf: infoPlistURL), + let appName = plist["CFBundleDisplayName"] as? String { + return appName + } + // Fallback: Use the app bundle's filename + return appURL.deletingPathExtension().lastPathComponent + } + return nil + } + + public func getBundleID(appURL: URL) -> String? { + let infoPlistURL = appURL.appendingPathComponent("Contents/Info.plist") + if let plist = NSDictionary(contentsOf: infoPlistURL), + let bundleID = plist["CFBundleIdentifier"] as? String { + return bundleID + } + return nil + } +} diff --git a/LocalPackages/DataBrokerProtection/Package.swift b/LocalPackages/DataBrokerProtection/Package.swift index 697488b039..b030c1529a 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: "233.1.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "234.0.0"), .package(path: "../SwiftUIExtensions"), .package(path: "../AppKitExtensions"), .package(path: "../XPCHelper"), diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Authentication/DataBrokerProtectionAuthenticationManaging.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Authentication/DataBrokerProtectionAuthenticationManaging.swift index 0aea2ac3ef..baa2ba6724 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Authentication/DataBrokerProtectionAuthenticationManaging.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Authentication/DataBrokerProtectionAuthenticationManaging.swift @@ -18,17 +18,21 @@ import Foundation +public enum AuthenticationError: Error, Equatable { + case noInviteCode + case cantGenerateURL + case noAuthToken + case issueRedeemingInviteCode(error: String) +} + public protocol DataBrokerProtectionAuthenticationManaging { var isUserAuthenticated: Bool { get } var accessToken: String? { get } func hasValidEntitlement() async throws -> Bool - func shouldAskForInviteCode() -> Bool - func redeem(inviteCode: String) async throws func getAuthHeader() -> String? } public final class DataBrokerProtectionAuthenticationManager: DataBrokerProtectionAuthenticationManaging { - private let redeemUseCase: DataBrokerProtectionRedeemUseCase private let subscriptionManager: DataBrokerProtectionSubscriptionManaging public var isUserAuthenticated: Bool { @@ -39,9 +43,7 @@ public final class DataBrokerProtectionAuthenticationManager: DataBrokerProtecti subscriptionManager.accessToken } - public init(redeemUseCase: any DataBrokerProtectionRedeemUseCase, - subscriptionManager: any DataBrokerProtectionSubscriptionManaging) { - self.redeemUseCase = redeemUseCase + public init(subscriptionManager: any DataBrokerProtectionSubscriptionManaging) { self.subscriptionManager = subscriptionManager } @@ -52,18 +54,4 @@ public final class DataBrokerProtectionAuthenticationManager: DataBrokerProtecti public func getAuthHeader() -> String? { ServicesAuthHeaderBuilder().getAuthHeader(accessToken) } - - // MARK: - Redeem code flow - - // We might want the ability to ask for invite code later on, keeping this here to make things easier - // https://app.asana.com/0/1204167627774280/1207270521849479/f - - public func shouldAskForInviteCode() -> Bool { - // redeemUseCase.shouldAskForInviteCode() - return false - } - - public func redeem(inviteCode: String) async throws { - // await redeemUseCase.redeem(inviteCode: inviteCode) - } } diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionBackendServicePixels.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionBackendServicePixels.swift index 98d574a0b9..191da347ac 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionBackendServicePixels.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionBackendServicePixels.swift @@ -35,31 +35,26 @@ protocol DataBrokerProtectionBackendServicePixels { final class DefaultDataBrokerProtectionBackendServicePixels: DataBrokerProtectionBackendServicePixels { private let pixelHandler: EventMapping private let settings: DataBrokerProtectionSettings - private let authRepository: AuthenticationRepository init(pixelHandler: EventMapping = DataBrokerProtectionPixelsHandler(), - settings: DataBrokerProtectionSettings = DataBrokerProtectionSettings(), - authRepository: AuthenticationRepository = KeychainAuthenticationData()) { + settings: DataBrokerProtectionSettings = DataBrokerProtectionSettings()) { self.pixelHandler = pixelHandler self.settings = settings - self.authRepository = authRepository } func fireGenerateEmailHTTPError(statusCode: Int) { let environment = settings.selectedEnvironment.rawValue - let wasOnWaitlist = authRepository.getWaitlistTimestamp() != nil pixelHandler.fire(.generateEmailHTTPErrorDaily(statusCode: statusCode, environment: environment, - wasOnWaitlist: wasOnWaitlist)) + wasOnWaitlist: false)) } func fireEmptyAccessToken(callSite: BackendServiceCallSite) { let environment = settings.selectedEnvironment.rawValue - let wasOnWaitlist = authRepository.getWaitlistTimestamp() != nil pixelHandler.fire(.emptyAccessTokenDaily(environment: environment, - wasOnWaitlist: wasOnWaitlist, + wasOnWaitlist: false, callSite: callSite)) } } diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/background-color.colorset/Contents.json b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/background-color.colorset/Contents.json deleted file mode 100644 index ae3af675d8..0000000000 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/background-color.colorset/Contents.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "display-p3", - "components" : { - "alpha" : "1.000", - "blue" : "1.000", - "green" : "1.000", - "red" : "1.000" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "display-p3", - "components" : { - "alpha" : "1.000", - "blue" : "0.067", - "green" : "0.067", - "red" : "0.067" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "contrast", - "value" : "high" - } - ], - "color" : { - "color-space" : "display-p3", - "components" : { - "alpha" : "1.000", - "blue" : "1.000", - "green" : "1.000", - "red" : "1.000" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - }, - { - "appearance" : "contrast", - "value" : "high" - } - ], - "color" : { - "color-space" : "display-p3", - "components" : { - "alpha" : "1.000", - "blue" : "0.067", - "green" : "0.067", - "red" : "0.067" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/background-pattern.imageset/Contents.json b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/background-pattern.imageset/Contents.json deleted file mode 100644 index 5a23c2bc7c..0000000000 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/background-pattern.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "filename" : "background-light.png", - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "background-dark.png", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/background-pattern.imageset/background-dark.png b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/background-pattern.imageset/background-dark.png deleted file mode 100644 index b6cab8ba9b..0000000000 Binary files a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/background-pattern.imageset/background-dark.png and /dev/null differ diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/background-pattern.imageset/background-light.png b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/background-pattern.imageset/background-light.png deleted file mode 100644 index 0569b335ab..0000000000 Binary files a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/background-pattern.imageset/background-light.png and /dev/null differ diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/header-background.imageset/Contents.json b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/header-background.imageset/Contents.json deleted file mode 100644 index 73eea6eeb3..0000000000 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/header-background.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "filename" : "DBP Gradient - Light.jpg", - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "DBP Gradient - Dark.jpg", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/header-background.imageset/DBP Gradient - Dark.jpg b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/header-background.imageset/DBP Gradient - Dark.jpg deleted file mode 100644 index 3439830040..0000000000 Binary files a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/header-background.imageset/DBP Gradient - Dark.jpg and /dev/null differ diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/header-background.imageset/DBP Gradient - Light.jpg b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/header-background.imageset/DBP Gradient - Light.jpg deleted file mode 100644 index 83b76dd18c..0000000000 Binary files a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/header-background.imageset/DBP Gradient - Light.jpg and /dev/null differ diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/header-hero.imageset/Contents.json b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/header-hero.imageset/Contents.json deleted file mode 100644 index a2c9657f41..0000000000 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/header-hero.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "filename" : "header-hero.pdf", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/header-hero.imageset/header-hero.pdf b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/header-hero.imageset/header-hero.pdf deleted file mode 100644 index 3083810319..0000000000 Binary files a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/header-hero.imageset/header-hero.pdf and /dev/null differ diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/modal-background-color.colorset/Contents.json b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/modal-background-color.colorset/Contents.json deleted file mode 100644 index e78c4f518a..0000000000 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/modal-background-color.colorset/Contents.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.980", - "green" : "0.980", - "red" : "0.976" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "display-p3", - "components" : { - "alpha" : "1.000", - "blue" : "0.133", - "green" : "0.133", - "red" : "0.133" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "contrast", - "value" : "high" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.980", - "green" : "0.980", - "red" : "0.976" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - }, - { - "appearance" : "contrast", - "value" : "high" - } - ], - "color" : { - "color-space" : "display-p3", - "components" : { - "alpha" : "1.000", - "blue" : "0.133", - "green" : "0.133", - "red" : "0.133" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/profile-background-color.colorset/Contents.json b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/profile-background-color.colorset/Contents.json deleted file mode 100644 index 644987b196..0000000000 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/DBP.xcassets/profile-background-color.colorset/Contents.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "1.000", - "green" : "1.000", - "red" : "1.000" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "display-p3", - "components" : { - "alpha" : "1.000", - "blue" : "0.133", - "green" : "0.133", - "red" : "0.133" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "contrast", - "value" : "high" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "1.000", - "green" : "1.000", - "red" : "1.000" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - }, - { - "appearance" : "contrast", - "value" : "high" - } - ], - "color" : { - "color-space" : "display-p3", - "components" : { - "alpha" : "1.000", - "blue" : "0.133", - "green" : "0.133", - "red" : "0.133" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Services/RedeemCodeServices.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Services/RedeemCodeServices.swift deleted file mode 100644 index 85f1dc162b..0000000000 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Services/RedeemCodeServices.swift +++ /dev/null @@ -1,237 +0,0 @@ -// -// RedeemCodeServices.swift -// -// Copyright © 2023 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 Foundation -import Common - -public protocol DataBrokerProtectionRedeemUseCase { - /// Method to know if we should ask for an invite code when selecting the DataBrokerProtectionPackage - /// - /// - Returns: `true` if we need the user to enter an invite code - /// `false` in othercase - func shouldAskForInviteCode() -> Bool - - /// Tries to redeem an invite code. Throws in case there was an issue when trying to redeem the invite code. - /// - /// - Parameters: - /// - inviteCode: An invite code used to reedem access to data broker protection - func redeem(inviteCode: String) async throws - - /// Returns the auth header needed for the authenticated endpoints. - /// - /// - Returns: `String` a string that contains the bearer access token or nil - func getAuthHeader() -> String? -} - -public protocol AuthenticationRepository { - func getInviteCode() -> String? - func getAccessToken() -> String? - func getWaitlistTimestamp() -> Int? - - func save(accessToken: String) - func save(inviteCode: String) - func reset() -} - -public protocol DataBrokerProtectionAuthenticationService { - /// Reedems an invite code. This will return an access token that needs to be used to authenticate data broker protection requests. - /// - /// - Parameters: - /// - inviteCode: An invite code used to reedem access to data broker protection - /// - Returns: `accessToken: String` a string that contains the access token needed for future authenticated requests - func redeem(inviteCode: String) async throws -> String -} - -public final class RedeemUseCase: DataBrokerProtectionRedeemUseCase { - private let authenticationService: DataBrokerProtectionAuthenticationService - private let authenticationRepository: AuthenticationRepository - - public init(authenticationService: DataBrokerProtectionAuthenticationService = AuthenticationService(), - authenticationRepository: AuthenticationRepository = KeychainAuthenticationData()) { - self.authenticationService = authenticationService - self.authenticationRepository = authenticationRepository - } - - public func shouldAskForInviteCode() -> Bool { - authenticationRepository.getAccessToken() == nil - } - - public func redeem(inviteCode: String) async throws { - let accessToken = try await authenticationService.redeem(inviteCode: inviteCode) - authenticationRepository.save(accessToken: accessToken) - } - - public func getAuthHeader() -> String? { - ServicesAuthHeaderBuilder().getAuthHeader(authenticationRepository.getAccessToken()) - } -} - -public final class KeychainAuthenticationData: AuthenticationRepository { - enum DBPWaitlistKeys: String { - case accessTokenKey = "dbp:accessTokenKey" - case inviteCodeKey = "dbp:inviteCodeKey" - case waitlistTimestamp = "databrokerprotection.timestamp" - } - - /// Hack to prevent further damage on https://app.asana.com/0/1203581873609357/1206097441142301/f - lazy var keychainPrefix: String = { - let originalString = Bundle.main.bundleIdentifier ?? "com.duckduckgo" - let replacedString = originalString.replacingOccurrences(of: "DBP.backgroundAgent", with: "browser") - return replacedString - }() - - // Initialize this constant with the DBP API Dev Access Token on Bitwarden if you do not want to use the redeem endpoint. - private let developmentToken: String? = nil - - public init() {} - - public func getInviteCode() -> String? { - return getString(forField: .inviteCodeKey) - } - - public func getAccessToken() -> String? { - getString(forField: .accessTokenKey) - } - - public func save(accessToken: String) { - add(string: accessToken, forField: .accessTokenKey) - } - - public func save(inviteCode: String) { - add(string: inviteCode, forField: .inviteCodeKey) - } - - public func getWaitlistTimestamp() -> Int? { - guard let timestampString = getString(forField: .waitlistTimestamp) else { return nil } - return Int(timestampString) - } - - public func reset() { - deleteItem(forField: .inviteCodeKey) - deleteItem(forField: .accessTokenKey) - } - - // MARK: - Keychain Read - - private func getString(forField field: DBPWaitlistKeys) -> String? { - guard let data = retrieveData(forField: field), - let string = String(data: data, encoding: String.Encoding.utf8) else { - return nil - } - return string - } - - private func retrieveData(forField field: DBPWaitlistKeys) -> Data? { - var query = defaultAttributes(serviceName: keychainServiceName(for: field)) - query[kSecMatchLimit as String] = kSecMatchLimitOne - query[kSecReturnData as String] = true - - var item: CFTypeRef? - let status = SecItemCopyMatching(query as CFDictionary, &item) - guard status == errSecSuccess, let existingItem = item as? Data else { - return nil - } - - return existingItem - } - - // MARK: - Keychain Write - - private func add(string: String, forField field: DBPWaitlistKeys) { - guard let stringData = string.data(using: .utf8) else { - return - } - - deleteItem(forField: field) - add(data: stringData, forField: field) - } - - private func add(data: Data, forField field: DBPWaitlistKeys) { - var query = defaultAttributes(serviceName: keychainServiceName(for: field)) - query[kSecValueData as String] = data - SecItemAdd(query as CFDictionary, nil) - } - - private func deleteItem(forField field: DBPWaitlistKeys) { - let query = defaultAttributes(serviceName: keychainServiceName(for: field)) - SecItemDelete(query as CFDictionary) - } - - // MARK: - - - private func defaultAttributes(serviceName: String) -> [String: Any] { - return [ - kSecClass as String: kSecClassGenericPassword, - kSecAttrSynchronizable as String: false, - kSecUseDataProtectionKeychain as String: true, - kSecAttrAccessGroup as String: Bundle.main.appGroupName, - kSecAttrService as String: serviceName, - kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock - ] - } - - func keychainServiceName(for field: DBPWaitlistKeys) -> String { - [keychainPrefix, "waitlist", field.rawValue].joined(separator: ".") - } -} - -public enum AuthenticationError: Error, Equatable { - case noInviteCode - case cantGenerateURL - case noAuthToken - case issueRedeemingInviteCode(error: String) -} - -struct RedeemResponse: Codable { - enum CodingKeys: String, CodingKey { - case accessToken - case message - } - - let accessToken: String? - let message: String? -} - -public struct AuthenticationService: DataBrokerProtectionAuthenticationService { - private let urlSession: URLSession - private let settings: DataBrokerProtectionSettings - - public init(urlSession: URLSession = URLSession.shared, settings: DataBrokerProtectionSettings = DataBrokerProtectionSettings()) { - self.urlSession = urlSession - self.settings = settings - } - - public func redeem(inviteCode: String) async throws -> String { - let redeemURL = settings.selectedEnvironment.endpointURL.appendingPathComponent("dbp/redeem") - - guard let url = URL(string: "\(redeemURL.absoluteString)?code=\(inviteCode)") else { - throw AuthenticationError.cantGenerateURL - } - var request = URLRequest(url: url) - request.httpMethod = "POST" - let (data, _) = try await urlSession.data(for: request) - - let result = try JSONDecoder().decode(RedeemResponse.self, from: data) - - if let accessToken = result.accessToken { - return accessToken - } else { - throw AuthenticationError.issueRedeemingInviteCode(error: result.message ?? "Unknown") - } - } -} diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/DataBrokerProtectionBrokerUpdater.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/BrokerManaging/DataBrokerProtectionBrokerUpdater.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/DataBrokerProtectionBrokerUpdater.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/BrokerManaging/DataBrokerProtectionBrokerUpdater.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/CCF/ActionsHandler.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/CCF/ActionsHandler.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/CCF/ActionsHandler.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/CCF/ActionsHandler.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/CCF/DataBrokerProtectionErrors.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/CCF/DataBrokerProtectionErrors.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/CCF/DataBrokerProtectionErrors.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/CCF/DataBrokerProtectionErrors.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/CCF/DataBrokerProtectionFeature.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/CCF/DataBrokerProtectionFeature.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/CCF/DataBrokerProtectionFeature.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/CCF/DataBrokerProtectionFeature.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/CCF/DataBrokerProtectionUtils.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/CCF/DataBrokerProtectionUtils.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/CCF/DataBrokerProtectionUtils.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/CCF/DataBrokerProtectionUtils.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Services/HTTPUtils.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/CCF/HTTPUtils.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Services/HTTPUtils.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/CCF/HTTPUtils.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/CCF/WebViewHandler.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/CCF/WebViewHandler.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/CCF/WebViewHandler.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/CCF/WebViewHandler.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Action.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/Action.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Action.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/Action.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/Actions/Click.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Click.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/Actions/Click.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/EmailConfirmation.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/Actions/EmailConfirmation.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/EmailConfirmation.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/Actions/EmailConfirmation.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Expectaction.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/Actions/Expectaction.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Expectaction.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/Actions/Expectaction.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Extract.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/Actions/Extract.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Extract.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/Actions/Extract.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/FillForm.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/Actions/FillForm.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/FillForm.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/Actions/FillForm.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/GetCaptchaInfo.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/Actions/GetCaptchaInfo.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/GetCaptchaInfo.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/Actions/GetCaptchaInfo.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Navigate.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/Actions/Navigate.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/Navigate.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/Actions/Navigate.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/SolveCaptcha.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/Actions/SolveCaptcha.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Actions/SolveCaptcha.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/Actions/SolveCaptcha.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/AttemptInformation.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/AttemptInformation.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/AttemptInformation.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/AttemptInformation.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/BrokerJobData.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/BrokerJobData.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/BrokerJobData.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/BrokerJobData.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/BrokerProfileQueryData.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/BrokerProfileQueryData.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/BrokerProfileQueryData.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/BrokerProfileQueryData.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/CCFRequestParameters.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/CCFRequestParameters.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/CCFRequestParameters.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/CCFRequestParameters.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/CCFResponseParameters.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/CCFResponseParameters.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/CCFResponseParameters.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/CCFResponseParameters.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/DataBroker.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/DataBroker.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/DataBroker.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/DataBroker.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/DataBrokerProtectionProfile.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/DataBrokerProtectionProfile.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/DataBrokerProtectionProfile.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/DataBrokerProtectionProfile.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/ExtractedProfile.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/ExtractedProfile.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/ExtractedProfile.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/ExtractedProfile.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/HistoryEvent.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/HistoryEvent.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/HistoryEvent.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/HistoryEvent.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/PageElement.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/PageElement.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/PageElement.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/PageElement.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/ProfileQuery.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/ProfileQuery.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/ProfileQuery.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/ProfileQuery.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Step.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/Step.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/Step.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Model/Step.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerExecutionConfig.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/OperationQueueManaging/DataBrokerExecutionConfig.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerExecutionConfig.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/OperationQueueManaging/DataBrokerExecutionConfig.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerOperationsCreator.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/OperationQueueManaging/DataBrokerOperationsCreator.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerOperationsCreator.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/OperationQueueManaging/DataBrokerOperationsCreator.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionQueueManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/OperationQueueManaging/DataBrokerProtectionQueueManager.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionQueueManager.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/OperationQueueManaging/DataBrokerProtectionQueueManager.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/DataBrokerJob.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Operations/DataBrokerJob.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/DataBrokerJob.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Operations/DataBrokerJob.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/DataBrokerJobRunner.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Operations/DataBrokerJobRunner.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/DataBrokerJobRunner.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Operations/DataBrokerJobRunner.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/DataBrokerJobRunnerProvider.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Operations/DataBrokerJobRunnerProvider.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/DataBrokerJobRunnerProvider.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Operations/DataBrokerJobRunnerProvider.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/DataBrokerOperation.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Operations/DataBrokerOperation.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/DataBrokerOperation.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Operations/DataBrokerOperation.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/DataBrokerProfileQueryOperationManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Operations/DataBrokerProfileQueryOperationManager.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/DataBrokerProfileQueryOperationManager.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Operations/DataBrokerProfileQueryOperationManager.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/OperationPreferredDateCalculator.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Operations/OperationPreferredDateCalculator.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/OperationPreferredDateCalculator.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Operations/OperationPreferredDateCalculator.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/OperationPreferredDateUpdater.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Operations/OperationPreferredDateUpdater.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/OperationPreferredDateUpdater.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Operations/OperationPreferredDateUpdater.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/OperationRetriesCalculatorUseCase.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Operations/OperationRetriesCalculatorUseCase.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/OperationRetriesCalculatorUseCase.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Operations/OperationRetriesCalculatorUseCase.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/OptOutJob.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Operations/OptOutJob.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/OptOutJob.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Operations/OptOutJob.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/ParentChildRelationship/MismatchCalculator.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Operations/ParentChildRelationship/MismatchCalculator.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/ParentChildRelationship/MismatchCalculator.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Operations/ParentChildRelationship/MismatchCalculator.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/ScanJob.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Operations/ScanJob.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Operations/ScanJob.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/Operations/ScanJob.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Services/CaptchaService.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/OptOutServices/CaptchaService.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Services/CaptchaService.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/OptOutServices/CaptchaService.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Services/EmailService.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/OptOutServices/EmailService.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Services/EmailService.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/OptOutServices/EmailService.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/DataBrokerProtectionCryptoProvider.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/SecureVaultStorage/DataBrokerProtectionCryptoProvider.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/DataBrokerProtectionCryptoProvider.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/SecureVaultStorage/DataBrokerProtectionCryptoProvider.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Database/DataBrokerProtectionDatabase.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/SecureVaultStorage/DataBrokerProtectionDatabase.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Database/DataBrokerProtectionDatabase.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/SecureVaultStorage/DataBrokerProtectionDatabase.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/DataBrokerProtectionDatabaseMigrationsProvider.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/SecureVaultStorage/DataBrokerProtectionDatabaseMigrationsProvider.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/DataBrokerProtectionDatabaseMigrationsProvider.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/SecureVaultStorage/DataBrokerProtectionDatabaseMigrationsProvider.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/DataBrokerProtectionDatabaseProvider.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/SecureVaultStorage/DataBrokerProtectionDatabaseProvider.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/DataBrokerProtectionDatabaseProvider.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/SecureVaultStorage/DataBrokerProtectionDatabaseProvider.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/DataBrokerProtectionKeyStoreProvider.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/SecureVaultStorage/DataBrokerProtectionKeyStoreProvider.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/DataBrokerProtectionKeyStoreProvider.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/SecureVaultStorage/DataBrokerProtectionKeyStoreProvider.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/DataBrokerProtectionSecureVault.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/SecureVaultStorage/DataBrokerProtectionSecureVault.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/DataBrokerProtectionSecureVault.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/SecureVaultStorage/DataBrokerProtectionSecureVault.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Database/DataBrokerProtectionSecureVaultErrorReporter.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/SecureVaultStorage/DataBrokerProtectionSecureVaultErrorReporter.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Database/DataBrokerProtectionSecureVaultErrorReporter.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/SecureVaultStorage/DataBrokerProtectionSecureVaultErrorReporter.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/DatabaseSchema.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/SecureVaultStorage/DatabaseSchema.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/DatabaseSchema.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/SecureVaultStorage/DatabaseSchema.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/Mappers.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/SecureVaultStorage/Mappers.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/Mappers.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/SecureVaultStorage/Mappers.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/SchedulerSchema.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/SecureVaultStorage/SchedulerSchema.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Storage/SchedulerSchema.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/SecureVaultStorage/SchedulerSchema.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/UI/DBPUICommunicationLayer.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UIWeb/DBPUICommunicationLayer.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/UI/DBPUICommunicationLayer.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UIWeb/DBPUICommunicationLayer.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/DBPUICommunicationModel.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UIWeb/DBPUICommunicationModel.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/DBPUICommunicationModel.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UIWeb/DBPUICommunicationModel.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/UI/DBPUIWebConfiguration.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UIWeb/DBPUIWebConfiguration.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/UI/DBPUIWebConfiguration.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UIWeb/DBPUIWebConfiguration.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/UI/DataBrokerProtectionWebUIURLSettings.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UIWeb/DataBrokerProtectionWebUIURLSettings.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/UI/DataBrokerProtectionWebUIURLSettings.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UIWeb/DataBrokerProtectionWebUIURLSettings.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/UI/UIMapper.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UIWeb/UIMapper.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/UI/UIMapper.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UIWeb/UIMapper.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/CodableExtension.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UtilsShared/CodableExtension.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/CodableExtension.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UtilsShared/CodableExtension.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/DataBrokerDebugFlag.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UtilsShared/DataBrokerDebugFlag.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/DataBrokerDebugFlag.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UtilsShared/DataBrokerDebugFlag.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/DataBrokerProtectionNotifications.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UtilsShared/DataBrokerProtectionNotifications.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/DataBrokerProtectionNotifications.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UtilsShared/DataBrokerProtectionNotifications.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/DataBrokerProtectionSettings.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UtilsShared/DataBrokerProtectionSettings.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/DataBrokerProtectionSettings.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UtilsShared/DataBrokerProtectionSettings.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/Logger+DataBrokerProtection.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UtilsShared/Logger+DataBrokerProtection.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/Logger+DataBrokerProtection.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UtilsShared/Logger+DataBrokerProtection.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/ServicesAuthHeaderBuilder.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UtilsShared/ServicesAuthHeaderBuilder.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/ServicesAuthHeaderBuilder.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UtilsShared/ServicesAuthHeaderBuilder.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/StringExtension.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UtilsShared/StringExtension.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/StringExtension.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Shared/UtilsShared/StringExtension.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/UI/InviteCodeFlow/DataBrokerProtectionInviteCodeViewModels.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/UI/InviteCodeFlow/DataBrokerProtectionInviteCodeViewModels.swift deleted file mode 100644 index 18d538532e..0000000000 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/UI/InviteCodeFlow/DataBrokerProtectionInviteCodeViewModels.swift +++ /dev/null @@ -1,120 +0,0 @@ -// -// DataBrokerProtectionInviteCodeViewModels.swift -// -// Copyright © 2023 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 SwiftUI -import SwiftUIExtensions - -protocol DataBrokerProtectionInviteCodeViewModelDelegate: AnyObject { - func dataBrokerProtectionInviteCodeViewModelDidReedemSuccessfully(_ viewModel: DataBrokerProtectionInviteCodeViewModel) - func dataBrokerProtectionInviteCodeViewModelDidCancel(_ viewModel: DataBrokerProtectionInviteCodeViewModel) -} - -final class DataBrokerProtectionInviteCodeViewModel: InviteCodeViewModel { - - private let redeemUseCase: DataBrokerProtectionRedeemUseCase - private weak var delegate: DataBrokerProtectionInviteCodeViewModelDelegate? - - var titleText: String { - "Personal Information Removal" - } - - var messageText: String { - "Enter your Invite Code to get started." - } - - var textFieldPlaceholder: String { - "Code" - } - - var cancelButtonText: String { - "Cancel" - } - - var confirmButtonText: String { - "Continue" - } - - @Published var textFieldText: String = "" { - didSet { - if oldValue != textFieldText { - textFieldText = textFieldText.uppercased() - } - } - } - - private let errorMessageText: String = "We didn’t recognize this Invite Code." - - @Published var errorText: String? - - @Published var showProgressView = false - - init(delegate: DataBrokerProtectionInviteCodeViewModelDelegate, - authenticationRepository: AuthenticationRepository = KeychainAuthenticationData(), - authenticationService: DataBrokerProtectionAuthenticationService = AuthenticationService()) { - self.delegate = delegate - self.redeemUseCase = RedeemUseCase(authenticationService: authenticationService, authenticationRepository: authenticationRepository) - } - - @MainActor - func onConfirm() async { - errorText = nil - showProgressView = true - do { - try await redeemUseCase.redeem(inviteCode: textFieldText) - } catch { - errorText = errorMessageText - showProgressView = false - return - } - showProgressView = false - delegate?.dataBrokerProtectionInviteCodeViewModelDidReedemSuccessfully(self) - } - - func onCancel() { - delegate?.dataBrokerProtectionInviteCodeViewModelDidCancel(self) - } -} - -protocol DataBrokerProtectionInviteCodeSuccessViewModelDelegate: AnyObject { - func dataBrokerProtectionInviteCodeSuccessViewModelDidConfirm(_ viewModel: DataBrokerProtectionInviteCodeSuccessViewModel) -} - -final class DataBrokerProtectionInviteCodeSuccessViewModel: InviteCodeSuccessViewModel { - - private weak var delegate: DataBrokerProtectionInviteCodeSuccessViewModelDelegate? - - var titleText: String { - "Personal Information Removal" - } - - var messageText: String { - "Data brokers and people-search sites publish personal info online. Discover where you’re exposed and automatically remove" - } - - var confirmButtonText: String { - "Continue" - } - - init(delegate: DataBrokerProtectionInviteCodeSuccessViewModelDelegate) { - self.delegate = delegate - } - - func onConfirm() { - delegate?.dataBrokerProtectionInviteCodeSuccessViewModelDidConfirm(self) - } -} diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/UI/InviteCodeFlow/DataBrokerProtectionInviteDialogsView.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/UI/InviteCodeFlow/DataBrokerProtectionInviteDialogsView.swift deleted file mode 100644 index 58c47086d1..0000000000 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/UI/InviteCodeFlow/DataBrokerProtectionInviteDialogsView.swift +++ /dev/null @@ -1,74 +0,0 @@ -// -// DataBrokerProtectionInviteDialogsView.swift -// -// Copyright © 2023 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 SwiftUI -import SwiftUIExtensions - -enum DataBrokerProtectionInviteDialogKind { - case codeEntry, success -} - -public struct DataBrokerProtectionInviteDialogsView: View { - - @ObservedObject var viewModel: DataBrokerProtectionInviteDialogsViewModel - - public init(viewModel: DataBrokerProtectionInviteDialogsViewModel) { - self.viewModel = viewModel - } - - public var body: some View { - switch viewModel.currentDialogKind { - case .codeEntry: - let codeViewModel = DataBrokerProtectionInviteCodeViewModel(delegate: viewModel) - InviteCodeView(viewModel: codeViewModel) - case .success: - let successViewModel = DataBrokerProtectionInviteCodeSuccessViewModel(delegate: viewModel) - InviteCodeSuccessView(viewModel: successViewModel) - } - } -} - -public protocol DataBrokerProtectionInviteDialogsViewModelDelegate: AnyObject { - func dataBrokerProtectionInviteDialogsViewModelDidReedemSuccessfully(_ viewModel: DataBrokerProtectionInviteDialogsViewModel) - func dataBrokerProtectionInviteDialogsViewModelDidCancel(_ viewModel: DataBrokerProtectionInviteDialogsViewModel) -} - -public final class DataBrokerProtectionInviteDialogsViewModel: ObservableObject { - @Published var currentDialogKind: DataBrokerProtectionInviteDialogKind = .codeEntry - private weak var delegate: DataBrokerProtectionInviteDialogsViewModelDelegate? - - public init(delegate: DataBrokerProtectionInviteDialogsViewModelDelegate) { - self.delegate = delegate - } -} - -extension DataBrokerProtectionInviteDialogsViewModel: DataBrokerProtectionInviteCodeViewModelDelegate { - func dataBrokerProtectionInviteCodeViewModelDidReedemSuccessfully(_ viewModel: DataBrokerProtectionInviteCodeViewModel) { - currentDialogKind = .success - } - - func dataBrokerProtectionInviteCodeViewModelDidCancel(_ viewModel: DataBrokerProtectionInviteCodeViewModel) { - delegate?.dataBrokerProtectionInviteDialogsViewModelDidCancel(self) - } -} - -extension DataBrokerProtectionInviteDialogsViewModel: DataBrokerProtectionInviteCodeSuccessViewModelDelegate { - func dataBrokerProtectionInviteCodeSuccessViewModelDidConfirm(_ viewModel: DataBrokerProtectionInviteCodeSuccessViewModel) { - delegate?.dataBrokerProtectionInviteDialogsViewModelDidReedemSuccessfully(self) - } -} diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/BackgroundAgent/DataBrokerProtectionAgentManager.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/BackgroundAgent/DataBrokerProtectionAgentManager.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionBackgroundActivityScheduler.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/BackgroundAgent/DataBrokerProtectionBackgroundActivityScheduler.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionBackgroundActivityScheduler.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/BackgroundAgent/DataBrokerProtectionBackgroundActivityScheduler.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Bundle/BundleExtension.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Bundle/BundleExtension.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Bundle/BundleExtension.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Bundle/BundleExtension.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Database/DataBrokerProtectionDataManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/DataCaching/DataBrokerProtectionDataManager.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Database/DataBrokerProtectionDataManager.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/DataCaching/DataBrokerProtectionDataManager.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/BrowserWindowManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Debug/BrowserWindowManager.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/BrowserWindowManager.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Debug/BrowserWindowManager.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBaseBrowser/DataBrokerDatabaseBrowserView.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Debug/DataBaseBrowser/DataBrokerDatabaseBrowserView.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBaseBrowser/DataBrokerDatabaseBrowserView.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Debug/DataBaseBrowser/DataBrokerDatabaseBrowserView.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBaseBrowser/DataBrokerDatabaseBrowserViewController.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Debug/DataBaseBrowser/DataBrokerDatabaseBrowserViewController.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBaseBrowser/DataBrokerDatabaseBrowserViewController.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Debug/DataBaseBrowser/DataBrokerDatabaseBrowserViewController.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBaseBrowser/DataBrokerDatabaseBrowserViewModel.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Debug/DataBaseBrowser/DataBrokerDatabaseBrowserViewModel.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBaseBrowser/DataBrokerDatabaseBrowserViewModel.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Debug/DataBaseBrowser/DataBrokerDatabaseBrowserViewModel.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBrokerRunCustomJSONView.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Debug/DataBrokerRunCustomJSONView.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBrokerRunCustomJSONView.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Debug/DataBrokerRunCustomJSONView.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBrokerRunCustomJSONViewController.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Debug/DataBrokerRunCustomJSONViewController.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBrokerRunCustomJSONViewController.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Debug/DataBrokerRunCustomJSONViewController.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBrokerRunCustomJSONViewModel.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Debug/DataBrokerRunCustomJSONViewModel.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBrokerRunCustomJSONViewModel.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Debug/DataBrokerRunCustomJSONViewModel.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DebugScanJob.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Debug/DebugScanJob.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DebugScanJob.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Debug/DebugScanJob.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/ForceOptOut/DataBrokerForceOptOutView.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Debug/ForceOptOut/DataBrokerForceOptOutView.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/ForceOptOut/DataBrokerForceOptOutView.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Debug/ForceOptOut/DataBrokerForceOptOutView.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/ForceOptOut/DataBrokerForceOptOutViewController.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Debug/ForceOptOut/DataBrokerForceOptOutViewController.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/ForceOptOut/DataBrokerForceOptOutViewController.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Debug/ForceOptOut/DataBrokerForceOptOutViewController.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/ForceOptOut/DataBrokerForceOptOutViewModel.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Debug/ForceOptOut/DataBrokerForceOptOutViewModel.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/ForceOptOut/DataBrokerForceOptOutViewModel.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Debug/ForceOptOut/DataBrokerForceOptOutViewModel.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/IPC/DataBrokerProtectionAppToAgentInterface.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/IPC/DataBrokerProtectionAppToAgentInterface.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/IPC/DataBrokerProtectionAppToAgentInterface.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/IPC/DataBrokerProtectionAppToAgentInterface.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/IPC/DataBrokerProtectionIPCClient.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/IPC/DataBrokerProtectionIPCClient.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/IPC/DataBrokerProtectionIPCClient.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/IPC/DataBrokerProtectionIPCClient.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/IPC/DataBrokerProtectionIPCServer.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/IPC/DataBrokerProtectionIPCServer.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/IPC/DataBrokerProtectionIPCServer.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/IPC/DataBrokerProtectionIPCServer.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/PrivacyConfiguration/ConfigurationManager.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/PrivacyConfiguration/ConfigurationManager.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/PrivacyConfiguration/ConfigurationStore.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/PrivacyConfiguration/ConfigurationStore.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPAgentConfigurationURLProvider.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/PrivacyConfiguration/DBPAgentConfigurationURLProvider.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPAgentConfigurationURLProvider.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/PrivacyConfiguration/DBPAgentConfigurationURLProvider.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPPrivacyConfigurationManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/PrivacyConfiguration/DBPPrivacyConfigurationManager.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPPrivacyConfigurationManager.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/PrivacyConfiguration/DBPPrivacyConfigurationManager.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DBPMocks.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/PrivacyConfiguration/DBPPrivacyConfigurationMocks.swift similarity index 96% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DBPMocks.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/PrivacyConfiguration/DBPPrivacyConfigurationMocks.swift index 43909c4691..2fcc3364cb 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DBPMocks.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/PrivacyConfiguration/DBPPrivacyConfigurationMocks.swift @@ -1,5 +1,5 @@ // -// DBPMocks.swift +// DBPPrivacyConfigurationMocks.swift // // Copyright © 2023 DuckDuckGo. All rights reserved. // diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/StatusItem/StatusBarMenu.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/StatusItem/StatusBarMenu.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/StatusItem/StatusBarMenu.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/StatusItem/StatusBarMenu.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/StatusItem/StatusBarMenuDebugInfoViewModel.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/StatusItem/StatusBarMenuDebugInfoViewModel.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/StatusItem/StatusBarMenuDebugInfoViewModel.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/StatusItem/StatusBarMenuDebugInfoViewModel.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/StatusItem/StatusBarPopover.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/StatusItem/StatusBarPopover.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/StatusItem/StatusBarPopover.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/StatusItem/StatusBarPopover.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/StatusItem/StatusBarPopoverView.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/StatusItem/StatusBarPopoverView.swift similarity index 98% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/StatusItem/StatusBarPopoverView.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/StatusItem/StatusBarPopoverView.swift index 2e9199dc21..a45410aa76 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/StatusItem/StatusBarPopoverView.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/StatusItem/StatusBarPopoverView.swift @@ -18,6 +18,7 @@ import SwiftUI import Combine +import SwiftUIExtensions struct StatusBarPopoverView: View { let viewModel: StatusBarMenuDebugInfoViewModel diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/DBPUIViewModel.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/UINative/DBPUIViewModel.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/DBPUIViewModel.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/UINative/DBPUIViewModel.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/UI/DataBrokerProtectionViewController.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/UINative/DataBrokerProtectionViewController.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/UI/DataBrokerProtectionViewController.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/UINative/DataBrokerProtectionViewController.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/UserNotifications/DataBrokerProtectionUserNotificationService.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/UserNotifications/DataBrokerProtectionUserNotificationService.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/UserNotifications/DataBrokerProtectionUserNotificationService.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/UserNotifications/DataBrokerProtectionUserNotificationService.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/DataBrokerProtectionAgentStopper.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Utils/DataBrokerProtectionAgentStopper.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/DataBrokerProtectionAgentStopper.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Utils/DataBrokerProtectionAgentStopper.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/DataBrokerProtectionBundleExtension.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Utils/DataBrokerProtectionBundleExtension.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/DataBrokerProtectionBundleExtension.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Utils/DataBrokerProtectionBundleExtension.swift diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/DataBrokerProtectionSleepObserver.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Utils/DataBrokerProtectionSleepObserver.swift similarity index 100% rename from LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/DataBrokerProtectionSleepObserver.swift rename to LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/macOS/Utils/DataBrokerProtectionSleepObserver.swift diff --git a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/AuthenticationServiceTests.swift b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/AuthenticationServiceTests.swift deleted file mode 100644 index de06ff28e0..0000000000 --- a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/AuthenticationServiceTests.swift +++ /dev/null @@ -1,111 +0,0 @@ -// -// AuthenticationServiceTests.swift -// -// Copyright © 2023 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 XCTest -import Foundation -@testable import DataBrokerProtection - -final class AuthenticationServiceTests: XCTestCase { - - enum MockError: Error { - case someError - } - - private var mockURLSession: URLSession { - let testConfiguration = URLSessionConfiguration.default - testConfiguration.protocolClasses = [MockURLProtocol.self] - return URLSession(configuration: testConfiguration) - } - - override func tearDown() async throws { - MockURLProtocol.requestHandlerQueue.removeAll() - } - - func testWhenFailsOnNetworkingLayer_thenNoAuthenticationErrorIsThrown() async { - MockURLProtocol.requestHandlerQueue.append({ _ in throw MockError.someError }) - let sut = AuthenticationService(urlSession: mockURLSession, settings: DataBrokerProtectionSettings(defaults: .standard)) - - do { - _ = try await sut.redeem(inviteCode: "someInviteCode") - XCTFail("Expected an error to be thrown") - } catch { - if let error = error as? AuthenticationError { - XCTFail("Unexpected error thrown: \(error).") - } - } - } - - func testWhenFailsToRedeemWithNoAccessTokenAndMessage_thenUnknownErrorIsThrown() async { - let emptyMessageResponseData = try? JSONEncoder().encode(RedeemResponse(accessToken: nil, message: nil)) - let emptyMessageResponse: RequestHandler = { _ in (HTTPURLResponse.ok, emptyMessageResponseData) } - MockURLProtocol.requestHandlerQueue.append(emptyMessageResponse) - let sut = AuthenticationService(urlSession: mockURLSession, settings: DataBrokerProtectionSettings(defaults: .standard)) - - do { - _ = try await sut.redeem(inviteCode: "someInviteCode") - XCTFail("Expected an error to be thrown") - } catch { - if let error = error as? AuthenticationError, case .issueRedeemingInviteCode(let error) = error { - if error != "Unknown" { - XCTFail("Unexpected error thrown: \(error).") - } - - return - } - - XCTFail("Unexpected error thrown: \(error).") - } - } - - func testWhenFailsToRedeemWithMessage_thenIssueRedeemingInviteCodeIsThrown() async { - let message = "FAILURE CRITICAL: Error" - let failureCriticalResponseData = try? JSONEncoder().encode(RedeemResponse(accessToken: nil, message: message)) - let failureCriticalResponse: RequestHandler = { _ in (HTTPURLResponse.ok, failureCriticalResponseData) } - MockURLProtocol.requestHandlerQueue.append(failureCriticalResponse) - let sut = AuthenticationService(urlSession: mockURLSession, settings: DataBrokerProtectionSettings(defaults: .standard)) - - do { - _ = try await sut.redeem(inviteCode: "someInviteCode") - XCTFail("Expected an error to be thrown") - } catch { - if let error = error as? AuthenticationError, case .issueRedeemingInviteCode(let error) = error { - if error != message { - XCTFail("Unexpected error thrown: \(error).") - } - - return - } - - XCTFail("Unexpected error thrown: \(error).") - } - } - - func testWhenAccessTokenIsRetrieved_thenAccessTokenIsReturned() async { - let emptyMessageResponseData = try? JSONEncoder().encode(RedeemResponse(accessToken: "accessToken", message: nil)) - let emptyMessageResponse: RequestHandler = { _ in (HTTPURLResponse.ok, emptyMessageResponseData) } - MockURLProtocol.requestHandlerQueue.append(emptyMessageResponse) - let sut = AuthenticationService(urlSession: mockURLSession, settings: DataBrokerProtectionSettings(defaults: .standard)) - - do { - let accessToken = try await sut.redeem(inviteCode: "someInviteCode") - XCTAssertEqual(accessToken, "accessToken") - } catch { - XCTFail("Unexpected error thrown: \(error).") - } - } -} diff --git a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAuthenticationManagerTests.swift b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAuthenticationManagerTests.swift index b0603c8399..cf3625bc77 100644 --- a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAuthenticationManagerTests.swift +++ b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAuthenticationManagerTests.swift @@ -21,25 +21,21 @@ import XCTest class DataBrokerProtectionAuthenticationManagerTests: XCTestCase { var authenticationManager: DataBrokerProtectionAuthenticationManager! - var redeemUseCase: DataBrokerProtectionRedeemUseCase! var subscriptionManager: MockDataBrokerProtectionSubscriptionManaging! override func setUp() async throws { - redeemUseCase = MockRedeemUseCase() subscriptionManager = MockDataBrokerProtectionSubscriptionManaging() } override func tearDown() async throws { authenticationManager = nil - redeemUseCase = nil subscriptionManager = nil } func testUserNotAuthenticatedWhenSubscriptionManagerReturnsFalse() { subscriptionManager.userAuthenticatedValue = false - authenticationManager = DataBrokerProtectionAuthenticationManager(redeemUseCase: redeemUseCase, - subscriptionManager: subscriptionManager) + authenticationManager = DataBrokerProtectionAuthenticationManager(subscriptionManager: subscriptionManager) XCTAssertEqual(authenticationManager.isUserAuthenticated, false) } @@ -47,8 +43,7 @@ class DataBrokerProtectionAuthenticationManagerTests: XCTestCase { func testEmptyAccessTokenResultsInNilAuthHeader() { subscriptionManager.accessTokenValue = nil - authenticationManager = DataBrokerProtectionAuthenticationManager(redeemUseCase: redeemUseCase, - subscriptionManager: subscriptionManager) + authenticationManager = DataBrokerProtectionAuthenticationManager(subscriptionManager: subscriptionManager) XCTAssertNil(authenticationManager.getAuthHeader()) } @@ -56,8 +51,7 @@ class DataBrokerProtectionAuthenticationManagerTests: XCTestCase { func testUserAuthenticatedWhenSubscriptionManagerReturnsTrue() { subscriptionManager.userAuthenticatedValue = true - authenticationManager = DataBrokerProtectionAuthenticationManager(redeemUseCase: redeemUseCase, - subscriptionManager: subscriptionManager) + authenticationManager = DataBrokerProtectionAuthenticationManager(subscriptionManager: subscriptionManager) XCTAssertEqual(authenticationManager.isUserAuthenticated, true) } @@ -66,8 +60,7 @@ class DataBrokerProtectionAuthenticationManagerTests: XCTestCase { let accessToken = "validAccessToken" subscriptionManager.accessTokenValue = accessToken - authenticationManager = DataBrokerProtectionAuthenticationManager(redeemUseCase: redeemUseCase, - subscriptionManager: subscriptionManager) + authenticationManager = DataBrokerProtectionAuthenticationManager(subscriptionManager: subscriptionManager) XCTAssertNotNil(authenticationManager.getAuthHeader()) } @@ -75,8 +68,7 @@ class DataBrokerProtectionAuthenticationManagerTests: XCTestCase { func testValidEntitlementCheckWithSuccess() async { subscriptionManager.entitlementResultValue = true - authenticationManager = DataBrokerProtectionAuthenticationManager(redeemUseCase: redeemUseCase, - subscriptionManager: subscriptionManager) + authenticationManager = DataBrokerProtectionAuthenticationManager(subscriptionManager: subscriptionManager) do { let result = try await authenticationManager.hasValidEntitlement() XCTAssertTrue(result, "Entitlement check should return true for valid entitlement") @@ -88,8 +80,7 @@ class DataBrokerProtectionAuthenticationManagerTests: XCTestCase { func testValidEntitlementCheckWithSuccessFalse() async { subscriptionManager.entitlementResultValue = false - authenticationManager = DataBrokerProtectionAuthenticationManager(redeemUseCase: redeemUseCase, - subscriptionManager: subscriptionManager) + authenticationManager = DataBrokerProtectionAuthenticationManager(subscriptionManager: subscriptionManager) do { let result = try await authenticationManager.hasValidEntitlement() @@ -103,8 +94,7 @@ class DataBrokerProtectionAuthenticationManagerTests: XCTestCase { let mockError = NSError(domain: "TestErrorDomain", code: 123, userInfo: nil) subscriptionManager.entitlementError = mockError - authenticationManager = DataBrokerProtectionAuthenticationManager(redeemUseCase: redeemUseCase, - subscriptionManager: subscriptionManager) + authenticationManager = DataBrokerProtectionAuthenticationManager(subscriptionManager: subscriptionManager) do { _ = try await authenticationManager.hasValidEntitlement() diff --git a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionBackendServicePixelsTests.swift b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionBackendServicePixelsTests.swift index 9acad52f24..ed2362b4d3 100644 --- a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionBackendServicePixelsTests.swift +++ b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionBackendServicePixelsTests.swift @@ -21,7 +21,6 @@ import XCTest final class DataBrokerProtectionBackendServicePixelsTests: XCTestCase { let mockHandler = MockDataBrokerProtectionPixelsHandler() - let mockRepository = MockAuthenticationRepository() var settings: DataBrokerProtectionSettings! override func setUpWithError() throws { @@ -32,17 +31,14 @@ final class DataBrokerProtectionBackendServicePixelsTests: XCTestCase { override func tearDownWithError() throws { mockHandler.clear() - mockRepository.reset() settings = nil } func testSendHTTPErrorOnStagingAndNotWaitlist_thenValidatePixelSent() { settings.selectedEnvironment = .staging - mockRepository.shouldSendNilWaitlistTimeStamp = true let backendPixel = DefaultDataBrokerProtectionBackendServicePixels(pixelHandler: mockHandler, - settings: settings, - authRepository: mockRepository) + settings: settings) backendPixel.fireGenerateEmailHTTPError(statusCode: 200) let lastPixel = MockDataBrokerProtectionPixelsHandler.lastPixelsFired.last @@ -56,8 +52,7 @@ final class DataBrokerProtectionBackendServicePixelsTests: XCTestCase { func testSendHTTPErrorOnProductionAndWaitlist_thenValidatePixelSent() { settings.selectedEnvironment = .production let backendPixel = DefaultDataBrokerProtectionBackendServicePixels(pixelHandler: mockHandler, - settings: settings, - authRepository: mockRepository) + settings: settings) backendPixel.fireGenerateEmailHTTPError(statusCode: 123) let lastPixel = MockDataBrokerProtectionPixelsHandler.lastPixelsFired.last @@ -65,14 +60,12 @@ final class DataBrokerProtectionBackendServicePixelsTests: XCTestCase { XCTAssertNotNil(lastPixel) XCTAssertEqual(lastPixel?.params?[DataBrokerProtectionPixels.Consts.httpCode], "123", "Incorrect statusCode") XCTAssertEqual(lastPixel?.params?[DataBrokerProtectionPixels.Consts.environmentKey], "production", "Incorrect environment") - XCTAssertEqual(lastPixel?.params?[DataBrokerProtectionPixels.Consts.wasOnWaitlist], "true", "should be true") } func testSendEmptyAccessTokenOnProductionAndWaitlistFromEmailCallsite_thenValidatePixelSent() { settings.selectedEnvironment = .production let backendPixel = DefaultDataBrokerProtectionBackendServicePixels(pixelHandler: mockHandler, - settings: settings, - authRepository: mockRepository) + settings: settings) backendPixel.fireEmptyAccessToken(callSite: .getEmail) @@ -80,18 +73,15 @@ final class DataBrokerProtectionBackendServicePixelsTests: XCTestCase { XCTAssertNotNil(lastPixel) XCTAssertEqual(lastPixel?.params?[DataBrokerProtectionPixels.Consts.environmentKey], "production", "Incorrect environment") - XCTAssertEqual(lastPixel?.params?[DataBrokerProtectionPixels.Consts.wasOnWaitlist], "true", "should be true") XCTAssertEqual(lastPixel?.params?[DataBrokerProtectionPixels.Consts.backendServiceCallSite], "getEmail", "Should be getEmail") } func testSendEmptyAccessTokenOnStagingAndNotOnWaitlistFromCaptchaCallsite_thenValidatePixelSent() { settings.selectedEnvironment = .staging - mockRepository.shouldSendNilWaitlistTimeStamp = true let backendPixel = DefaultDataBrokerProtectionBackendServicePixels(pixelHandler: mockHandler, - settings: settings, - authRepository: mockRepository) + settings: settings) backendPixel.fireEmptyAccessToken(callSite: .submitCaptchaInformationRequest) diff --git a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift index 76747680a5..88aa13c6d0 100644 --- a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift +++ b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift @@ -428,96 +428,6 @@ final class CaptchaServiceMock: CaptchaServiceProtocol { } } -final class MockRedeemUseCase: DataBrokerProtectionRedeemUseCase { - var shouldSendNilAuthHeader = false - - func getAuthHeader() -> String? { - if shouldSendNilAuthHeader { - return nil - } - return "auth header" - } - - func shouldAskForInviteCode() -> Bool { - false - } - - func redeem(inviteCode: String) async throws { - - } - - func reset() { - shouldSendNilAuthHeader = false - } -} - -final class MockAuthenticationService: DataBrokerProtectionAuthenticationService { - - var wasRedeemCalled = false - var shouldThrow = false - - func redeem(inviteCode: String) async throws -> String { - wasRedeemCalled = true - if shouldThrow { - throw AuthenticationError.issueRedeemingInviteCode(error: "mock") - } - - return "accessToken" - } - - func reset() { - wasRedeemCalled = false - shouldThrow = false - } -} - -final class MockAuthenticationRepository: AuthenticationRepository { - var shouldSendNilInviteCode = false - var shouldSendNilAccessToken = false - var wasInviteCodeSaveCalled = false - var wasAccessTokenSaveCalled = false - var shouldSendNilWaitlistTimeStamp = false - - func getInviteCode() -> String? { - if shouldSendNilInviteCode { - return nil - } - - return "inviteCode" - } - - func getAccessToken() -> String? { - if shouldSendNilAccessToken { - return nil - } - - return "accessToken" - } - - func save(inviteCode: String) { - wasInviteCodeSaveCalled = true - } - - func save(accessToken: String) { - wasAccessTokenSaveCalled = true - } - - func getWaitlistTimestamp() -> Int? { - if shouldSendNilWaitlistTimeStamp { - return nil - } - return 123 - } - - func reset() { - shouldSendNilInviteCode = false - shouldSendNilAccessToken = false - wasInviteCodeSaveCalled = false - wasAccessTokenSaveCalled = false - shouldSendNilWaitlistTimeStamp = false - } -} - final class BrokerUpdaterRepositoryMock: BrokerUpdaterRepository { var wasSaveLatestAppVersionCheckCalled = false var lastCheckedVersion: String? diff --git a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/RedeemUseCaseTests.swift b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/RedeemUseCaseTests.swift deleted file mode 100644 index 25fa7defc0..0000000000 --- a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/RedeemUseCaseTests.swift +++ /dev/null @@ -1,101 +0,0 @@ -// -// RedeemUseCaseTests.swift -// -// Copyright © 2023 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 XCTest -import Foundation -@testable import DataBrokerProtection - -final class RedeemUseCaseTests: XCTestCase { - let repository = MockAuthenticationRepository() - let service = MockAuthenticationService() - - override func tearDown() async throws { - repository.reset() - service.reset() - } - - func testWhenAccessTokenIsNil_thenShouldAskForInviteCodeReturnsTrue() { - repository.shouldSendNilAccessToken = true - - let sut = RedeemUseCase(authenticationService: service, authenticationRepository: repository) - - XCTAssertTrue(sut.shouldAskForInviteCode()) - } - - func testWhenAccessTokenIsNotNil_thenShouldAskForInviteCodeReturnsFalse() { - repository.shouldSendNilAccessToken = false - - let sut = RedeemUseCase(authenticationService: service, authenticationRepository: repository) - - XCTAssertFalse(sut.shouldAskForInviteCode()) - } - - func testWhenRedeemSucceds_thenTokenIsSaved() async { - service.shouldThrow = false - let sut = RedeemUseCase(authenticationService: service, authenticationRepository: repository) - - try? await sut.redeem(inviteCode: "someInviteCode") - - XCTAssertTrue(repository.wasAccessTokenSaveCalled) - } - - func testWhenRedeemFails_thenTokenIsNotSaved() async { - service.shouldThrow = true - let sut = RedeemUseCase(authenticationService: service, authenticationRepository: repository) - - try? await sut.redeem(inviteCode: "someInviteCode") - - XCTAssertFalse(repository.wasAccessTokenSaveCalled) - } - - func testWhenGetAuthHeaderHasNoInviteCode_thenThrowsNoInviteCodeError() async { - repository.shouldSendNilInviteCode = true - repository.shouldSendNilAccessToken = true - - let sut = RedeemUseCase(authenticationService: service, authenticationRepository: repository) - - let accessToken = sut.getAuthHeader() - - XCTAssertNil(accessToken) - XCTAssertFalse(service.wasRedeemCalled) - XCTAssertFalse(repository.wasAccessTokenSaveCalled) - } - - func testWhenGetAuthHeadersHasEmptyAccessToken_thenWeReturnNil() async { - repository.shouldSendNilInviteCode = false - repository.shouldSendNilAccessToken = true - let sut = RedeemUseCase(authenticationService: service, authenticationRepository: repository) - - let result = sut.getAuthHeader() - - XCTAssertNil(result) - XCTAssertFalse(repository.wasAccessTokenSaveCalled) - } - - func testWhenGetAuthHeadersHasAccessToken_thenWeReturnHeaderWithoutRedeeming() async { - repository.shouldSendNilInviteCode = false - repository.shouldSendNilAccessToken = false - let sut = RedeemUseCase(authenticationService: service, authenticationRepository: repository) - - let accessToken = sut.getAuthHeader() - - XCTAssertEqual(accessToken, "bearer accessToken") - XCTAssertFalse(service.wasRedeemCalled) - XCTAssertFalse(repository.wasAccessTokenSaveCalled) - } -} diff --git a/LocalPackages/FeatureFlags/Package.swift b/LocalPackages/FeatureFlags/Package.swift index 2113246af1..ad20557f10 100644 --- a/LocalPackages/FeatureFlags/Package.swift +++ b/LocalPackages/FeatureFlags/Package.swift @@ -32,7 +32,7 @@ let package = Package( targets: ["FeatureFlags"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "233.1.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "234.0.0"), ], targets: [ // Targets are the basic building blocks of a package, defining a module or a test suite. diff --git a/LocalPackages/FeatureFlags/Sources/FeatureFlags/FeatureFlag.swift b/LocalPackages/FeatureFlags/Sources/FeatureFlags/FeatureFlag.swift index f117356bdf..b7937b99e7 100644 --- a/LocalPackages/FeatureFlags/Sources/FeatureFlags/FeatureFlag.swift +++ b/LocalPackages/FeatureFlags/Sources/FeatureFlags/FeatureFlag.swift @@ -57,13 +57,31 @@ public enum FeatureFlag: String, CaseIterable { case autcompleteTabs case webExtensions case syncSeamlessAccountSwitching + + case testExperiment } extension FeatureFlag: FeatureFlagDescribing { + public var cohortType: (any FeatureFlagCohortDescribing.Type)? { + switch self { + case .testExperiment: + return TestExperimentCohort.self + default: + return nil + } + } + + public enum TestExperimentCohort: String, FeatureFlagCohortDescribing { + case control + case treatment + } + public var supportsLocalOverriding: Bool { switch self { case .htmlNewTabPage, .autofillPartialFormSaves, .autcompleteTabs, .networkProtectionAppExclusions, .syncSeamlessAccountSwitching, .historyView, .webExtensions: return true + case .testExperiment: + return true case .debugMenu, .sslCertificatesBypass, .appendAtbToSerpQueries, @@ -114,6 +132,8 @@ extension FeatureFlag: FeatureFlagDescribing { return .internalOnly() case .syncSeamlessAccountSwitching: return .remoteReleasable(.subfeature(SyncSubfeature.seamlessAccountSwitching)) + case .testExperiment: + return .remoteReleasable(.subfeature(ExperimentTestSubfeatures.experimentTestAA)) } } } diff --git a/LocalPackages/HistoryView/Package.swift b/LocalPackages/HistoryView/Package.swift index 03d6ff902e..35f522e47f 100644 --- a/LocalPackages/HistoryView/Package.swift +++ b/LocalPackages/HistoryView/Package.swift @@ -32,7 +32,7 @@ let package = Package( targets: ["HistoryView"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "233.1.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "234.0.0"), .package(path: "../WebKitExtensions"), .package(path: "../UserScriptActionsManager"), .package(path: "../Utilities"), diff --git a/LocalPackages/NetworkProtectionMac/Package.swift b/LocalPackages/NetworkProtectionMac/Package.swift index f03b23f97f..af1544bb6f 100644 --- a/LocalPackages/NetworkProtectionMac/Package.swift +++ b/LocalPackages/NetworkProtectionMac/Package.swift @@ -33,7 +33,7 @@ let package = Package( .library(name: "VPNAppLauncher", targets: ["VPNAppLauncher"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "233.1.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "234.0.0"), .package(url: "https://github.com/airbnb/lottie-spm", exact: "4.4.3"), .package(path: "../AppLauncher"), .package(path: "../UDSHelper"), diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionProxy/Provider/TransparentProxyProvider.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionProxy/Provider/TransparentProxyProvider.swift index ba8ea1e8a5..077c3b2727 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionProxy/Provider/TransparentProxyProvider.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionProxy/Provider/TransparentProxyProvider.swift @@ -456,7 +456,7 @@ open class TransparentProxyProvider: NETransparentProxyProvider { case .block: return .block(dueTo: .appRule) case .exclude: - return .excludeFromVPN(dueTo: .domainRule) + return .excludeFromVPN(dueTo: .appRule) } } diff --git a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionProxy/Settings/TransparentProxySettings.swift b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionProxy/Settings/TransparentProxySettings.swift index 5e541ffeca..36989c2f48 100644 --- a/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionProxy/Settings/TransparentProxySettings.swift +++ b/LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionProxy/Settings/TransparentProxySettings.swift @@ -67,6 +67,20 @@ public final class TransparentProxySettings { } } + public var appRoutingRulesPublisher: AnyPublisher { + defaults.vpnProxyAppRoutingRulesPublisher + } + + public var excludedApps: [String] { + appRoutingRules.compactMap { (bundleID, rule) in + guard rule == .exclude else { + return nil + } + + return bundleID + } + } + public var excludedDomains: [String] { get { defaults.vpnProxyExcludedDomains @@ -77,6 +91,10 @@ public final class TransparentProxySettings { } } + public var excludedDomainsPublisher: AnyPublisher<[String], Never> { + defaults.vpnProxyExcludedDomainsPublisher + } + public var proxyAvailable: Bool { get { defaults.vpnProxyFeatureAvailable diff --git a/LocalPackages/NewTabPage/Package.swift b/LocalPackages/NewTabPage/Package.swift index be9d4259ab..b592fbefdb 100644 --- a/LocalPackages/NewTabPage/Package.swift +++ b/LocalPackages/NewTabPage/Package.swift @@ -32,7 +32,7 @@ let package = Package( targets: ["NewTabPage"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "233.1.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "234.0.0"), .package(path: "../WebKitExtensions"), .package(path: "../UserScriptActionsManager"), .package(path: "../Utilities"), diff --git a/LocalPackages/PreferencesUI/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/LocalPackages/PreferencesUI/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..919434a625 --- /dev/null +++ b/LocalPackages/PreferencesUI/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/LocalPackages/SubscriptionUI/Package.swift b/LocalPackages/SubscriptionUI/Package.swift index 1734f63edd..9db6763a40 100644 --- a/LocalPackages/SubscriptionUI/Package.swift +++ b/LocalPackages/SubscriptionUI/Package.swift @@ -13,7 +13,7 @@ let package = Package( targets: ["SubscriptionUI"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "233.1.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "234.0.0"), .package(path: "../PreferencesUI-macOS"), .package(path: "../SwiftUIExtensions"), .package(path: "../FeatureFlags") diff --git a/LocalPackages/UserScriptActionsManager/Package.swift b/LocalPackages/UserScriptActionsManager/Package.swift index 6984eb9b89..5e85659c13 100644 --- a/LocalPackages/UserScriptActionsManager/Package.swift +++ b/LocalPackages/UserScriptActionsManager/Package.swift @@ -31,7 +31,7 @@ let package = Package( targets: ["UserScriptActionsManager"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "233.1.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "234.0.0"), ], targets: [ .target( diff --git a/LocalPackages/WebKitExtensions/Package.swift b/LocalPackages/WebKitExtensions/Package.swift index 92235cf9c9..4f95aab3ff 100644 --- a/LocalPackages/WebKitExtensions/Package.swift +++ b/LocalPackages/WebKitExtensions/Package.swift @@ -32,7 +32,7 @@ let package = Package( ), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "233.1.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "234.0.0"), .package(path: "../AppKitExtensions") ], targets: [ diff --git a/UnitTests/Menus/MainMenuTests.swift b/UnitTests/Menus/MainMenuTests.swift index 5b9cd52ef4..5fa4e37e66 100644 --- a/UnitTests/Menus/MainMenuTests.swift +++ b/UnitTests/Menus/MainMenuTests.swift @@ -271,13 +271,11 @@ private class DummyFeatureFlagger: FeatureFlagger { return nil } - func getCohortIfEnabled(for featureFlag: Flag) -> (any FlagCohort)? where Flag: FeatureFlagExperimentDescribing { + func resolveCohort(for featureFlag: Flag, allowOverride: Bool) -> (any FeatureFlagCohortDescribing)? where Flag: FeatureFlagDescribing { return nil } - func getAllActiveExperiments() -> Experiments { - return [:] - } + var allActiveExperiments: Experiments = [:] } private class DummyAIChatConfig: AIChatMenuVisibilityConfigurable { diff --git a/UnitTests/TabExtensionsTests/ErrorPageTabExtensionTest.swift b/UnitTests/TabExtensionsTests/ErrorPageTabExtensionTest.swift index 885da505ae..792b8fd69f 100644 --- a/UnitTests/TabExtensionsTests/ErrorPageTabExtensionTest.swift +++ b/UnitTests/TabExtensionsTests/ErrorPageTabExtensionTest.swift @@ -540,7 +540,7 @@ class ChallangeSender: URLAuthenticationChallengeSender { class MockFeatureFlagger: FeatureFlagger { var internalUserDecider: InternalUserDecider = DefaultInternalUserDecider(store: MockInternalUserStoring()) var localOverrides: FeatureFlagLocalOverriding? - var cohort: (any FlagCohort)? + var cohort: (any FeatureFlagCohortDescribing)? var isFeatureOn = true func isFeatureOn(for featureFlag: Flag, allowOverride: Bool) -> Bool { @@ -551,11 +551,9 @@ class MockFeatureFlagger: FeatureFlagger { return nil } - func getCohortIfEnabled(for featureFlag: Flag) -> (any FlagCohort)? where Flag: FeatureFlagExperimentDescribing { + func resolveCohort(for featureFlag: Flag, allowOverride: Bool) -> (any FeatureFlagCohortDescribing)? where Flag: FeatureFlagDescribing { return cohort } - func getAllActiveExperiments() -> Experiments { - return [:] - } + var allActiveExperiments: Experiments = [:] }