From f3ac7552c0ca210470075babaf0eca6f45e9abd5 Mon Sep 17 00:00:00 2001 From: Alessandro Boron Date: Fri, 22 Nov 2024 14:01:53 +0100 Subject: [PATCH 01/13] wip --- Core/UserDefaultsPropertyWrapper.swift | 3 + DuckDuckGo-iOS.xcodeproj/project.pbxproj | 4 + DuckDuckGo/SettingsGeneralView.swift | 13 +++ .../SettingsViewModel+ThreatDetection.swift | 80 +++++++++++++++++++ DuckDuckGo/SettingsViewModel.swift | 1 + 5 files changed, 101 insertions(+) create mode 100644 DuckDuckGo/SettingsViewModel+ThreatDetection.swift diff --git a/Core/UserDefaultsPropertyWrapper.swift b/Core/UserDefaultsPropertyWrapper.swift index 7e218e5e33..86ec77f578 100644 --- a/Core/UserDefaultsPropertyWrapper.swift +++ b/Core/UserDefaultsPropertyWrapper.swift @@ -189,6 +189,9 @@ public struct UserDefaultsWrapper { // TipKit case resetTipKitOnNextLaunch = "com.duckduckgo.ios.tipKit.resetOnNextLaunch" + + // Malicious Site Protection + case maliciousSiteProtectionEnabled = "com.duckduckgo.ios.maliciousSiteProtection.enabled" } private let key: Key diff --git a/DuckDuckGo-iOS.xcodeproj/project.pbxproj b/DuckDuckGo-iOS.xcodeproj/project.pbxproj index fbccb32920..4ac6d16f01 100644 --- a/DuckDuckGo-iOS.xcodeproj/project.pbxproj +++ b/DuckDuckGo-iOS.xcodeproj/project.pbxproj @@ -860,6 +860,7 @@ 9F9A92342C86B42B001D036D /* AppIconPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F9A92332C86B42B001D036D /* AppIconPicker.swift */; }; 9F9EE4CE2C377D4900D4118E /* OnboardingFirePixelMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F9EE4CC2C377D3F00D4118E /* OnboardingFirePixelMock.swift */; }; 9F9EE4D42C37BB1300D4118E /* OnboardingView+Landing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F9EE4D32C37BB1300D4118E /* OnboardingView+Landing.swift */; }; + 9F9F325A2CEFA75100211B49 /* SettingsViewModel+ThreatDetection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F9F32592CEFA74600211B49 /* SettingsViewModel+ThreatDetection.swift */; }; 9FA5E44B2BF1AF3400BDEF02 /* SubscriptionContainerViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FA5E44A2BF1AF3400BDEF02 /* SubscriptionContainerViewFactory.swift */; }; 9FB027122C2526DD009EA190 /* OnboardingView+IntroDialogContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB027112C2526DD009EA190 /* OnboardingView+IntroDialogContent.swift */; }; 9FB027142C252E0C009EA190 /* OnboardingView+BrowsersComparisonContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB027132C252E0C009EA190 /* OnboardingView+BrowsersComparisonContent.swift */; }; @@ -2778,6 +2779,7 @@ 9F9A92332C86B42B001D036D /* AppIconPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIconPicker.swift; sourceTree = ""; }; 9F9EE4CC2C377D3F00D4118E /* OnboardingFirePixelMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingFirePixelMock.swift; sourceTree = ""; }; 9F9EE4D32C37BB1300D4118E /* OnboardingView+Landing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OnboardingView+Landing.swift"; sourceTree = ""; }; + 9F9F32592CEFA74600211B49 /* SettingsViewModel+ThreatDetection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SettingsViewModel+ThreatDetection.swift"; sourceTree = ""; }; 9FA5E44A2BF1AF3400BDEF02 /* SubscriptionContainerViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionContainerViewFactory.swift; sourceTree = ""; }; 9FB027112C2526DD009EA190 /* OnboardingView+IntroDialogContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OnboardingView+IntroDialogContent.swift"; sourceTree = ""; }; 9FB027132C252E0C009EA190 /* OnboardingView+BrowsersComparisonContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OnboardingView+BrowsersComparisonContent.swift"; sourceTree = ""; }; @@ -6135,6 +6137,7 @@ children = ( D6E83C5F2B22B3C9006C8AFB /* SettingsState.swift */, D6E83C2D2B1EA06E006C8AFB /* SettingsViewModel.swift */, + 9F9F32592CEFA74600211B49 /* SettingsViewModel+ThreatDetection.swift */, D6E83C552B21ECC1006C8AFB /* SettingsLegacyViewProvider.swift */, 85449EFC23FDA71F00512AAF /* KeyboardSettings.swift */, 4B53648926718D0E001AA041 /* EmailWaitlist.swift */, @@ -8444,6 +8447,7 @@ EEC02C142B0519DE0045CE11 /* NetworkProtectionVPNLocationViewModel.swift in Sources */, D63FF8962C1B67E9006DE24D /* YoutubeOverlayUserScript.swift in Sources */, 9F38A28C2D09BDE500EB100E /* SpecialErrorPageThreatProvider.swift in Sources */, + 9F9F325A2CEFA75100211B49 /* SettingsViewModel+ThreatDetection.swift in Sources */, F13B4BC01F180D8A00814661 /* TabsModel.swift in Sources */, 8598D2E02CEB98B500C45685 /* Favicons.swift in Sources */, 8598D2E12CEB98B500C45685 /* NotFoundCachingDownloader.swift in Sources */, diff --git a/DuckDuckGo/SettingsGeneralView.swift b/DuckDuckGo/SettingsGeneralView.swift index 115c88a0fb..fd62d911a0 100644 --- a/DuckDuckGo/SettingsGeneralView.swift +++ b/DuckDuckGo/SettingsGeneralView.swift @@ -85,6 +85,19 @@ struct SettingsGeneralView: View { SettingsCellView(label: UserText.settingsAssociatedApps, accessory: .toggle(isOn: viewModel.universalLinksBinding)) } + + if viewModel.threatDetectionSettingsViewModel.shouldShowMaliciousSiteProtectionSection { + Section( + header: Text(verbatim: "Malicious Site Protection"), + footer: Text(verbatim: "Disabling this feature can put your personal information at risk. ") + .foregroundColor(.red) + ) { + SettingsCellView(label: "Warn me when a webpage may be malicious or fraudulent", + accessory: .toggle(isOn: viewModel.threatDetectionSettingsViewModel.threatDetectionBinding)) + } + } + + } .applySettingsListModifiers(title: UserText.general, displayMode: .inline, diff --git a/DuckDuckGo/SettingsViewModel+ThreatDetection.swift b/DuckDuckGo/SettingsViewModel+ThreatDetection.swift new file mode 100644 index 0000000000..9f512ce903 --- /dev/null +++ b/DuckDuckGo/SettingsViewModel+ThreatDetection.swift @@ -0,0 +1,80 @@ +// +// SettingsViewModel+ThreatDetection.swift +// DuckDuckGo +// +// 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 Foundation +import Combine +import Core +import SwiftUI + +final class ThreatDetectionSettingsViewModel: ObservableObject { + @Published var shouldShowMaliciousSiteProtectionSection: Bool = false + + var threatDetectionBinding: Binding { + Binding( + get: { + self.manager.isEnabled + }, + set: { + self.manager.isEnabled = $0 + } + ) + } + + private let manager: ThreatDetetctionPreferencesManaging + private let featureChecker: ThreatDetectionSettingsChecking + + init( + manager: ThreatDetetctionPreferencesManaging = ThreatDetectionPreferencesManager(), + featureChecker: ThreatDetectionSettingsChecking = ThreatDetectionFeatureCheck() + ) { + self.manager = manager + self.featureChecker = featureChecker + shouldShowMaliciousSiteProtectionSection = featureChecker.isThreatDetectionSettingsEnabled + } +} + +protocol ThreatDetectionPreferencesStorage: AnyObject { + var isEnabled: Bool { get set } +} + +final class ThreatDetectionPreferencesUserDefaultsStore: ThreatDetectionPreferencesStorage { + @UserDefaultsWrapper(key: .threatDetectionEnabled, defaultValue: false) + var isEnabled: Bool +} + +protocol ThreatDetetctionPreferencesManaging: AnyObject { + var isEnabled: Bool { get set } +} + +final class ThreatDetectionPreferencesManager: ThreatDetetctionPreferencesManaging { + + @Published var isEnabled: Bool { + didSet { + store.isEnabled = isEnabled + print("~~~IS ENABLED: ", isEnabled) + } + } + + private let store: ThreatDetectionPreferencesStorage + + init(store: ThreatDetectionPreferencesStorage = ThreatDetectionPreferencesUserDefaultsStore()) { + self.store = store + self.isEnabled = store.isEnabled + } +} diff --git a/DuckDuckGo/SettingsViewModel.swift b/DuckDuckGo/SettingsViewModel.swift index 19d0c77a01..cc65fd5773 100644 --- a/DuckDuckGo/SettingsViewModel.swift +++ b/DuckDuckGo/SettingsViewModel.swift @@ -47,6 +47,7 @@ final class SettingsViewModel: ObservableObject { let privacyProDataReporter: PrivacyProDataReporting? let textZoomCoordinator: TextZoomCoordinating let aiChatSettings: AIChatSettingsProvider + @Published var threatDetectionSettingsViewModel: ThreatDetectionSettingsViewModel = ThreatDetectionSettingsViewModel() // Subscription Dependencies let subscriptionManager: SubscriptionManager From 6d35c2ee4a07a83ffdd505317697e8dbcdd7e347 Mon Sep 17 00:00:00 2001 From: Alessandro Boron Date: Mon, 16 Dec 2024 15:40:03 +0100 Subject: [PATCH 02/13] Add Malicious Site Protection Learn More URL --- Core/AppURLs.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/AppURLs.swift b/Core/AppURLs.swift index 56542c2066..b7e28f68d3 100644 --- a/Core/AppURLs.swift +++ b/Core/AppURLs.swift @@ -38,6 +38,7 @@ public extension URL { static let apps = URL(string: AppDeepLinkSchemes.quickLink.appending("\(ddg.host!)/apps?origin=funnel_app_ios"))! static let searchSettings = URL(string: AppDeepLinkSchemes.quickLink.appending("\(ddg.host!)/settings"))! static let autofillHelpPageLink = URL(string: AppDeepLinkSchemes.quickLink.appending("\(ddg.host!)/duckduckgo-help-pages/sync-and-backup/password-manager-security/"))! + static let maliciousSiteProtectionLearnMore = URL(string: AppDeepLinkSchemes.quickLink.appending("\(ddg.host!)/duckduckgo-help-pages/privacy/phishing-and-malware-protection/"))! static let surrogates = URL(string: "\(staticBase)/surrogates.txt")! From 5e65a8986c5844132376ede8941929ed5d9245fe Mon Sep 17 00:00:00 2001 From: Alessandro Boron Date: Mon, 16 Dec 2024 16:03:43 +0100 Subject: [PATCH 03/13] Add tests for Preferences Manager --- DuckDuckGo-iOS.xcodeproj/project.pbxproj | 4 + ...iousSiteProtectionPreferencesManager.swift | 24 +++++- ...iteProtectionPreferencesManagerTests.swift | 74 +++++++++++++++++++ 3 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 DuckDuckGoTests/MaliciousSiteProtection/MaliciousSiteProtectionPreferencesManagerTests.swift diff --git a/DuckDuckGo-iOS.xcodeproj/project.pbxproj b/DuckDuckGo-iOS.xcodeproj/project.pbxproj index 4ac6d16f01..a601bf3a0a 100644 --- a/DuckDuckGo-iOS.xcodeproj/project.pbxproj +++ b/DuckDuckGo-iOS.xcodeproj/project.pbxproj @@ -791,6 +791,7 @@ 9F0949A02D372AE60079291B /* MaliciousSiteProtectionDatasetsFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F09499F2D372ADB0079291B /* MaliciousSiteProtectionDatasetsFetcher.swift */; }; 9F0949A52D3898FF0079291B /* MaliciousSiteProtectionPreferencesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0949A42D3898F80079291B /* MaliciousSiteProtectionPreferencesManager.swift */; }; 9F0949A72D389F1E0079291B /* MaliciousSiteProtectionAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0949A62D389F180079291B /* MaliciousSiteProtectionAPI.swift */; }; + 9F06EB922D10740500905426 /* MaliciousSiteProtectionPreferencesManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F06EB912D10740500905426 /* MaliciousSiteProtectionPreferencesManagerTests.swift */; }; 9F16230B2CA0F0190093C4FC /* DebouncerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F16230A2CA0F0190093C4FC /* DebouncerTests.swift */; }; 9F1798572CD2443F0073018B /* AddToDockPromoViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F1798562CD2443F0073018B /* AddToDockPromoViewModelTests.swift */; }; 9F23B8012C2BC94400950875 /* OnboardingBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F23B8002C2BC94400950875 /* OnboardingBackground.swift */; }; @@ -2713,6 +2714,7 @@ 9F09499F2D372ADB0079291B /* MaliciousSiteProtectionDatasetsFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionDatasetsFetcher.swift; sourceTree = ""; }; 9F0949A42D3898F80079291B /* MaliciousSiteProtectionPreferencesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionPreferencesManager.swift; sourceTree = ""; }; 9F0949A62D389F180079291B /* MaliciousSiteProtectionAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionAPI.swift; sourceTree = ""; }; + 9F06EB912D10740500905426 /* MaliciousSiteProtectionPreferencesManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionPreferencesManagerTests.swift; sourceTree = ""; }; 9F16230A2CA0F0190093C4FC /* DebouncerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebouncerTests.swift; sourceTree = ""; }; 9F1798562CD2443F0073018B /* AddToDockPromoViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddToDockPromoViewModelTests.swift; sourceTree = ""; }; 9F23B8002C2BC94400950875 /* OnboardingBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingBackground.swift; sourceTree = ""; }; @@ -5382,6 +5384,7 @@ 9F5BEA782D4382430045E484 /* Mocks */, 9F5BEA792D4382430045E484 /* MaliciousSiteProtectionDatasetsFetcherTests.swift */, 9F5BEA7A2D4382430045E484 /* MaliciousSiteProtectionManagerTests.swift */, + 9F06EB912D10740500905426 /* MaliciousSiteProtectionPreferencesManagerTests.swift */, ); path = MaliciousSiteProtection; sourceTree = ""; @@ -8944,6 +8947,7 @@ 5650E36F2D3E8E5900D41ECF /* PageRefreshMonitorExtensionTests.swift in Sources */, C1106F312D0EFD8B0054A221 /* FreeTrialsFeatureFlagExperimentTests.swift in Sources */, 851DFD8A212C5EE800D95F20 /* TabSwitcherButtonTests.swift in Sources */, + 9F06EB922D10740500905426 /* MaliciousSiteProtectionPreferencesManagerTests.swift in Sources */, 98983096255B5019003339A2 /* BookmarksCachingSearchTests.swift in Sources */, D6B67A122C332B6E002122EB /* DuckPlayerMocks.swift in Sources */, 9FEA22352C327226006B03BF /* MockTimer.swift in Sources */, diff --git a/DuckDuckGo/MaliciousSiteProtection/UserPreferences/MaliciousSiteProtectionPreferencesManager.swift b/DuckDuckGo/MaliciousSiteProtection/UserPreferences/MaliciousSiteProtectionPreferencesManager.swift index 6a15ac496a..f7ea980bdc 100644 --- a/DuckDuckGo/MaliciousSiteProtection/UserPreferences/MaliciousSiteProtectionPreferencesManager.swift +++ b/DuckDuckGo/MaliciousSiteProtection/UserPreferences/MaliciousSiteProtectionPreferencesManager.swift @@ -19,6 +19,16 @@ import Foundation import Combine +import Core + +protocol MaliciousSiteProtectionPreferencesStorage: AnyObject { + var isEnabled: Bool { get set } +} + +final class MaliciousSiteProtectionPreferencesUserDefaultsStore: MaliciousSiteProtectionPreferencesStorage { + @UserDefaultsWrapper(key: .maliciousSiteProtectionEnabled, defaultValue: false) + var isEnabled: Bool +} protocol MaliciousSiteProtectionPreferencesPublishing { var isMaliciousSiteProtectionOnPublisher: AnyPublisher { get } @@ -37,12 +47,18 @@ protocol MaliciousSiteProtectionPreferencesWriting { typealias MaliciousSiteProtectionPreferencesManaging = MaliciousSiteProtectionPreferencesWriting & MaliciousSiteProtectionPreferencesProvider final class MaliciousSiteProtectionPreferencesManager: MaliciousSiteProtectionPreferencesManaging { - @Published var isMaliciousSiteProtectionOn: Bool + @Published var isMaliciousSiteProtectionOn: Bool { + didSet { + store.isEnabled = isMaliciousSiteProtectionOn + } + } var isMaliciousSiteProtectionOnPublisher: AnyPublisher { $isMaliciousSiteProtectionOn.eraseToAnyPublisher() } - // TODO: Inject Store - init() { - isMaliciousSiteProtectionOn = true + private let store: MaliciousSiteProtectionPreferencesStorage + + init(store: MaliciousSiteProtectionPreferencesStorage = MaliciousSiteProtectionPreferencesUserDefaultsStore()) { + self.store = store + isMaliciousSiteProtectionOn = store.isEnabled } } diff --git a/DuckDuckGoTests/MaliciousSiteProtection/MaliciousSiteProtectionPreferencesManagerTests.swift b/DuckDuckGoTests/MaliciousSiteProtection/MaliciousSiteProtectionPreferencesManagerTests.swift new file mode 100644 index 0000000000..093c248f75 --- /dev/null +++ b/DuckDuckGoTests/MaliciousSiteProtection/MaliciousSiteProtectionPreferencesManagerTests.swift @@ -0,0 +1,74 @@ +// +// MaliciousSiteProtectionPreferencesManagerTests.swift +// DuckDuckGo +// +// 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 Testing +import Combine +@testable import DuckDuckGo + +final class MaliciousSiteProtectionPreferencesManagerTests { + private var sut: MaliciousSiteProtectionPreferencesManager! + private var store: MockMaliciousSiteProtectionPreferencesStore! + private var cancellables: Set! + + init() { + cancellables = [] + store = MockMaliciousSiteProtectionPreferencesStore() + sut = MaliciousSiteProtectionPreferencesManager(store: store) + } + + @Test( + "Update Malicious Site Protection Storage", + arguments: [ + true, + false + ] + ) + func whenIsEnabledIsSet_ThenStoreIsUpdated(value: Bool) { + // GIVEN + store.isEnabled = !value + + // WHEN + sut.isEnabled = value + + // THEN + #expect(store.isEnabled == value) + } + + @Test( + "Publish Malicious Site Protection User Preferences", + arguments: [ + true, + false + ] + ) + func whenIsEnabledIsSet_ThenValueIsPublished(value: Bool) { + // GIVEN + var capturedIsEnabled: Bool? + sut.isEnabledPublisher.sink { isEnabled in + capturedIsEnabled = isEnabled + } + .store(in: &cancellables) + + // WHEN + sut.isEnabled = value + + // THEN + #expect(capturedIsEnabled == value) + } +} From 24736f83916d1c7f820c4eb32eaf7d3ea534be52 Mon Sep 17 00:00:00 2001 From: Alessandro Boron Date: Mon, 16 Dec 2024 16:05:40 +0100 Subject: [PATCH 04/13] Add Settings VM and tests --- DuckDuckGo-iOS.xcodeproj/project.pbxproj | 21 ++- ...ciousSiteProtectionSettingsViewModel.swift | 60 +++++++ .../SettingsViewModel+ThreatDetection.swift | 80 --------- DuckDuckGo/SettingsViewModel.swift | 1 - ...SiteProtectionSettingsViewModelTests.swift | 165 ++++++++++++++++++ .../Mocks/MaliciousSiteProtectionMocks.swift | 5 + .../MockMaliciousSiteProtectionManager.swift | 0 7 files changed, 246 insertions(+), 86 deletions(-) create mode 100644 DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift delete mode 100644 DuckDuckGo/SettingsViewModel+ThreatDetection.swift create mode 100644 DuckDuckGoTests/MaliciousSiteProtection/MaliciousSiteProtectionSettingsViewModelTests.swift rename DuckDuckGoTests/{SpecialErrorPage/TestDoubles => MaliciousSiteProtection/Mocks}/MockMaliciousSiteProtectionManager.swift (100%) diff --git a/DuckDuckGo-iOS.xcodeproj/project.pbxproj b/DuckDuckGo-iOS.xcodeproj/project.pbxproj index a601bf3a0a..fd33ea54e9 100644 --- a/DuckDuckGo-iOS.xcodeproj/project.pbxproj +++ b/DuckDuckGo-iOS.xcodeproj/project.pbxproj @@ -791,6 +791,7 @@ 9F0949A02D372AE60079291B /* MaliciousSiteProtectionDatasetsFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F09499F2D372ADB0079291B /* MaliciousSiteProtectionDatasetsFetcher.swift */; }; 9F0949A52D3898FF0079291B /* MaliciousSiteProtectionPreferencesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0949A42D3898F80079291B /* MaliciousSiteProtectionPreferencesManager.swift */; }; 9F0949A72D389F1E0079291B /* MaliciousSiteProtectionAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0949A62D389F180079291B /* MaliciousSiteProtectionAPI.swift */; }; + 9F06EB8C2D10578000905426 /* MaliciousSiteProtectionSettingsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F06EB8B2D10578000905426 /* MaliciousSiteProtectionSettingsViewModelTests.swift */; }; 9F06EB922D10740500905426 /* MaliciousSiteProtectionPreferencesManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F06EB912D10740500905426 /* MaliciousSiteProtectionPreferencesManagerTests.swift */; }; 9F16230B2CA0F0190093C4FC /* DebouncerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F16230A2CA0F0190093C4FC /* DebouncerTests.swift */; }; 9F1798572CD2443F0073018B /* AddToDockPromoViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F1798562CD2443F0073018B /* AddToDockPromoViewModelTests.swift */; }; @@ -861,7 +862,7 @@ 9F9A92342C86B42B001D036D /* AppIconPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F9A92332C86B42B001D036D /* AppIconPicker.swift */; }; 9F9EE4CE2C377D4900D4118E /* OnboardingFirePixelMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F9EE4CC2C377D3F00D4118E /* OnboardingFirePixelMock.swift */; }; 9F9EE4D42C37BB1300D4118E /* OnboardingView+Landing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F9EE4D32C37BB1300D4118E /* OnboardingView+Landing.swift */; }; - 9F9F325A2CEFA75100211B49 /* SettingsViewModel+ThreatDetection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F9F32592CEFA74600211B49 /* SettingsViewModel+ThreatDetection.swift */; }; + 9F9F325A2CEFA75100211B49 /* MaliciousSiteProtectionSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F9F32592CEFA74600211B49 /* MaliciousSiteProtectionSettingsViewModel.swift */; }; 9FA5E44B2BF1AF3400BDEF02 /* SubscriptionContainerViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FA5E44A2BF1AF3400BDEF02 /* SubscriptionContainerViewFactory.swift */; }; 9FB027122C2526DD009EA190 /* OnboardingView+IntroDialogContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB027112C2526DD009EA190 /* OnboardingView+IntroDialogContent.swift */; }; 9FB027142C252E0C009EA190 /* OnboardingView+BrowsersComparisonContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB027132C252E0C009EA190 /* OnboardingView+BrowsersComparisonContent.swift */; }; @@ -2714,6 +2715,7 @@ 9F09499F2D372ADB0079291B /* MaliciousSiteProtectionDatasetsFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionDatasetsFetcher.swift; sourceTree = ""; }; 9F0949A42D3898F80079291B /* MaliciousSiteProtectionPreferencesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionPreferencesManager.swift; sourceTree = ""; }; 9F0949A62D389F180079291B /* MaliciousSiteProtectionAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionAPI.swift; sourceTree = ""; }; + 9F06EB8B2D10578000905426 /* MaliciousSiteProtectionSettingsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionSettingsViewModelTests.swift; sourceTree = ""; }; 9F06EB912D10740500905426 /* MaliciousSiteProtectionPreferencesManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionPreferencesManagerTests.swift; sourceTree = ""; }; 9F16230A2CA0F0190093C4FC /* DebouncerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebouncerTests.swift; sourceTree = ""; }; 9F1798562CD2443F0073018B /* AddToDockPromoViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddToDockPromoViewModelTests.swift; sourceTree = ""; }; @@ -2781,7 +2783,7 @@ 9F9A92332C86B42B001D036D /* AppIconPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIconPicker.swift; sourceTree = ""; }; 9F9EE4CC2C377D3F00D4118E /* OnboardingFirePixelMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingFirePixelMock.swift; sourceTree = ""; }; 9F9EE4D32C37BB1300D4118E /* OnboardingView+Landing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OnboardingView+Landing.swift"; sourceTree = ""; }; - 9F9F32592CEFA74600211B49 /* SettingsViewModel+ThreatDetection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SettingsViewModel+ThreatDetection.swift"; sourceTree = ""; }; + 9F9F32592CEFA74600211B49 /* MaliciousSiteProtectionSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionSettingsViewModel.swift; sourceTree = ""; }; 9FA5E44A2BF1AF3400BDEF02 /* SubscriptionContainerViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionContainerViewFactory.swift; sourceTree = ""; }; 9FB027112C2526DD009EA190 /* OnboardingView+IntroDialogContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OnboardingView+IntroDialogContent.swift"; sourceTree = ""; }; 9FB027132C252E0C009EA190 /* OnboardingView+BrowsersComparisonContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OnboardingView+BrowsersComparisonContent.swift"; sourceTree = ""; }; @@ -5254,6 +5256,14 @@ path = UserPreferences; sourceTree = ""; }; + 9F06EB882D0D737500905426 /* Settings */ = { + isa = PBXGroup; + children = ( + 9F9F32592CEFA74600211B49 /* MaliciousSiteProtectionSettingsViewModel.swift */, + ); + path = Settings; + sourceTree = ""; + }; 9F23B7FF2C2BABE000950875 /* OnboardingIntro */ = { isa = PBXGroup; children = ( @@ -5310,6 +5320,7 @@ children = ( 9F0949A32D3898DD0079291B /* UserPreferences */, 9F0949A22D3898C20079291B /* Services */, + 9F06EB882D0D737500905426 /* Settings */, 9F254AAA2CF47DD50063B308 /* MaliciousSiteProtectionManager.swift */, ); path = MaliciousSiteProtection; @@ -5336,7 +5347,6 @@ 9F254AD72CF605310063B308 /* MockSSLErrorPageNavigationHandler.swift */, 9F254ADA2CF6120E0063B308 /* MockSpecialErrorPageNavigationDelegate.swift */, 9F254ADD2CF636CF0063B308 /* DummyWKNavigation.swift */, - 9FBC76692CFE3802008B21E7 /* MockMaliciousSiteProtectionManager.swift */, ); path = TestDoubles; sourceTree = ""; @@ -5385,6 +5395,7 @@ 9F5BEA792D4382430045E484 /* MaliciousSiteProtectionDatasetsFetcherTests.swift */, 9F5BEA7A2D4382430045E484 /* MaliciousSiteProtectionManagerTests.swift */, 9F06EB912D10740500905426 /* MaliciousSiteProtectionPreferencesManagerTests.swift */, + 9F06EB8B2D10578000905426 /* MaliciousSiteProtectionSettingsViewModelTests.swift */, ); path = MaliciousSiteProtection; sourceTree = ""; @@ -6140,7 +6151,6 @@ children = ( D6E83C5F2B22B3C9006C8AFB /* SettingsState.swift */, D6E83C2D2B1EA06E006C8AFB /* SettingsViewModel.swift */, - 9F9F32592CEFA74600211B49 /* SettingsViewModel+ThreatDetection.swift */, D6E83C552B21ECC1006C8AFB /* SettingsLegacyViewProvider.swift */, 85449EFC23FDA71F00512AAF /* KeyboardSettings.swift */, 4B53648926718D0E001AA041 /* EmailWaitlist.swift */, @@ -8450,7 +8460,7 @@ EEC02C142B0519DE0045CE11 /* NetworkProtectionVPNLocationViewModel.swift in Sources */, D63FF8962C1B67E9006DE24D /* YoutubeOverlayUserScript.swift in Sources */, 9F38A28C2D09BDE500EB100E /* SpecialErrorPageThreatProvider.swift in Sources */, - 9F9F325A2CEFA75100211B49 /* SettingsViewModel+ThreatDetection.swift in Sources */, + 9F9F325A2CEFA75100211B49 /* MaliciousSiteProtectionSettingsViewModel.swift in Sources */, F13B4BC01F180D8A00814661 /* TabsModel.swift in Sources */, 8598D2E02CEB98B500C45685 /* Favicons.swift in Sources */, 8598D2E12CEB98B500C45685 /* NotFoundCachingDownloader.swift in Sources */, @@ -8834,6 +8844,7 @@ BDFF03262BA3DA4900F324C9 /* NetworkProtectionFeatureVisibilityTests.swift in Sources */, 9F8E0F332CCA642D001EA7C5 /* VideoPlayerViewModelTests.swift in Sources */, 6F1422842D314DD100B6D3DE /* TabInteractionStateDiskSourceTests.swift in Sources */, + 9F06EB8C2D10578000905426 /* MaliciousSiteProtectionSettingsViewModelTests.swift in Sources */, D62EC3BA2C246A7000FC9D04 /* YoutublePlayerNavigationHandlerTests.swift in Sources */, 1EAABE712C99FC75003F5137 /* SubscriptionFeatureAvailabilityMock.swift in Sources */, 8341D807212D5E8D000514C2 /* HashExtensionTest.swift in Sources */, diff --git a/DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift b/DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift new file mode 100644 index 0000000000..30a2bb64db --- /dev/null +++ b/DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift @@ -0,0 +1,60 @@ +// +// MaliciousSiteProtectionSettingsViewModel.swift +// DuckDuckGo +// +// 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 Foundation +import Combine +import Core +import SwiftUI + +final class MaliciousSiteProtectionSettingsViewModel: ObservableObject { + @Published var shouldShowMaliciousSiteProtectionSection = false + @Published var isMaliciousSiteProtectionEnabled: Bool = false + + var maliciousSiteProtectionBinding: Binding { + Binding( + get: { + self.manager.isEnabled + }, + set: { + self.manager.isEnabled = $0 + self.isMaliciousSiteProtectionEnabled = $0 + } + ) + } + + private let manager: MaliciousSiteProtectionPreferencesManaging + private let featureFlagger: MaliciousSiteProtectionFeatureFlagger + private let urlOpener: URLOpener + + init( + manager: MaliciousSiteProtectionPreferencesManaging = MaliciousSiteProtectionPreferencesManager(), + featureFlagger: MaliciousSiteProtectionFeatureFlagger = MaliciousSiteProtectionFeatureFlags(), + urlOpener: URLOpener = UIApplication.shared + ) { + self.manager = manager + self.featureFlagger = featureFlagger + self.urlOpener = urlOpener + shouldShowMaliciousSiteProtectionSection = true //featureFlagger.isMaliciousSiteProtectionEnabled + isMaliciousSiteProtectionEnabled = manager.isEnabled + } + + func learnMoreAction() { + urlOpener.open(URL.maliciousSiteProtectionLearnMore) + } +} diff --git a/DuckDuckGo/SettingsViewModel+ThreatDetection.swift b/DuckDuckGo/SettingsViewModel+ThreatDetection.swift deleted file mode 100644 index 9f512ce903..0000000000 --- a/DuckDuckGo/SettingsViewModel+ThreatDetection.swift +++ /dev/null @@ -1,80 +0,0 @@ -// -// SettingsViewModel+ThreatDetection.swift -// DuckDuckGo -// -// 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 Foundation -import Combine -import Core -import SwiftUI - -final class ThreatDetectionSettingsViewModel: ObservableObject { - @Published var shouldShowMaliciousSiteProtectionSection: Bool = false - - var threatDetectionBinding: Binding { - Binding( - get: { - self.manager.isEnabled - }, - set: { - self.manager.isEnabled = $0 - } - ) - } - - private let manager: ThreatDetetctionPreferencesManaging - private let featureChecker: ThreatDetectionSettingsChecking - - init( - manager: ThreatDetetctionPreferencesManaging = ThreatDetectionPreferencesManager(), - featureChecker: ThreatDetectionSettingsChecking = ThreatDetectionFeatureCheck() - ) { - self.manager = manager - self.featureChecker = featureChecker - shouldShowMaliciousSiteProtectionSection = featureChecker.isThreatDetectionSettingsEnabled - } -} - -protocol ThreatDetectionPreferencesStorage: AnyObject { - var isEnabled: Bool { get set } -} - -final class ThreatDetectionPreferencesUserDefaultsStore: ThreatDetectionPreferencesStorage { - @UserDefaultsWrapper(key: .threatDetectionEnabled, defaultValue: false) - var isEnabled: Bool -} - -protocol ThreatDetetctionPreferencesManaging: AnyObject { - var isEnabled: Bool { get set } -} - -final class ThreatDetectionPreferencesManager: ThreatDetetctionPreferencesManaging { - - @Published var isEnabled: Bool { - didSet { - store.isEnabled = isEnabled - print("~~~IS ENABLED: ", isEnabled) - } - } - - private let store: ThreatDetectionPreferencesStorage - - init(store: ThreatDetectionPreferencesStorage = ThreatDetectionPreferencesUserDefaultsStore()) { - self.store = store - self.isEnabled = store.isEnabled - } -} diff --git a/DuckDuckGo/SettingsViewModel.swift b/DuckDuckGo/SettingsViewModel.swift index cc65fd5773..19d0c77a01 100644 --- a/DuckDuckGo/SettingsViewModel.swift +++ b/DuckDuckGo/SettingsViewModel.swift @@ -47,7 +47,6 @@ final class SettingsViewModel: ObservableObject { let privacyProDataReporter: PrivacyProDataReporting? let textZoomCoordinator: TextZoomCoordinating let aiChatSettings: AIChatSettingsProvider - @Published var threatDetectionSettingsViewModel: ThreatDetectionSettingsViewModel = ThreatDetectionSettingsViewModel() // Subscription Dependencies let subscriptionManager: SubscriptionManager diff --git a/DuckDuckGoTests/MaliciousSiteProtection/MaliciousSiteProtectionSettingsViewModelTests.swift b/DuckDuckGoTests/MaliciousSiteProtection/MaliciousSiteProtectionSettingsViewModelTests.swift new file mode 100644 index 0000000000..60a987b3a8 --- /dev/null +++ b/DuckDuckGoTests/MaliciousSiteProtection/MaliciousSiteProtectionSettingsViewModelTests.swift @@ -0,0 +1,165 @@ +// +// MaliciousSiteProtectionSettingsViewModelTests.swift +// DuckDuckGo +// +// 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 Testing +@testable import DuckDuckGo + +@Suite("Malicious Site Protection - Settings View Model Unit Tests") +final class MaliciousSiteProtectionSettingsViewModelTests { + private var sut: MaliciousSiteProtectionSettingsViewModel! + private var preferencesManager: MockMaliciousSiteProtectionPreferencesManager! + private var featureFlagger: MockMaliciousSiteProtectionFeatureFlags! + private var urlOpener: MockURLOpener! + + init() { + preferencesManager = MockMaliciousSiteProtectionPreferencesManager() + featureFlagger = MockMaliciousSiteProtectionFeatureFlags() + urlOpener = MockURLOpener() + setupSUT() + } + + @Test("Malicious Site Protection Settings Section should be shown") + func whenInit_AndIsMaliciousSiteProtectionSetToTrue_ThenShouldShowMaliciousSiteProtectionSectionReturnsTrue() { + // GIVEN + featureFlagger.isMaliciousSiteProtectionEnabled = true + setupSUT() + + // WHEN + let result = sut.shouldShowMaliciousSiteProtectionSection + + // THEN + #expect(result) + } + + @Test("Malicious Site Protection Settings Section should not be shown") + func whenInit_AndIsMaliciousSiteProtectionSetToFalse_ThenShouldShowMaliciousSiteProtectionSectionReturnsFalse() { + // GIVEN + featureFlagger.isMaliciousSiteProtectionEnabled = false + setupSUT() + + // WHEN + let result = sut.shouldShowMaliciousSiteProtectionSection + + // THEN + #expect(!result) + } + + @Test("Malicious Site Protection preference is enabled") + func whenInit_AndIsEnabledPreferenceSetToTrue_ThenIsMaliciousSiteProtectionEnabledReturnsTrue() { + // GIVEN + preferencesManager.isEnabled = true + setupSUT() + + // WHEN + let result = sut.isMaliciousSiteProtectionEnabled + + // THEN + #expect(result) + } + + @Test("Malicious Site Protection preference is disabled") + func whenInit_AndIsEnabledPreferenceSetToFalse_ThenIsMaliciousSiteProtectionEnabledReturnsFalse() { + // GIVEN + preferencesManager.isEnabled = false + setupSUT() + + // WHEN + let result = sut.isMaliciousSiteProtectionEnabled + + // THEN + #expect(!result) + } + + @Test("Malicious Site Protection Settings binding value is true") + func whenMaliciousSiteProtectionBindingIsCalled_AndValueIsTrue_ThenReturnTrue() { + // GIVEN + preferencesManager.isEnabled = true + + // WHEN + let result = sut.maliciousSiteProtectionBinding + + // THEN + #expect(result.wrappedValue) + } + + @Test("Malicious Site Protection Settings binding value is false") + func whenMaliciousSiteProtectionBindingIsCalled_AndValueIsFalse_ThenReturnFalse() { + // GIVEN + preferencesManager.isEnabled = false + + // WHEN + let result = sut.maliciousSiteProtectionBinding + + // THEN + #expect(!result.wrappedValue) + } + + @Test("Malicious Site Protection Settings binding value is set to true") + func whenMaliciousSiteProtectionBindingIsSetToTrue_ThenIsMaliciousSiteProtectionEnabledIsSetToTrue() { + // GIVEN + preferencesManager.isEnabled = false + #expect(!preferencesManager.isEnabled) + + // WHEN + sut.maliciousSiteProtectionBinding.wrappedValue = true + + // THEN + #expect(preferencesManager.isEnabled) + } + + @Test("Malicious Site Protection Settings binding value is set to false") + func whenMaliciousSiteProtectionBindingIsSetToFalse_ThenIsMaliciousSiteProtectionEnabledIsSetToFalse() { + // GIVEN + preferencesManager.isEnabled = true + #expect(preferencesManager.isEnabled) + + // WHEN + sut.maliciousSiteProtectionBinding.wrappedValue = false + + // THEN + #expect(!preferencesManager.isEnabled) + } + + @Test("Open Malicious Site Protection Learn More") + func whenLearnMoreAction_ThenShouldNavigateToLearnMorePage() { + // GIVEN + #expect(!urlOpener.didCallOpenURL) + #expect(urlOpener.capturedURL == nil) + setupSUT() + + // WHEN + sut.learnMoreAction() + + // THEN + #expect(urlOpener.didCallOpenURL) + #expect(urlOpener.capturedURL == .maliciousSiteProtectionLearnMore) + } +} + +extension MaliciousSiteProtectionSettingsViewModelTests { + + func setupSUT() { + sut = MaliciousSiteProtectionSettingsViewModel( + manager: preferencesManager, + featureFlagger: featureFlagger, + urlOpener: urlOpener + ) + } + +} diff --git a/DuckDuckGoTests/MaliciousSiteProtection/Mocks/MaliciousSiteProtectionMocks.swift b/DuckDuckGoTests/MaliciousSiteProtection/Mocks/MaliciousSiteProtectionMocks.swift index 939ddddd79..06f18d9502 100644 --- a/DuckDuckGoTests/MaliciousSiteProtection/Mocks/MaliciousSiteProtectionMocks.swift +++ b/DuckDuckGoTests/MaliciousSiteProtection/Mocks/MaliciousSiteProtectionMocks.swift @@ -186,3 +186,8 @@ final class MockMaliciousSiteDetector: MaliciousSiteProtection.MaliciousSiteDete } } + +final class MockMaliciousSiteProtectionPreferencesStore: MaliciousSiteProtectionPreferencesStorage { + var isEnabled: Bool = true + +} diff --git a/DuckDuckGoTests/SpecialErrorPage/TestDoubles/MockMaliciousSiteProtectionManager.swift b/DuckDuckGoTests/MaliciousSiteProtection/Mocks/MockMaliciousSiteProtectionManager.swift similarity index 100% rename from DuckDuckGoTests/SpecialErrorPage/TestDoubles/MockMaliciousSiteProtectionManager.swift rename to DuckDuckGoTests/MaliciousSiteProtection/Mocks/MockMaliciousSiteProtectionManager.swift From 40601f110fe5ee286f721847800db2508c926ffc Mon Sep 17 00:00:00 2001 From: Alessandro Boron Date: Mon, 16 Dec 2024 16:06:03 +0100 Subject: [PATCH 05/13] Add View for settings --- DuckDuckGo-iOS.xcodeproj/project.pbxproj | 4 ++ DuckDuckGo/SettingsGeneralView.swift | 12 +--- .../SettingsMaliciousSiteProtectionView.swift | 56 +++++++++++++++++++ DuckDuckGo/UserText.swift | 7 +++ DuckDuckGo/en.lproj/Localizable.strings | 12 ++++ 5 files changed, 80 insertions(+), 11 deletions(-) create mode 100644 DuckDuckGo/SettingsMaliciousSiteProtectionView.swift diff --git a/DuckDuckGo-iOS.xcodeproj/project.pbxproj b/DuckDuckGo-iOS.xcodeproj/project.pbxproj index fd33ea54e9..28408b2b43 100644 --- a/DuckDuckGo-iOS.xcodeproj/project.pbxproj +++ b/DuckDuckGo-iOS.xcodeproj/project.pbxproj @@ -791,6 +791,7 @@ 9F0949A02D372AE60079291B /* MaliciousSiteProtectionDatasetsFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F09499F2D372ADB0079291B /* MaliciousSiteProtectionDatasetsFetcher.swift */; }; 9F0949A52D3898FF0079291B /* MaliciousSiteProtectionPreferencesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0949A42D3898F80079291B /* MaliciousSiteProtectionPreferencesManager.swift */; }; 9F0949A72D389F1E0079291B /* MaliciousSiteProtectionAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0949A62D389F180079291B /* MaliciousSiteProtectionAPI.swift */; }; + 9F06EB8A2D10560200905426 /* SettingsMaliciousSiteProtectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F06EB892D10560200905426 /* SettingsMaliciousSiteProtectionView.swift */; }; 9F06EB8C2D10578000905426 /* MaliciousSiteProtectionSettingsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F06EB8B2D10578000905426 /* MaliciousSiteProtectionSettingsViewModelTests.swift */; }; 9F06EB922D10740500905426 /* MaliciousSiteProtectionPreferencesManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F06EB912D10740500905426 /* MaliciousSiteProtectionPreferencesManagerTests.swift */; }; 9F16230B2CA0F0190093C4FC /* DebouncerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F16230A2CA0F0190093C4FC /* DebouncerTests.swift */; }; @@ -2715,6 +2716,7 @@ 9F09499F2D372ADB0079291B /* MaliciousSiteProtectionDatasetsFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionDatasetsFetcher.swift; sourceTree = ""; }; 9F0949A42D3898F80079291B /* MaliciousSiteProtectionPreferencesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionPreferencesManager.swift; sourceTree = ""; }; 9F0949A62D389F180079291B /* MaliciousSiteProtectionAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionAPI.swift; sourceTree = ""; }; + 9F06EB892D10560200905426 /* SettingsMaliciousSiteProtectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsMaliciousSiteProtectionView.swift; sourceTree = ""; }; 9F06EB8B2D10578000905426 /* MaliciousSiteProtectionSettingsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionSettingsViewModelTests.swift; sourceTree = ""; }; 9F06EB912D10740500905426 /* MaliciousSiteProtectionPreferencesManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionPreferencesManagerTests.swift; sourceTree = ""; }; 9F16230A2CA0F0190093C4FC /* DebouncerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebouncerTests.swift; sourceTree = ""; }; @@ -3570,6 +3572,7 @@ 1DEAADED2BA45DFE00E25A97 /* SettingsDataClearingView.swift */, D65625A02C232F5E006EF297 /* SettingsDuckPlayerView.swift */, 317CA3422CFF82DB00F88848 /* SettingsAIChatView.swift */, + 9F06EB892D10560200905426 /* SettingsMaliciousSiteProtectionView.swift */, ); name = MainSettings; sourceTree = ""; @@ -8639,6 +8642,7 @@ 9FCFCD852C75C91A006EB7A0 /* ProgressBarView.swift in Sources */, 6F3537A42C4AC140009F8717 /* NewTabPageDaxLogoView.swift in Sources */, 314C92B827C3DD660042EC96 /* QuickLookPreviewView.swift in Sources */, + 9F06EB8A2D10560200905426 /* SettingsMaliciousSiteProtectionView.swift in Sources */, 6F5345AF2C53F2DE00424A43 /* NewTabPageSettingsPersistentStorage.swift in Sources */, F1AE54E81F0425FC00D9A700 /* AuthenticationViewController.swift in Sources */, CBAD0F0A2CFF418F006267B8 /* AppShortcuts.swift in Sources */, diff --git a/DuckDuckGo/SettingsGeneralView.swift b/DuckDuckGo/SettingsGeneralView.swift index fd62d911a0..2e63dd124c 100644 --- a/DuckDuckGo/SettingsGeneralView.swift +++ b/DuckDuckGo/SettingsGeneralView.swift @@ -86,17 +86,7 @@ struct SettingsGeneralView: View { accessory: .toggle(isOn: viewModel.universalLinksBinding)) } - if viewModel.threatDetectionSettingsViewModel.shouldShowMaliciousSiteProtectionSection { - Section( - header: Text(verbatim: "Malicious Site Protection"), - footer: Text(verbatim: "Disabling this feature can put your personal information at risk. ") - .foregroundColor(.red) - ) { - SettingsCellView(label: "Warn me when a webpage may be malicious or fraudulent", - accessory: .toggle(isOn: viewModel.threatDetectionSettingsViewModel.threatDetectionBinding)) - } - } - + SettingsMaliciousProtectionView() } .applySettingsListModifiers(title: UserText.general, diff --git a/DuckDuckGo/SettingsMaliciousSiteProtectionView.swift b/DuckDuckGo/SettingsMaliciousSiteProtectionView.swift new file mode 100644 index 0000000000..2f7d0060f2 --- /dev/null +++ b/DuckDuckGo/SettingsMaliciousSiteProtectionView.swift @@ -0,0 +1,56 @@ +// +// SettingsMaliciousSiteProtectionView.swift +// DuckDuckGo +// +// 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 SwiftUI +import DuckUI + +struct SettingsMaliciousProtectionView: View { + @StateObject private var maliciousSiteProtectionSettingsModel = MaliciousSiteProtectionSettingsViewModel() + + var body: some View { + if maliciousSiteProtectionSettingsModel.shouldShowMaliciousSiteProtectionSection { + Section( + header: Text(UserText.MaliciousSiteProtectionSettings.header), + footer: + VStack(alignment: .leading, spacing: 10) { + Button(action: maliciousSiteProtectionSettingsModel.learnMoreAction) { + Text(UserText.MaliciousSiteProtectionSettings.footerLearnMore) + .foregroundColor(.blueBase) + } + + Text(UserText.MaliciousSiteProtectionSettings.footerDisabledMessage) + .opacity(maliciousSiteProtectionSettingsModel.maliciousSiteProtectionBinding.wrappedValue ? 0 : 1) + .foregroundColor(.red) + .font(.footnote) + } + ) { + SettingsCellView( + label: UserText.MaliciousSiteProtectionSettings.toggleMessage, + accessory: .toggle(isOn: maliciousSiteProtectionSettingsModel.maliciousSiteProtectionBinding) + ) + } + } else { + EmptyView() + } + } +} + +#Preview { + SettingsMaliciousProtectionView() +} diff --git a/DuckDuckGo/UserText.swift b/DuckDuckGo/UserText.swift index a110fd74a4..f4bf636aa3 100644 --- a/DuckDuckGo/UserText.swift +++ b/DuckDuckGo/UserText.swift @@ -1399,6 +1399,13 @@ Duck.ai is an optional feature that lets you chat anonymously with popular 3rd-p public static let aiChatSettingsEnableAddressBarToggle = NSLocalizedString("duckai.settings.enable.address-bar-toggle", value: "Show Duck.ai While Searching", comment: "Toggle text to enable/disable AI Chat in the address bar") + public enum MaliciousSiteProtectionSettings { + public static let header = NSLocalizedString("malicious-site-protection.settings.header", value: "Malicious Site Protection", comment: "Header text for Malicious Site Protection settings") + public static let toggleMessage = NSLocalizedString("malicious-site-protection.settings.toggle.message", value: "Warn me on sites flagged for phishing or malware", comment: "Text explaining what happens when Malicious Site Protection is enabled") + public static let footerLearnMore = NSLocalizedString("malicious-site-protection.settings.footer.button.learn-more", value: "Learn More", comment: "Button that redirect the user to a web page explaining what Malicious Site Protection is") + public static let footerDisabledMessage = NSLocalizedString("malicious-site-protection.settings.footer.message", value: "Disabling this feature can put your personal information at risk.", comment: "Footer text for Malicious Site Protection settings warning the user about the risks of disabling the feature") + } + // MARK: - New Tab Page // MARK: Shortcuts diff --git a/DuckDuckGo/en.lproj/Localizable.strings b/DuckDuckGo/en.lproj/Localizable.strings index 02c502438d..8e31de6cbe 100644 --- a/DuckDuckGo/en.lproj/Localizable.strings +++ b/DuckDuckGo/en.lproj/Localizable.strings @@ -1626,6 +1626,18 @@ https://duckduckgo.com/mac"; /* Title for the Mac Waitlist feature */ "mac-waitlist.title" = "DuckDuckGo App for Mac"; +/* Button that redirect the user to a web page explaining what Malicious Site Protection is */ +"malicious-site-protection.settings.footer.button.learn-more" = "Learn More"; + +/* Footer text for Malicious Site Protection settings warning the user about the risks of disabling the feature */ +"malicious-site-protection.settings.footer.message" = "Disabling this feature can put your personal information at risk."; + +/* Header text for Malicious Site Protection settings */ +"malicious-site-protection.settings.header" = "Malicious Site Protection"; + +/* Text explaining what happens when Malicious Site Protection is enabled */ +"malicious-site-protection.settings.toggle.message" = "Warn me on sites flagged for phishing or malware"; + /* No comment provided by engineer. */ "menu.button.hint" = "Browsing Menu"; From 4ce3f87eb8931b1f92ef09e93cfbc5e64e7f9200 Mon Sep 17 00:00:00 2001 From: Alessandro Boron Date: Thu, 19 Dec 2024 12:11:23 +0100 Subject: [PATCH 06/13] Fix malicious site protection user preference being out of sync --- DuckDuckGo-iOS.xcodeproj/project.pbxproj | 14 ++++++++++--- ...ciousSiteProtectionSettingsViewModel.swift | 12 +++++------ ...iousSiteProtectionPreferencesManager.swift | 2 +- ...iteProtectionPreferencesManagerTests.swift | 6 +++--- ...SiteProtectionSettingsViewModelTests.swift | 20 +++++++++---------- .../Mocks/MaliciousSiteProtectionMocks.swift | 1 - 6 files changed, 31 insertions(+), 24 deletions(-) diff --git a/DuckDuckGo-iOS.xcodeproj/project.pbxproj b/DuckDuckGo-iOS.xcodeproj/project.pbxproj index 28408b2b43..27777bd013 100644 --- a/DuckDuckGo-iOS.xcodeproj/project.pbxproj +++ b/DuckDuckGo-iOS.xcodeproj/project.pbxproj @@ -820,6 +820,7 @@ 9F38A28C2D09BDE500EB100E /* SpecialErrorPageThreatProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F38A28B2D09BDE500EB100E /* SpecialErrorPageThreatProvider.swift */; }; 9F465E1E2D3F5C2E00490109 /* Logger+MaliciousSiteProtection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F465E1D2D3F5C2700490109 /* Logger+MaliciousSiteProtection.swift */; }; 9F465E2A2D41B61900490109 /* MaliciousSiteProtectionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F465E292D41B61300490109 /* MaliciousSiteProtectionService.swift */; }; + 9F465E232D407A4D00490109 /* MockMaliciousSiteProtectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F465E222D407A4D00490109 /* MockMaliciousSiteProtectionManager.swift */; }; 9F46BEF82CD8D7490092E0EF /* OnboardingView+AddToDockContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F46BEF72CD8D7490092E0EF /* OnboardingView+AddToDockContent.swift */; }; 9F4CC5152C47AD08006A96EB /* ContextualOnboardingPresenterMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F4CC5142C47AD08006A96EB /* ContextualOnboardingPresenterMock.swift */; }; 9F4CC5172C48B8D4006A96EB /* TabViewControllerDaxDialogTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F4CC5162C48B8D4006A96EB /* TabViewControllerDaxDialogTests.swift */; }; @@ -871,7 +872,6 @@ 9FB0271B2C2927D0009EA190 /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB0271A2C2927D0009EA190 /* OnboardingView.swift */; }; 9FB0271D2C293619009EA190 /* OnboardingIntroViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB0271C2C293619009EA190 /* OnboardingIntroViewModel.swift */; }; 9FBC76672CFE33B5008B21E7 /* MaliciousSiteProtectionNavigationHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FBC76662CFE33B5008B21E7 /* MaliciousSiteProtectionNavigationHandlerTests.swift */; }; - 9FBC766A2CFE3802008B21E7 /* MockMaliciousSiteProtectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FBC76692CFE3802008B21E7 /* MockMaliciousSiteProtectionManager.swift */; }; 9FCFCD802C6AF56D006EB7A0 /* LaunchOptionsHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FCFCD7F2C6AF56D006EB7A0 /* LaunchOptionsHandlerTests.swift */; }; 9FCFCD812C6B020D006EB7A0 /* LaunchOptionsHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FCFCD7D2C6AF52A006EB7A0 /* LaunchOptionsHandler.swift */; }; 9FCFCD852C75C91A006EB7A0 /* ProgressBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FCFCD842C75C91A006EB7A0 /* ProgressBarView.swift */; }; @@ -2744,6 +2744,7 @@ 9F38A28B2D09BDE500EB100E /* SpecialErrorPageThreatProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpecialErrorPageThreatProvider.swift; sourceTree = ""; }; 9F465E1D2D3F5C2700490109 /* Logger+MaliciousSiteProtection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Logger+MaliciousSiteProtection.swift"; sourceTree = ""; }; 9F465E292D41B61300490109 /* MaliciousSiteProtectionService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionService.swift; sourceTree = ""; }; + 9F465E222D407A4D00490109 /* MockMaliciousSiteProtectionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMaliciousSiteProtectionManager.swift; sourceTree = ""; }; 9F46BEF72CD8D7490092E0EF /* OnboardingView+AddToDockContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OnboardingView+AddToDockContent.swift"; sourceTree = ""; }; 9F4CC5142C47AD08006A96EB /* ContextualOnboardingPresenterMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextualOnboardingPresenterMock.swift; sourceTree = ""; }; 9F4CC5162C48B8D4006A96EB /* TabViewControllerDaxDialogTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabViewControllerDaxDialogTests.swift; sourceTree = ""; }; @@ -2793,7 +2794,6 @@ 9FB0271A2C2927D0009EA190 /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = ""; }; 9FB0271C2C293619009EA190 /* OnboardingIntroViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingIntroViewModel.swift; sourceTree = ""; }; 9FBC76662CFE33B5008B21E7 /* MaliciousSiteProtectionNavigationHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionNavigationHandlerTests.swift; sourceTree = ""; }; - 9FBC76692CFE3802008B21E7 /* MockMaliciousSiteProtectionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMaliciousSiteProtectionManager.swift; sourceTree = ""; }; 9FCFCD7D2C6AF52A006EB7A0 /* LaunchOptionsHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = LaunchOptionsHandler.swift; path = ../DuckDuckGo/LaunchOptionsHandler.swift; sourceTree = ""; }; 9FCFCD7F2C6AF56D006EB7A0 /* LaunchOptionsHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchOptionsHandlerTests.swift; sourceTree = ""; }; 9FCFCD842C75C91A006EB7A0 /* ProgressBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressBarView.swift; sourceTree = ""; }; @@ -4623,6 +4623,7 @@ 83ED3B8D1FA8E63700B47556 /* README.md */, 83ED3B8C1FA8E61D00B47556 /* ManualTestsScript.md */, 85A313962028E78A00327D00 /* release_notes.txt */, + 9F465E212D4076E500490109 /* Recovered References */, ); sourceTree = ""; }; @@ -5374,6 +5375,13 @@ path = Model; sourceTree = ""; }; + 9F465E212D4076E500490109 /* Recovered References */ = { + isa = PBXGroup; + children = ( + ); + name = "Recovered References"; + sourceTree = ""; + }; 9F4CC5252C4E22F9006A96EB /* ContextualOnboarding */ = { isa = PBXGroup; children = ( @@ -8839,7 +8847,6 @@ 9F254ADF2CF636CF0063B308 /* DummyWKNavigation.swift in Sources */, 6F7BACD42CEE084B00F561D8 /* OmniBarEqualityCheckTests.swift in Sources */, 6F7FB8E72C66197E00867DA7 /* NewTabPageSectionsSettingsModelTests.swift in Sources */, - 9FBC766A2CFE3802008B21E7 /* MockMaliciousSiteProtectionManager.swift in Sources */, 851CD674244D7E6000331B98 /* UserDefaultsExtension.swift in Sources */, 569437362BE5160600C0881B /* SyncSettingsViewControllerErrorTests.swift in Sources */, EEC02C162B065BE00045CE11 /* NetworkProtectionVPNLocationViewModelTests.swift in Sources */, @@ -8947,6 +8954,7 @@ 987130C6294AAB9F00AB05E0 /* BookmarkListViewModelTests.swift in Sources */, 6F03CB022C32ED47004179A8 /* NewTabPageMessagesModelTests.swift in Sources */, F1134ED21F40EF3A00B73467 /* JsonTestDataLoader.swift in Sources */, + 9F465E232D407A4D00490109 /* MockMaliciousSiteProtectionManager.swift in Sources */, 850250B520D80419002199C7 /* AtbAndVariantCleanupTests.swift in Sources */, C14882E727F20DAB00D59F0C /* HtmlTestDataLoader.swift in Sources */, F17D72391E8B35C6003E8B0E /* AppURLsTests.swift in Sources */, diff --git a/DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift b/DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift index 30a2bb64db..1b41ef8adb 100644 --- a/DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift +++ b/DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift @@ -29,10 +29,10 @@ final class MaliciousSiteProtectionSettingsViewModel: ObservableObject { var maliciousSiteProtectionBinding: Binding { Binding( get: { - self.manager.isEnabled + self.manager.isMaliciousSiteProtectionOn }, set: { - self.manager.isEnabled = $0 + self.manager.isMaliciousSiteProtectionOn = $0 self.isMaliciousSiteProtectionEnabled = $0 } ) @@ -43,15 +43,15 @@ final class MaliciousSiteProtectionSettingsViewModel: ObservableObject { private let urlOpener: URLOpener init( - manager: MaliciousSiteProtectionPreferencesManaging = MaliciousSiteProtectionPreferencesManager(), - featureFlagger: MaliciousSiteProtectionFeatureFlagger = MaliciousSiteProtectionFeatureFlags(), + manager: MaliciousSiteProtectionPreferencesManaging = AppDependencyProvider.shared.maliciousSiteProtectionPreferencesManager, + featureFlagger: MaliciousSiteProtectionFeatureFlagger = MaliciousSiteProtectionFeatureFlags(featureFlagger: AppDependencyProvider.shared.featureFlagger), urlOpener: URLOpener = UIApplication.shared ) { self.manager = manager self.featureFlagger = featureFlagger self.urlOpener = urlOpener - shouldShowMaliciousSiteProtectionSection = true //featureFlagger.isMaliciousSiteProtectionEnabled - isMaliciousSiteProtectionEnabled = manager.isEnabled + shouldShowMaliciousSiteProtectionSection = featureFlagger.isMaliciousSiteProtectionEnabled + isMaliciousSiteProtectionEnabled = manager.isMaliciousSiteProtectionOn } func learnMoreAction() { diff --git a/DuckDuckGo/MaliciousSiteProtection/UserPreferences/MaliciousSiteProtectionPreferencesManager.swift b/DuckDuckGo/MaliciousSiteProtection/UserPreferences/MaliciousSiteProtectionPreferencesManager.swift index f7ea980bdc..f93c295444 100644 --- a/DuckDuckGo/MaliciousSiteProtection/UserPreferences/MaliciousSiteProtectionPreferencesManager.swift +++ b/DuckDuckGo/MaliciousSiteProtection/UserPreferences/MaliciousSiteProtectionPreferencesManager.swift @@ -40,7 +40,7 @@ protocol MaliciousSiteProtectionPreferencesReading { typealias MaliciousSiteProtectionPreferencesProvider = MaliciousSiteProtectionPreferencesReading & MaliciousSiteProtectionPreferencesPublishing -protocol MaliciousSiteProtectionPreferencesWriting { +protocol MaliciousSiteProtectionPreferencesWriting: AnyObject { var isMaliciousSiteProtectionOn: Bool { get set } } diff --git a/DuckDuckGoTests/MaliciousSiteProtection/MaliciousSiteProtectionPreferencesManagerTests.swift b/DuckDuckGoTests/MaliciousSiteProtection/MaliciousSiteProtectionPreferencesManagerTests.swift index 093c248f75..06b9007370 100644 --- a/DuckDuckGoTests/MaliciousSiteProtection/MaliciousSiteProtectionPreferencesManagerTests.swift +++ b/DuckDuckGoTests/MaliciousSiteProtection/MaliciousSiteProtectionPreferencesManagerTests.swift @@ -44,7 +44,7 @@ final class MaliciousSiteProtectionPreferencesManagerTests { store.isEnabled = !value // WHEN - sut.isEnabled = value + sut.isMaliciousSiteProtectionOn = value // THEN #expect(store.isEnabled == value) @@ -60,13 +60,13 @@ final class MaliciousSiteProtectionPreferencesManagerTests { func whenIsEnabledIsSet_ThenValueIsPublished(value: Bool) { // GIVEN var capturedIsEnabled: Bool? - sut.isEnabledPublisher.sink { isEnabled in + sut.isMaliciousSiteProtectionOnPublisher.sink { isEnabled in capturedIsEnabled = isEnabled } .store(in: &cancellables) // WHEN - sut.isEnabled = value + sut.isMaliciousSiteProtectionOn = value // THEN #expect(capturedIsEnabled == value) diff --git a/DuckDuckGoTests/MaliciousSiteProtection/MaliciousSiteProtectionSettingsViewModelTests.swift b/DuckDuckGoTests/MaliciousSiteProtection/MaliciousSiteProtectionSettingsViewModelTests.swift index 60a987b3a8..e4f8a9821c 100644 --- a/DuckDuckGoTests/MaliciousSiteProtection/MaliciousSiteProtectionSettingsViewModelTests.swift +++ b/DuckDuckGoTests/MaliciousSiteProtection/MaliciousSiteProtectionSettingsViewModelTests.swift @@ -63,7 +63,7 @@ final class MaliciousSiteProtectionSettingsViewModelTests { @Test("Malicious Site Protection preference is enabled") func whenInit_AndIsEnabledPreferenceSetToTrue_ThenIsMaliciousSiteProtectionEnabledReturnsTrue() { // GIVEN - preferencesManager.isEnabled = true + preferencesManager.isMaliciousSiteProtectionOn = true setupSUT() // WHEN @@ -76,7 +76,7 @@ final class MaliciousSiteProtectionSettingsViewModelTests { @Test("Malicious Site Protection preference is disabled") func whenInit_AndIsEnabledPreferenceSetToFalse_ThenIsMaliciousSiteProtectionEnabledReturnsFalse() { // GIVEN - preferencesManager.isEnabled = false + preferencesManager.isMaliciousSiteProtectionOn = false setupSUT() // WHEN @@ -89,7 +89,7 @@ final class MaliciousSiteProtectionSettingsViewModelTests { @Test("Malicious Site Protection Settings binding value is true") func whenMaliciousSiteProtectionBindingIsCalled_AndValueIsTrue_ThenReturnTrue() { // GIVEN - preferencesManager.isEnabled = true + preferencesManager.isMaliciousSiteProtectionOn = true // WHEN let result = sut.maliciousSiteProtectionBinding @@ -101,7 +101,7 @@ final class MaliciousSiteProtectionSettingsViewModelTests { @Test("Malicious Site Protection Settings binding value is false") func whenMaliciousSiteProtectionBindingIsCalled_AndValueIsFalse_ThenReturnFalse() { // GIVEN - preferencesManager.isEnabled = false + preferencesManager.isMaliciousSiteProtectionOn = false // WHEN let result = sut.maliciousSiteProtectionBinding @@ -113,27 +113,27 @@ final class MaliciousSiteProtectionSettingsViewModelTests { @Test("Malicious Site Protection Settings binding value is set to true") func whenMaliciousSiteProtectionBindingIsSetToTrue_ThenIsMaliciousSiteProtectionEnabledIsSetToTrue() { // GIVEN - preferencesManager.isEnabled = false - #expect(!preferencesManager.isEnabled) + preferencesManager.isMaliciousSiteProtectionOn = false + #expect(!preferencesManager.isMaliciousSiteProtectionOn) // WHEN sut.maliciousSiteProtectionBinding.wrappedValue = true // THEN - #expect(preferencesManager.isEnabled) + #expect(preferencesManager.isMaliciousSiteProtectionOn) } @Test("Malicious Site Protection Settings binding value is set to false") func whenMaliciousSiteProtectionBindingIsSetToFalse_ThenIsMaliciousSiteProtectionEnabledIsSetToFalse() { // GIVEN - preferencesManager.isEnabled = true - #expect(preferencesManager.isEnabled) + preferencesManager.isMaliciousSiteProtectionOn = true + #expect(preferencesManager.isMaliciousSiteProtectionOn) // WHEN sut.maliciousSiteProtectionBinding.wrappedValue = false // THEN - #expect(!preferencesManager.isEnabled) + #expect(!preferencesManager.isMaliciousSiteProtectionOn) } @Test("Open Malicious Site Protection Learn More") diff --git a/DuckDuckGoTests/MaliciousSiteProtection/Mocks/MaliciousSiteProtectionMocks.swift b/DuckDuckGoTests/MaliciousSiteProtection/Mocks/MaliciousSiteProtectionMocks.swift index 06f18d9502..6462099651 100644 --- a/DuckDuckGoTests/MaliciousSiteProtection/Mocks/MaliciousSiteProtectionMocks.swift +++ b/DuckDuckGoTests/MaliciousSiteProtection/Mocks/MaliciousSiteProtectionMocks.swift @@ -189,5 +189,4 @@ final class MockMaliciousSiteDetector: MaliciousSiteProtection.MaliciousSiteDete final class MockMaliciousSiteProtectionPreferencesStore: MaliciousSiteProtectionPreferencesStorage { var isEnabled: Bool = true - } From 77103733d27e4c8dd60fce76ba2bd3b4a8e24348 Mon Sep 17 00:00:00 2001 From: Alessandro Boron Date: Thu, 9 Jan 2025 15:45:30 +1100 Subject: [PATCH 07/13] Clean up code --- .../MaliciousSiteProtectionSettingsViewModel.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift b/DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift index 1b41ef8adb..895c6e413e 100644 --- a/DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift +++ b/DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift @@ -32,8 +32,7 @@ final class MaliciousSiteProtectionSettingsViewModel: ObservableObject { self.manager.isMaliciousSiteProtectionOn }, set: { - self.manager.isMaliciousSiteProtectionOn = $0 - self.isMaliciousSiteProtectionEnabled = $0 + self.updateMaliciousSiteProtection(enabled: $0) } ) } @@ -57,4 +56,10 @@ final class MaliciousSiteProtectionSettingsViewModel: ObservableObject { func learnMoreAction() { urlOpener.open(URL.maliciousSiteProtectionLearnMore) } + + private func updateMaliciousSiteProtection(enabled isEnabled: Bool) { + manager.isMaliciousSiteProtectionOn = isEnabled + isMaliciousSiteProtectionEnabled = isEnabled + } + } From bf19c1f7e264a0d16e270bfaef7f7b64c2cad40d Mon Sep 17 00:00:00 2001 From: Alessandro Boron Date: Wed, 22 Jan 2025 16:33:59 +1100 Subject: [PATCH 08/13] Clean up --- ...ciousSiteProtectionSettingsViewModel.swift | 22 +++-------- .../SettingsMaliciousSiteProtectionView.swift | 4 +- ...SiteProtectionSettingsViewModelTests.swift | 39 ++++--------------- 3 files changed, 15 insertions(+), 50 deletions(-) diff --git a/DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift b/DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift index 895c6e413e..fdbe3eb3bd 100644 --- a/DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift +++ b/DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift @@ -24,17 +24,10 @@ import SwiftUI final class MaliciousSiteProtectionSettingsViewModel: ObservableObject { @Published var shouldShowMaliciousSiteProtectionSection = false - @Published var isMaliciousSiteProtectionEnabled: Bool = false - - var maliciousSiteProtectionBinding: Binding { - Binding( - get: { - self.manager.isMaliciousSiteProtectionOn - }, - set: { - self.updateMaliciousSiteProtection(enabled: $0) - } - ) + @Published var isMaliciousSiteProtectionOn: Bool = false { + didSet { + manager.isMaliciousSiteProtectionOn = isMaliciousSiteProtectionOn + } } private let manager: MaliciousSiteProtectionPreferencesManaging @@ -50,16 +43,11 @@ final class MaliciousSiteProtectionSettingsViewModel: ObservableObject { self.featureFlagger = featureFlagger self.urlOpener = urlOpener shouldShowMaliciousSiteProtectionSection = featureFlagger.isMaliciousSiteProtectionEnabled - isMaliciousSiteProtectionEnabled = manager.isMaliciousSiteProtectionOn + isMaliciousSiteProtectionOn = manager.isMaliciousSiteProtectionOn } func learnMoreAction() { urlOpener.open(URL.maliciousSiteProtectionLearnMore) } - private func updateMaliciousSiteProtection(enabled isEnabled: Bool) { - manager.isMaliciousSiteProtectionOn = isEnabled - isMaliciousSiteProtectionEnabled = isEnabled - } - } diff --git a/DuckDuckGo/SettingsMaliciousSiteProtectionView.swift b/DuckDuckGo/SettingsMaliciousSiteProtectionView.swift index 2f7d0060f2..a8cdd8ba72 100644 --- a/DuckDuckGo/SettingsMaliciousSiteProtectionView.swift +++ b/DuckDuckGo/SettingsMaliciousSiteProtectionView.swift @@ -35,14 +35,14 @@ struct SettingsMaliciousProtectionView: View { } Text(UserText.MaliciousSiteProtectionSettings.footerDisabledMessage) - .opacity(maliciousSiteProtectionSettingsModel.maliciousSiteProtectionBinding.wrappedValue ? 0 : 1) + .opacity(maliciousSiteProtectionSettingsModel.isMaliciousSiteProtectionOn ? 0 : 1) .foregroundColor(.red) .font(.footnote) } ) { SettingsCellView( label: UserText.MaliciousSiteProtectionSettings.toggleMessage, - accessory: .toggle(isOn: maliciousSiteProtectionSettingsModel.maliciousSiteProtectionBinding) + accessory: .toggle(isOn: $maliciousSiteProtectionSettingsModel.isMaliciousSiteProtectionOn) ) } } else { diff --git a/DuckDuckGoTests/MaliciousSiteProtection/MaliciousSiteProtectionSettingsViewModelTests.swift b/DuckDuckGoTests/MaliciousSiteProtection/MaliciousSiteProtectionSettingsViewModelTests.swift index e4f8a9821c..e3e0e9b191 100644 --- a/DuckDuckGoTests/MaliciousSiteProtection/MaliciousSiteProtectionSettingsViewModelTests.swift +++ b/DuckDuckGoTests/MaliciousSiteProtection/MaliciousSiteProtectionSettingsViewModelTests.swift @@ -60,77 +60,54 @@ final class MaliciousSiteProtectionSettingsViewModelTests { #expect(!result) } - @Test("Malicious Site Protection preference is enabled") + @Test("Malicious Site Protection preference is On") func whenInit_AndIsEnabledPreferenceSetToTrue_ThenIsMaliciousSiteProtectionEnabledReturnsTrue() { // GIVEN preferencesManager.isMaliciousSiteProtectionOn = true setupSUT() // WHEN - let result = sut.isMaliciousSiteProtectionEnabled + let result = sut.isMaliciousSiteProtectionOn // THEN #expect(result) } - @Test("Malicious Site Protection preference is disabled") + @Test("Malicious Site Protection preference is Off") func whenInit_AndIsEnabledPreferenceSetToFalse_ThenIsMaliciousSiteProtectionEnabledReturnsFalse() { // GIVEN preferencesManager.isMaliciousSiteProtectionOn = false setupSUT() // WHEN - let result = sut.isMaliciousSiteProtectionEnabled + let result = sut.isMaliciousSiteProtectionOn // THEN #expect(!result) } - @Test("Malicious Site Protection Settings binding value is true") - func whenMaliciousSiteProtectionBindingIsCalled_AndValueIsTrue_ThenReturnTrue() { - // GIVEN - preferencesManager.isMaliciousSiteProtectionOn = true - - // WHEN - let result = sut.maliciousSiteProtectionBinding - - // THEN - #expect(result.wrappedValue) - } - - @Test("Malicious Site Protection Settings binding value is false") - func whenMaliciousSiteProtectionBindingIsCalled_AndValueIsFalse_ThenReturnFalse() { - // GIVEN - preferencesManager.isMaliciousSiteProtectionOn = false - - // WHEN - let result = sut.maliciousSiteProtectionBinding - - // THEN - #expect(!result.wrappedValue) - } - @Test("Malicious Site Protection Settings binding value is set to true") + @Test("Turning On Malicious Site Protection Settings Save User Preference") func whenMaliciousSiteProtectionBindingIsSetToTrue_ThenIsMaliciousSiteProtectionEnabledIsSetToTrue() { // GIVEN preferencesManager.isMaliciousSiteProtectionOn = false #expect(!preferencesManager.isMaliciousSiteProtectionOn) // WHEN - sut.maliciousSiteProtectionBinding.wrappedValue = true + sut.isMaliciousSiteProtectionOn = true // THEN #expect(preferencesManager.isMaliciousSiteProtectionOn) } - @Test("Malicious Site Protection Settings binding value is set to false") + @Test("Turning Off Malicious Site Protection Settings Save User Preference") func whenMaliciousSiteProtectionBindingIsSetToFalse_ThenIsMaliciousSiteProtectionEnabledIsSetToFalse() { // GIVEN preferencesManager.isMaliciousSiteProtectionOn = true #expect(preferencesManager.isMaliciousSiteProtectionOn) // WHEN - sut.maliciousSiteProtectionBinding.wrappedValue = false + sut.isMaliciousSiteProtectionOn = false // THEN #expect(!preferencesManager.isMaliciousSiteProtectionOn) From 027e82608b5461ef9938b63bd1554a254332d12c Mon Sep 17 00:00:00 2001 From: Alessandro Boron Date: Wed, 22 Jan 2025 18:00:23 +1100 Subject: [PATCH 09/13] Updated settings to use shared user preferences from main view controller --- DuckDuckGo/MainViewController+Segues.swift | 3 ++- DuckDuckGo/MainViewController.swift | 2 ++ ...ciousSiteProtectionSettingsViewModel.swift | 2 +- DuckDuckGo/SettingsGeneralView.swift | 2 +- .../SettingsMaliciousSiteProtectionView.swift | 20 +++++++++++-------- DuckDuckGo/SettingsViewModel.swift | 7 +++++-- 6 files changed, 23 insertions(+), 13 deletions(-) diff --git a/DuckDuckGo/MainViewController+Segues.swift b/DuckDuckGo/MainViewController+Segues.swift index 844ec1dfce..33511a3518 100644 --- a/DuckDuckGo/MainViewController+Segues.swift +++ b/DuckDuckGo/MainViewController+Segues.swift @@ -277,7 +277,8 @@ extension MainViewController { syncPausedStateManager: syncPausedStateManager, privacyProDataReporter: privacyProDataReporter, textZoomCoordinator: textZoomCoordinator, - aiChatSettings: aiChatSettings) + aiChatSettings: aiChatSettings, + maliciousSiteProtectionPreferencesManager: maliciousSiteProtectionPreferencesManager) Pixel.fire(pixel: .settingsPresented) if let navigationController = self.presentedViewController as? UINavigationController, diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 02bda05127..276c0ef5e1 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -187,6 +187,7 @@ class MainViewController: UIViewController { var viewCoordinator: MainViewCoordinator! var appDidFinishLaunchingStartTime: CFAbsoluteTime? + let maliciousSiteProtectionPreferencesManager: MaliciousSiteProtectionPreferencesManaging private lazy var aiChatViewControllerManager: AIChatViewControllerManager = { let manager = AIChatViewControllerManager() @@ -276,6 +277,7 @@ class MainViewController: UIViewController { self.textZoomCoordinator = textZoomCoordinator self.websiteDataManager = websiteDataManager self.appDidFinishLaunchingStartTime = appDidFinishLaunchingStartTime + self.maliciousSiteProtectionPreferencesManager = maliciousSiteProtectionPreferencesManager super.init(nibName: nil, bundle: nil) diff --git a/DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift b/DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift index fdbe3eb3bd..626475384f 100644 --- a/DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift +++ b/DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift @@ -35,7 +35,7 @@ final class MaliciousSiteProtectionSettingsViewModel: ObservableObject { private let urlOpener: URLOpener init( - manager: MaliciousSiteProtectionPreferencesManaging = AppDependencyProvider.shared.maliciousSiteProtectionPreferencesManager, + manager: MaliciousSiteProtectionPreferencesManaging, featureFlagger: MaliciousSiteProtectionFeatureFlagger = MaliciousSiteProtectionFeatureFlags(featureFlagger: AppDependencyProvider.shared.featureFlagger), urlOpener: URLOpener = UIApplication.shared ) { diff --git a/DuckDuckGo/SettingsGeneralView.swift b/DuckDuckGo/SettingsGeneralView.swift index 2e63dd124c..ae6dffd89a 100644 --- a/DuckDuckGo/SettingsGeneralView.swift +++ b/DuckDuckGo/SettingsGeneralView.swift @@ -86,7 +86,7 @@ struct SettingsGeneralView: View { accessory: .toggle(isOn: viewModel.universalLinksBinding)) } - SettingsMaliciousProtectionView() + SettingsMaliciousProtectionView(model: MaliciousSiteProtectionSettingsViewModel(manager: viewModel.maliciousSiteProtectionPreferencesManager)) } .applySettingsListModifiers(title: UserText.general, diff --git a/DuckDuckGo/SettingsMaliciousSiteProtectionView.swift b/DuckDuckGo/SettingsMaliciousSiteProtectionView.swift index a8cdd8ba72..a8be1deccf 100644 --- a/DuckDuckGo/SettingsMaliciousSiteProtectionView.swift +++ b/DuckDuckGo/SettingsMaliciousSiteProtectionView.swift @@ -21,28 +21,32 @@ import SwiftUI import DuckUI struct SettingsMaliciousProtectionView: View { - @StateObject private var maliciousSiteProtectionSettingsModel = MaliciousSiteProtectionSettingsViewModel() + @ObservedObject private var model: MaliciousSiteProtectionSettingsViewModel + + init(model: MaliciousSiteProtectionSettingsViewModel) { + self.model = model + } var body: some View { - if maliciousSiteProtectionSettingsModel.shouldShowMaliciousSiteProtectionSection { + if model.shouldShowMaliciousSiteProtectionSection { Section( header: Text(UserText.MaliciousSiteProtectionSettings.header), footer: VStack(alignment: .leading, spacing: 10) { - Button(action: maliciousSiteProtectionSettingsModel.learnMoreAction) { + Button(action: model.learnMoreAction) { Text(UserText.MaliciousSiteProtectionSettings.footerLearnMore) .foregroundColor(.blueBase) } Text(UserText.MaliciousSiteProtectionSettings.footerDisabledMessage) - .opacity(maliciousSiteProtectionSettingsModel.isMaliciousSiteProtectionOn ? 0 : 1) + .opacity(model.isMaliciousSiteProtectionOn ? 0 : 1) .foregroundColor(.red) .font(.footnote) } ) { SettingsCellView( label: UserText.MaliciousSiteProtectionSettings.toggleMessage, - accessory: .toggle(isOn: $maliciousSiteProtectionSettingsModel.isMaliciousSiteProtectionOn) + accessory: .toggle(isOn: $model.isMaliciousSiteProtectionOn) ) } } else { @@ -51,6 +55,6 @@ struct SettingsMaliciousProtectionView: View { } } -#Preview { - SettingsMaliciousProtectionView() -} +//#Preview { +// SettingsMaliciousProtectionView(model: ) +//} diff --git a/DuckDuckGo/SettingsViewModel.swift b/DuckDuckGo/SettingsViewModel.swift index 19d0c77a01..d721f1f3ec 100644 --- a/DuckDuckGo/SettingsViewModel.swift +++ b/DuckDuckGo/SettingsViewModel.swift @@ -47,6 +47,7 @@ final class SettingsViewModel: ObservableObject { let privacyProDataReporter: PrivacyProDataReporting? let textZoomCoordinator: TextZoomCoordinating let aiChatSettings: AIChatSettingsProvider + let maliciousSiteProtectionPreferencesManager: MaliciousSiteProtectionPreferencesManaging // Subscription Dependencies let subscriptionManager: SubscriptionManager @@ -416,7 +417,9 @@ final class SettingsViewModel: ObservableObject { syncPausedStateManager: any SyncPausedStateManaging, privacyProDataReporter: PrivacyProDataReporting, textZoomCoordinator: TextZoomCoordinating, - aiChatSettings: AIChatSettingsProvider) { + aiChatSettings: AIChatSettingsProvider, + maliciousSiteProtectionPreferencesManager: MaliciousSiteProtectionPreferencesManaging + ) { self.state = SettingsState.defaults self.legacyViewProvider = legacyViewProvider @@ -429,7 +432,7 @@ final class SettingsViewModel: ObservableObject { self.privacyProDataReporter = privacyProDataReporter self.textZoomCoordinator = textZoomCoordinator self.aiChatSettings = aiChatSettings - + self.maliciousSiteProtectionPreferencesManager = maliciousSiteProtectionPreferencesManager setupNotificationObservers() updateRecentlyVisitedSitesVisibility() } From 9081cb152b32692bc210fadd4517285efd725316 Mon Sep 17 00:00:00 2001 From: Alessandro Boron Date: Wed, 22 Jan 2025 18:10:02 +1100 Subject: [PATCH 10/13] Fix Swiftlint issues --- DuckDuckGo/SettingsMaliciousSiteProtectionView.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DuckDuckGo/SettingsMaliciousSiteProtectionView.swift b/DuckDuckGo/SettingsMaliciousSiteProtectionView.swift index a8be1deccf..4975d53d13 100644 --- a/DuckDuckGo/SettingsMaliciousSiteProtectionView.swift +++ b/DuckDuckGo/SettingsMaliciousSiteProtectionView.swift @@ -55,6 +55,6 @@ struct SettingsMaliciousProtectionView: View { } } -//#Preview { -// SettingsMaliciousProtectionView(model: ) -//} +#Preview { + SettingsMaliciousProtectionView(model: MaliciousSiteProtectionSettingsViewModel(manager: MaliciousSiteProtectionPreferencesManager())) +} From f8382e0027272752bea0ca7daffb7644d85ad994 Mon Sep 17 00:00:00 2001 From: Alessandro Boron Date: Thu, 23 Jan 2025 12:07:56 +1100 Subject: [PATCH 11/13] Default Malicious Site Protection User Preferences to true --- .../MaliciousSiteProtectionPreferencesManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo/MaliciousSiteProtection/UserPreferences/MaliciousSiteProtectionPreferencesManager.swift b/DuckDuckGo/MaliciousSiteProtection/UserPreferences/MaliciousSiteProtectionPreferencesManager.swift index f93c295444..0ad46defb1 100644 --- a/DuckDuckGo/MaliciousSiteProtection/UserPreferences/MaliciousSiteProtectionPreferencesManager.swift +++ b/DuckDuckGo/MaliciousSiteProtection/UserPreferences/MaliciousSiteProtectionPreferencesManager.swift @@ -26,7 +26,7 @@ protocol MaliciousSiteProtectionPreferencesStorage: AnyObject { } final class MaliciousSiteProtectionPreferencesUserDefaultsStore: MaliciousSiteProtectionPreferencesStorage { - @UserDefaultsWrapper(key: .maliciousSiteProtectionEnabled, defaultValue: false) + @UserDefaultsWrapper(key: .maliciousSiteProtectionEnabled, defaultValue: true) var isEnabled: Bool } From 12021b2e4965eb2d453e7a74138668c6fb6c9c38 Mon Sep 17 00:00:00 2001 From: Alessandro Boron Date: Fri, 24 Jan 2025 19:57:06 +1100 Subject: [PATCH 12/13] Fixed project conflicts --- DuckDuckGo-iOS.xcodeproj/project.pbxproj | 47 ++++++++++-------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/DuckDuckGo-iOS.xcodeproj/project.pbxproj b/DuckDuckGo-iOS.xcodeproj/project.pbxproj index 27777bd013..217ac3c2d8 100644 --- a/DuckDuckGo-iOS.xcodeproj/project.pbxproj +++ b/DuckDuckGo-iOS.xcodeproj/project.pbxproj @@ -788,12 +788,12 @@ 98F3A1D8217B37010011A0D4 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98F3A1D7217B37010011A0D4 /* Theme.swift */; }; 98F6EA472863124100720957 /* ContentBlockerRulesLists.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98F6EA462863124100720957 /* ContentBlockerRulesLists.swift */; }; 98F78B8E22419093007CACF4 /* ThemableNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98F78B8D22419093007CACF4 /* ThemableNavigationController.swift */; }; - 9F0949A02D372AE60079291B /* MaliciousSiteProtectionDatasetsFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F09499F2D372ADB0079291B /* MaliciousSiteProtectionDatasetsFetcher.swift */; }; - 9F0949A52D3898FF0079291B /* MaliciousSiteProtectionPreferencesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0949A42D3898F80079291B /* MaliciousSiteProtectionPreferencesManager.swift */; }; - 9F0949A72D389F1E0079291B /* MaliciousSiteProtectionAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0949A62D389F180079291B /* MaliciousSiteProtectionAPI.swift */; }; 9F06EB8A2D10560200905426 /* SettingsMaliciousSiteProtectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F06EB892D10560200905426 /* SettingsMaliciousSiteProtectionView.swift */; }; 9F06EB8C2D10578000905426 /* MaliciousSiteProtectionSettingsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F06EB8B2D10578000905426 /* MaliciousSiteProtectionSettingsViewModelTests.swift */; }; 9F06EB922D10740500905426 /* MaliciousSiteProtectionPreferencesManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F06EB912D10740500905426 /* MaliciousSiteProtectionPreferencesManagerTests.swift */; }; + 9F0949A02D372AE60079291B /* MaliciousSiteProtectionDatasetsFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F09499F2D372ADB0079291B /* MaliciousSiteProtectionDatasetsFetcher.swift */; }; + 9F0949A52D3898FF0079291B /* MaliciousSiteProtectionPreferencesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0949A42D3898F80079291B /* MaliciousSiteProtectionPreferencesManager.swift */; }; + 9F0949A72D389F1E0079291B /* MaliciousSiteProtectionAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0949A62D389F180079291B /* MaliciousSiteProtectionAPI.swift */; }; 9F16230B2CA0F0190093C4FC /* DebouncerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F16230A2CA0F0190093C4FC /* DebouncerTests.swift */; }; 9F1798572CD2443F0073018B /* AddToDockPromoViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F1798562CD2443F0073018B /* AddToDockPromoViewModelTests.swift */; }; 9F23B8012C2BC94400950875 /* OnboardingBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F23B8002C2BC94400950875 /* OnboardingBackground.swift */; }; @@ -820,7 +820,6 @@ 9F38A28C2D09BDE500EB100E /* SpecialErrorPageThreatProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F38A28B2D09BDE500EB100E /* SpecialErrorPageThreatProvider.swift */; }; 9F465E1E2D3F5C2E00490109 /* Logger+MaliciousSiteProtection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F465E1D2D3F5C2700490109 /* Logger+MaliciousSiteProtection.swift */; }; 9F465E2A2D41B61900490109 /* MaliciousSiteProtectionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F465E292D41B61300490109 /* MaliciousSiteProtectionService.swift */; }; - 9F465E232D407A4D00490109 /* MockMaliciousSiteProtectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F465E222D407A4D00490109 /* MockMaliciousSiteProtectionManager.swift */; }; 9F46BEF82CD8D7490092E0EF /* OnboardingView+AddToDockContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F46BEF72CD8D7490092E0EF /* OnboardingView+AddToDockContent.swift */; }; 9F4CC5152C47AD08006A96EB /* ContextualOnboardingPresenterMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F4CC5142C47AD08006A96EB /* ContextualOnboardingPresenterMock.swift */; }; 9F4CC5172C48B8D4006A96EB /* TabViewControllerDaxDialogTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F4CC5162C48B8D4006A96EB /* TabViewControllerDaxDialogTests.swift */; }; @@ -833,6 +832,7 @@ 9F5BEA7D2D4382430045E484 /* TimeTraveller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F5BEA772D4382430045E484 /* TimeTraveller.swift */; }; 9F5BEA7E2D4382430045E484 /* MaliciousSiteProtectionDatasetsFetcherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F5BEA792D4382430045E484 /* MaliciousSiteProtectionDatasetsFetcherTests.swift */; }; 9F5BEA7F2D4382430045E484 /* MaliciousSiteProtectionManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F5BEA7A2D4382430045E484 /* MaliciousSiteProtectionManagerTests.swift */; }; + 9F5BEA832D438B880045E484 /* MockMaliciousSiteProtectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F5BEA822D438B880045E484 /* MockMaliciousSiteProtectionManager.swift */; }; 9F5E5AAC2C3D0FCD00165F54 /* ContextualDaxDialogsFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F5E5AAB2C3D0FCD00165F54 /* ContextualDaxDialogsFactory.swift */; }; 9F5E5AB02C3E4C6000165F54 /* ContextualOnboardingPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F5E5AAF2C3E4C6000165F54 /* ContextualOnboardingPresenter.swift */; }; 9F5E5AB22C3E606D00165F54 /* ContextualOnboardingPresenterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F5E5AB12C3E606D00165F54 /* ContextualOnboardingPresenterTests.swift */; }; @@ -2713,12 +2713,12 @@ 98F3A1D7217B37010011A0D4 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; }; 98F6EA462863124100720957 /* ContentBlockerRulesLists.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBlockerRulesLists.swift; sourceTree = ""; }; 98F78B8D22419093007CACF4 /* ThemableNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemableNavigationController.swift; sourceTree = ""; }; - 9F09499F2D372ADB0079291B /* MaliciousSiteProtectionDatasetsFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionDatasetsFetcher.swift; sourceTree = ""; }; - 9F0949A42D3898F80079291B /* MaliciousSiteProtectionPreferencesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionPreferencesManager.swift; sourceTree = ""; }; - 9F0949A62D389F180079291B /* MaliciousSiteProtectionAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionAPI.swift; sourceTree = ""; }; 9F06EB892D10560200905426 /* SettingsMaliciousSiteProtectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsMaliciousSiteProtectionView.swift; sourceTree = ""; }; 9F06EB8B2D10578000905426 /* MaliciousSiteProtectionSettingsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionSettingsViewModelTests.swift; sourceTree = ""; }; 9F06EB912D10740500905426 /* MaliciousSiteProtectionPreferencesManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionPreferencesManagerTests.swift; sourceTree = ""; }; + 9F09499F2D372ADB0079291B /* MaliciousSiteProtectionDatasetsFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionDatasetsFetcher.swift; sourceTree = ""; }; + 9F0949A42D3898F80079291B /* MaliciousSiteProtectionPreferencesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionPreferencesManager.swift; sourceTree = ""; }; + 9F0949A62D389F180079291B /* MaliciousSiteProtectionAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionAPI.swift; sourceTree = ""; }; 9F16230A2CA0F0190093C4FC /* DebouncerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebouncerTests.swift; sourceTree = ""; }; 9F1798562CD2443F0073018B /* AddToDockPromoViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddToDockPromoViewModelTests.swift; sourceTree = ""; }; 9F23B8002C2BC94400950875 /* OnboardingBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingBackground.swift; sourceTree = ""; }; @@ -2744,7 +2744,6 @@ 9F38A28B2D09BDE500EB100E /* SpecialErrorPageThreatProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpecialErrorPageThreatProvider.swift; sourceTree = ""; }; 9F465E1D2D3F5C2700490109 /* Logger+MaliciousSiteProtection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Logger+MaliciousSiteProtection.swift"; sourceTree = ""; }; 9F465E292D41B61300490109 /* MaliciousSiteProtectionService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionService.swift; sourceTree = ""; }; - 9F465E222D407A4D00490109 /* MockMaliciousSiteProtectionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMaliciousSiteProtectionManager.swift; sourceTree = ""; }; 9F46BEF72CD8D7490092E0EF /* OnboardingView+AddToDockContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OnboardingView+AddToDockContent.swift"; sourceTree = ""; }; 9F4CC5142C47AD08006A96EB /* ContextualOnboardingPresenterMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextualOnboardingPresenterMock.swift; sourceTree = ""; }; 9F4CC5162C48B8D4006A96EB /* TabViewControllerDaxDialogTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabViewControllerDaxDialogTests.swift; sourceTree = ""; }; @@ -2757,6 +2756,7 @@ 9F5BEA772D4382430045E484 /* TimeTraveller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeTraveller.swift; sourceTree = ""; }; 9F5BEA792D4382430045E484 /* MaliciousSiteProtectionDatasetsFetcherTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionDatasetsFetcherTests.swift; sourceTree = ""; }; 9F5BEA7A2D4382430045E484 /* MaliciousSiteProtectionManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaliciousSiteProtectionManagerTests.swift; sourceTree = ""; }; + 9F5BEA822D438B880045E484 /* MockMaliciousSiteProtectionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMaliciousSiteProtectionManager.swift; sourceTree = ""; }; 9F5E5AAB2C3D0FCD00165F54 /* ContextualDaxDialogsFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextualDaxDialogsFactory.swift; sourceTree = ""; }; 9F5E5AAF2C3E4C6000165F54 /* ContextualOnboardingPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextualOnboardingPresenter.swift; sourceTree = ""; }; 9F5E5AB12C3E606D00165F54 /* ContextualOnboardingPresenterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextualOnboardingPresenterTests.swift; sourceTree = ""; }; @@ -4623,7 +4623,6 @@ 83ED3B8D1FA8E63700B47556 /* README.md */, 83ED3B8C1FA8E61D00B47556 /* ManualTestsScript.md */, 85A313962028E78A00327D00 /* release_notes.txt */, - 9F465E212D4076E500490109 /* Recovered References */, ); sourceTree = ""; }; @@ -5242,6 +5241,14 @@ name = Themes; sourceTree = ""; }; + 9F06EB882D0D737500905426 /* Settings */ = { + isa = PBXGroup; + children = ( + 9F9F32592CEFA74600211B49 /* MaliciousSiteProtectionSettingsViewModel.swift */, + ); + path = Settings; + sourceTree = ""; + }; 9F0949A22D3898C20079291B /* Services */ = { isa = PBXGroup; children = ( @@ -5260,14 +5267,6 @@ path = UserPreferences; sourceTree = ""; }; - 9F06EB882D0D737500905426 /* Settings */ = { - isa = PBXGroup; - children = ( - 9F9F32592CEFA74600211B49 /* MaliciousSiteProtectionSettingsViewModel.swift */, - ); - path = Settings; - sourceTree = ""; - }; 9F23B7FF2C2BABE000950875 /* OnboardingIntro */ = { isa = PBXGroup; children = ( @@ -5375,13 +5374,6 @@ path = Model; sourceTree = ""; }; - 9F465E212D4076E500490109 /* Recovered References */ = { - isa = PBXGroup; - children = ( - ); - name = "Recovered References"; - sourceTree = ""; - }; 9F4CC5252C4E22F9006A96EB /* ContextualOnboarding */ = { isa = PBXGroup; children = ( @@ -5393,6 +5385,7 @@ 9F5BEA782D4382430045E484 /* Mocks */ = { isa = PBXGroup; children = ( + 9F5BEA822D438B880045E484 /* MockMaliciousSiteProtectionManager.swift */, 9F5BEA762D4382430045E484 /* MaliciousSiteProtectionMocks.swift */, 9F5BEA772D4382430045E484 /* TimeTraveller.swift */, ); @@ -5405,8 +5398,8 @@ 9F5BEA782D4382430045E484 /* Mocks */, 9F5BEA792D4382430045E484 /* MaliciousSiteProtectionDatasetsFetcherTests.swift */, 9F5BEA7A2D4382430045E484 /* MaliciousSiteProtectionManagerTests.swift */, - 9F06EB912D10740500905426 /* MaliciousSiteProtectionPreferencesManagerTests.swift */, - 9F06EB8B2D10578000905426 /* MaliciousSiteProtectionSettingsViewModelTests.swift */, + 9F06EB912D10740500905426 /* MaliciousSiteProtectionPreferencesManagerTests.swift */, + 9F06EB8B2D10578000905426 /* MaliciousSiteProtectionSettingsViewModelTests.swift */, ); path = MaliciousSiteProtection; sourceTree = ""; @@ -8904,6 +8897,7 @@ 56A061452BEE086E00F24B36 /* CapturingSyncPausedStateManager.swift in Sources */, 987130C9294AAB9F00AB05E0 /* BookmarkUtilsTests.swift in Sources */, 56A061442BEE086700F24B36 /* CapturingAdapterErrorHandler.swift in Sources */, + 9F5BEA832D438B880045E484 /* MockMaliciousSiteProtectionManager.swift in Sources */, C1BF0BA929B63E2200482B73 /* AutofillLoginPromptViewModelTests.swift in Sources */, EE3B226B29DE0F110082298A /* MockInternalUserStoring.swift in Sources */, 6F1422862D31509500B6D3DE /* MockDataExtensions.swift in Sources */, @@ -8954,7 +8948,6 @@ 987130C6294AAB9F00AB05E0 /* BookmarkListViewModelTests.swift in Sources */, 6F03CB022C32ED47004179A8 /* NewTabPageMessagesModelTests.swift in Sources */, F1134ED21F40EF3A00B73467 /* JsonTestDataLoader.swift in Sources */, - 9F465E232D407A4D00490109 /* MockMaliciousSiteProtectionManager.swift in Sources */, 850250B520D80419002199C7 /* AtbAndVariantCleanupTests.swift in Sources */, C14882E727F20DAB00D59F0C /* HtmlTestDataLoader.swift in Sources */, F17D72391E8B35C6003E8B0E /* AppURLsTests.swift in Sources */, From 70f7b655efdeba3970585d07e3385163392eb5e8 Mon Sep 17 00:00:00 2001 From: Alessandro Boron Date: Fri, 24 Jan 2025 19:57:30 +1100 Subject: [PATCH 13/13] Fix compile issue due to MaliciousSiteProtectionFeatureFlag imported from BSK --- .../Settings/MaliciousSiteProtectionSettingsViewModel.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift b/DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift index 626475384f..a231c7ca5d 100644 --- a/DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift +++ b/DuckDuckGo/MaliciousSiteProtection/Settings/MaliciousSiteProtectionSettingsViewModel.swift @@ -21,6 +21,7 @@ import Foundation import Combine import Core import SwiftUI +import MaliciousSiteProtection final class MaliciousSiteProtectionSettingsViewModel: ObservableObject { @Published var shouldShowMaliciousSiteProtectionSection = false