diff --git a/.xcode-version b/.xcode-version index c32b0ec5ab..f6eb05e3c6 100644 --- a/.xcode-version +++ b/.xcode-version @@ -1 +1 @@ -16.1 +16.2 diff --git a/Configuration/BuildNumber.xcconfig b/Configuration/BuildNumber.xcconfig index 610113ea86..78ac15dc98 100644 --- a/Configuration/BuildNumber.xcconfig +++ b/Configuration/BuildNumber.xcconfig @@ -1 +1 @@ -CURRENT_PROJECT_VERSION = 330 +CURRENT_PROJECT_VERSION = 332 diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 1183e8fcc6..46c507b66a 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -1319,6 +1319,9 @@ 37FC2A192CF903080048E226 /* MockPrivacyStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FC2A172CF903060048E226 /* MockPrivacyStats.swift */; }; 37FD78112A29EBD100B36DB1 /* SyncErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FD78102A29EBD100B36DB1 /* SyncErrorHandler.swift */; }; 37FD78122A29EBD100B36DB1 /* SyncErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FD78102A29EBD100B36DB1 /* SyncErrorHandler.swift */; }; + 46066CBC2D1330A100AB683B /* Persistence in Frameworks */ = {isa = PBXBuildFile; productRef = 46066CBB2D1330A100AB683B /* Persistence */; }; + 467D16672D0C98D5007C020A /* CrashReportSenderExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 467D16662D0C98D5007C020A /* CrashReportSenderExtensions.swift */; }; + 467D16682D0C98D5007C020A /* CrashReportSenderExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 467D16662D0C98D5007C020A /* CrashReportSenderExtensions.swift */; }; 4B0135CE2729F1AA00D54834 /* NSPasteboardExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B0135CD2729F1AA00D54834 /* NSPasteboardExtension.swift */; }; 4B02198925E05FAC00ED7DEA /* FireproofingURLExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B02197F25E05FAC00ED7DEA /* FireproofingURLExtensions.swift */; }; 4B02198A25E05FAC00ED7DEA /* FireproofDomains.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B02198125E05FAC00ED7DEA /* FireproofDomains.swift */; }; @@ -3796,6 +3799,7 @@ 37F8ABD22CE3EE5B00CB0294 /* FeatureFlagOverridesMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureFlagOverridesMenu.swift; sourceTree = ""; }; 37FC2A172CF903060048E226 /* MockPrivacyStats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPrivacyStats.swift; sourceTree = ""; }; 37FD78102A29EBD100B36DB1 /* SyncErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncErrorHandler.swift; sourceTree = ""; }; + 467D16662D0C98D5007C020A /* CrashReportSenderExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrashReportSenderExtensions.swift; sourceTree = ""; }; 4B0135CD2729F1AA00D54834 /* NSPasteboardExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSPasteboardExtension.swift; sourceTree = ""; }; 4B02197F25E05FAC00ED7DEA /* FireproofingURLExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FireproofingURLExtensions.swift; sourceTree = ""; }; 4B02198125E05FAC00ED7DEA /* FireproofDomains.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FireproofDomains.swift; sourceTree = ""; }; @@ -5345,6 +5349,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 46066CBC2D1330A100AB683B /* Persistence in Frameworks */, B6DA44172616C13800DD1EC2 /* OHHTTPStubs in Frameworks */, F116A7C32BD1924B00F3FCF7 /* PixelKitTestingUtilities in Frameworks */, 84BBC8012CFA0D3800BAE57A /* TestUtils in Frameworks */, @@ -8639,6 +8644,7 @@ AAC30A27268E045400D2D9CD /* CrashReportReader.swift */, AAC30A2D268F1EE300D2D9CD /* CrashReportPromptPresenter.swift */, AAC30A29268E239100D2D9CD /* CrashReport.swift */, + 467D16662D0C98D5007C020A /* CrashReportSenderExtensions.swift */, ); path = Model; sourceTree = ""; @@ -10497,6 +10503,7 @@ 9DC5FACA2C6B8E050011F068 /* AppKitExtensions */, 84BBC8002CFA0D3800BAE57A /* TestUtils */, 374EFDEE2D01C70300B30939 /* Utilities */, + 46066CBB2D1330A100AB683B /* Persistence */, ); productName = DuckDuckGoTests; productReference = AA585D90248FD31400E9A3E2 /* Unit Tests.xctest */; @@ -11571,6 +11578,7 @@ 37197EA82942443D00394917 /* BrowserTabViewController.swift in Sources */, 3706FB39293F65D500E42796 /* PrivacyDashboardPopover.swift in Sources */, 3706FB3B293F65D500E42796 /* RootView.swift in Sources */, + 467D16672D0C98D5007C020A /* CrashReportSenderExtensions.swift in Sources */, 3706FB3C293F65D500E42796 /* AddressBarTextField.swift in Sources */, 3706FB3D293F65D500E42796 /* FocusRingView.swift in Sources */, 3706FB3E293F65D500E42796 /* BookmarksBarViewModel.swift in Sources */, @@ -13085,6 +13093,7 @@ AABEE6A524AA0A7F0043105B /* SuggestionViewController.swift in Sources */, 1D6216B229069BBF00386B2C /* BWKeyStorage.swift in Sources */, AA7E919F287872EA00AB6B62 /* VisitViewModel.swift in Sources */, + 467D16682D0C98D5007C020A /* CrashReportSenderExtensions.swift in Sources */, 7BD7B0012C19D3830039D20A /* VPNIPCResources.swift in Sources */, C1935A1B2C88F9ED001AD72D /* SyncPromoViewModel.swift in Sources */, B6676BE12AA986A700525A21 /* AddressBarTextEditor.swift in Sources */, @@ -15296,8 +15305,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit"; requirement = { - branch = "alex/malware-protection-pixels"; - kind = branch; + kind = exactVersion; + version = 223.0.0; }; }; 9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */ = { @@ -15700,6 +15709,11 @@ package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = Navigation; }; + 46066CBB2D1330A100AB683B /* Persistence */ = { + isa = XCSwiftPackageProductDependency; + package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = Persistence; + }; 4B2D062B2A11C0E100DE1F49 /* Networking */ = { isa = XCSwiftPackageProductDependency; package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 9161995876..abbc299c08 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/BrowserServicesKit", "state" : { - "branch" : "alex/malware-protection-pixels", - "revision" : "6ebb657ff44df3ce17bcb93b3103946db470f52b" + "revision" : "e8f94cf597f4a447f86f39f461b736ac9ea280ce", + "version" : "223.0.0" } }, { diff --git a/DuckDuckGo/Application/AppDelegate.swift b/DuckDuckGo/Application/AppDelegate.swift index 1b0788355b..3aa256fb0f 100644 --- a/DuckDuckGo/Application/AppDelegate.swift +++ b/DuckDuckGo/Application/AppDelegate.swift @@ -71,7 +71,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate { let fileStore: FileStore #if APPSTORE - private let crashCollection = CrashCollection(platform: .macOSAppStore) + private let crashCollection = CrashCollection(crashReportSender: CrashReportSender(platform: .macOSAppStore, + pixelEvents: CrashReportSender.pixelEvents)) #else private let crashReporter = CrashReporter() #endif diff --git a/DuckDuckGo/CrashReports/Model/CrashReportSenderExtensions.swift b/DuckDuckGo/CrashReports/Model/CrashReportSenderExtensions.swift new file mode 100644 index 0000000000..85618d2d99 --- /dev/null +++ b/DuckDuckGo/CrashReports/Model/CrashReportSenderExtensions.swift @@ -0,0 +1,43 @@ +// +// CrashReportSenderExtensions.swift +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Crashes +import Common +import PixelKit + +extension CrashReportSender { + + static let pixelEvents: EventMapping = .init { event, _, _, _ in + switch event { + case CrashReportSenderError.crcidMissing: + PixelKit.fire(GeneralPixel.crashReportCRCIDMissing) + + case CrashReportSenderError.submissionFailed(let error): + if let error { + PixelKit.fire(DebugEvent(GeneralPixel.crashReportingSubmissionFailed), + frequency: .standard, + withHeaders: [:], + withAdditionalParameters: ["HTTPStatusCode": "\(error.statusCode)"], + withError: nil, + allowedQueryReservedCharacters: nil) + } else { + PixelKit.fire(GeneralPixel.crashReportingSubmissionFailed) + } + } + } +} diff --git a/DuckDuckGo/CrashReports/Model/CrashReporter.swift b/DuckDuckGo/CrashReports/Model/CrashReporter.swift index b3456a3adb..4883679907 100644 --- a/DuckDuckGo/CrashReports/Model/CrashReporter.swift +++ b/DuckDuckGo/CrashReports/Model/CrashReporter.swift @@ -24,7 +24,8 @@ import PixelKit final class CrashReporter { private let reader = CrashReportReader() - private lazy var sender = CrashReportSender(platform: .macOS) + private lazy var sender = CrashReportSender(platform: .macOS, pixelEvents: CrashReportSender.pixelEvents) + private lazy var crcidManager = CRCIDManager() private lazy var promptPresenter = CrashReportPromptPresenter() @UserDefaultsWrapper(key: .lastCrashReportCheckDate, defaultValue: nil) @@ -56,7 +57,9 @@ final class CrashReporter { return } Task { - await self.sender.send(contentData) + let crcid = self.crcidManager.crcid + let result = await self.sender.send(contentData, crcid: crcid) + self.crcidManager.handleCrashSenderResult(result: result.result, response: result.response) } } diff --git a/DuckDuckGo/DataImport/View/FileImportView.swift b/DuckDuckGo/DataImport/View/FileImportView.swift index 8e704b2293..6536a1cc0a 100644 --- a/DuckDuckGo/DataImport/View/FileImportView.swift +++ b/DuckDuckGo/DataImport/View/FileImportView.swift @@ -275,18 +275,34 @@ func fileImportInstructionsBuilder(source: DataImport.Source, dataType: DataImpo button(UserText.importBookmarksSelectHTMLFile) case (.safari, .passwords), (.safariTechnologyPreview, .passwords): - NSLocalizedString("import.csv.instructions.safari", value: """ - %d Open **Safari** - %d Select **File → Export → Passwords** - %d Save the passwords file someplace you can find it (e.g., Desktop) - %d %@ - """, comment: """ - Instructions to import Passwords as CSV from Safari. - %N$d - step number - %5$@ - “Select Passwords CSV File” button - **bold text**; _italic text_ - """) - button(UserText.importLoginsSelectCSVFile) + if #available(macOS 15.2, *) { + NSLocalizedString("import.csv.instructions.safari.macos15-2", value: """ + %d Open **Safari** + %d Open the **File menu → Export Browsing Data to File...** + %d Select **passwords** and save the file someplace you can find it (e.g., Desktop) + %d Double click the .zip file to unzip it + %d %@ + """, comment: """ + Instructions to import Passwords as CSV from Safari zip file on >= macOS 15.2. + %N$d - step number + %5$@ - “Select Passwords CSV File” button + **bold text**; _italic text_ + """) + button(UserText.importLoginsSelectCSVFile) + } else { + NSLocalizedString("import.csv.instructions.safari", value: """ + %d Open **Safari** + %d Select **File → Export → Passwords** + %d Save the passwords file someplace you can find it (e.g., Desktop) + %d %@ + """, comment: """ + Instructions to import Passwords as CSV from Safari. + %N$d - step number + %5$@ - “Select Passwords CSV File” button + **bold text**; _italic text_ + """) + button(UserText.importLoginsSelectCSVFile) + } case (.safari, .bookmarks), (.safariTechnologyPreview, .bookmarks): NSLocalizedString("import.html.instructions.safari", value: """ diff --git a/DuckDuckGo/Localizable.xcstrings b/DuckDuckGo/Localizable.xcstrings index b0968acf1e..b90ea9b9b0 100644 --- a/DuckDuckGo/Localizable.xcstrings +++ b/DuckDuckGo/Localizable.xcstrings @@ -30931,6 +30931,66 @@ } } }, + "import.csv.instructions.safari.macos15-2" : { + "comment" : "Instructions to import Passwords as CSV from Safari zip file on >= macOS 15.2.\n%N$d - step number\n%5$@ - “Select Passwords CSV File” button\n**bold text**; _italic text_", + "extractionState" : "extracted_with_value", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$d Öffne **Safari**\n%2$d Öffne das Menü **Datei → Browserdaten in Datei exportieren ...**\n%3$d Wähle **Passwörter** und speichere die Datei an einem Ort, an dem du sie wiederfindest (z. B. Desktop)\n%4$d Doppelklicke auf die .zip-Datei, um sie zu entpacken\n%5$d %6$@" + } + }, + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "%1$d Open **Safari**\n%2$d Open the **File menu → Export Browsing Data to File...**\n%3$d Select **passwords** and save the file someplace you can find it (e.g., Desktop)\n%4$d Double click the .zip file to unzip it\n%5$d %6$@" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$d Abre **Safari**\n%2$d Abre el menú **Archivo → Exportar datos de navegación a archivo...**\n%3$d Selecciona **contraseñas** y guarda el archivo en algún lugar donde puedas encontrarlo (por ejemplo, en el escritorio)\n%4$d Haz doble clic en el archivo .zip para descomprimirlo\n%5$d %6$@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$d Ouvrez **Safari**\n%2$d Ouvrez le **menu Fichier → Exporter les données de navigation vers le fichier…**\n%3$d Sélectionnez **mots de passe** et enregistrez le fichier à un endroit où le trouver facilement (par exemple, sur le bureau)\n%4$d Double-cliquez sur le fichier .zip pour le décompresser\n%5$d %6$@" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$d Apri **Safari**\n%2$d Apri il menu **File → Esporta i dati di navigazione in un file...**\n%3$d Seleziona **password** e salva il file in una posizione che ti consenta di trovarlo (ad esempio, sul desktop)\n%4$d Fai doppio clic sul file .zip per decomprimerlo\n%5$d %6$@" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$d Open **Safari**\n%2$d Open het menu **Bestand → Browsergegevens exporteren naar bestand...**\n%3$d Selecteer **wachtwoorden** en sla het bestand op een plek op waar je het kunt vinden (bijv. je bureaublad)\n%4$d Dubbelklik op het .zip-bestand om het uit te pakken\n%5$d %6$@" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$d Otwórz **Safari**\n%2$d Otwórz menu **Plik → Eksportuj dane przeglądania do pliku...**\n%3$d Wybierz **hasła** i zapisz plik w łatwo dostępnym miejscu (np. na biurku)\n%4$d Kliknij dwukrotnie plik .zip w celu jego rozpakowania\n%5$d %6$@" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$d Abre o **Safari**\n%2$d Abre o menu **Ficheiro → Exportar dados de navegação para ficheiro...**\n%3$d Seleciona **palavras-passe** e guarda o ficheiro num local onde o possas encontrar (por exemplo, no ambiente de trabalho)\n%4$d Clica duas vezes no ficheiro .zip para o descomprimir\n%5$d %6$@" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "%1$d Запустите **Safari**\n%2$d Откройте меню **Файл → Экспортировать данные просмотров в файл...**\n%3$d Выберите **пароли** и сохраните файл там, где вы легко его найдете (например, на рабочем столе)\n%4$d Дважды нажмите мышью файл .zip, чтобы распаковать его\n%5$d %6$@" + } + } + } + }, "import.csv.instructions.vivaldi" : { "comment" : "Instructions to import Passwords exported as CSV from Vivaldi browser.\n%N$d - step number\n%2$s - browser name (Vivaldi)\n%5$@ - menu button icon\n%8$@ - “Select Passwords CSV File” button\n**bold text**; _italic text_", "extractionState" : "extracted_with_value", @@ -36833,18 +36893,18 @@ "comment" : "Title shown in an error page tab that warn users of security risks on a website that has been flagged as Malware.", "extractionState" : "extracted_with_value", "localizations" : { - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "Warning: Site May Be Malicious" - } - }, "de" : { "stringUnit" : { "state" : "translated", "value" : "Warnung: Website ist möglicherweise bösartig" } }, + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Warning: Site May Be Malicious" + } + }, "es" : { "stringUnit" : { "state" : "translated", diff --git a/DuckDuckGo/NetworkProtection/NetworkExtensionTargets/NetworkExtensionAndNotificationTargets/Localizable.xcstrings b/DuckDuckGo/NetworkProtection/NetworkExtensionTargets/NetworkExtensionAndNotificationTargets/Localizable.xcstrings index a4d7368f52..b3cab01392 100644 --- a/DuckDuckGo/NetworkProtection/NetworkExtensionTargets/NetworkExtensionAndNotificationTargets/Localizable.xcstrings +++ b/DuckDuckGo/NetworkProtection/NetworkExtensionTargets/NetworkExtensionAndNotificationTargets/Localizable.xcstrings @@ -557,6 +557,12 @@ "value" : "Reconnect" } }, + "en-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reconnect" + } + }, "es" : { "stringUnit" : { "state" : "translated", diff --git a/DuckDuckGo/Statistics/GeneralPixel.swift b/DuckDuckGo/Statistics/GeneralPixel.swift index 2a92aa9845..0c24d26481 100644 --- a/DuckDuckGo/Statistics/GeneralPixel.swift +++ b/DuckDuckGo/Statistics/GeneralPixel.swift @@ -26,6 +26,8 @@ enum GeneralPixel: PixelKitEventV2 { case crash case crashOnCrashHandlersSetUp + case crashReportingSubmissionFailed + case crashReportCRCIDMissing case compileRulesWait(onboardingShown: OnboardingShown, waitTime: CompileRulesWaitTime, result: WaitResult) case launchInitial(cohort: String) case launch(isDefault: Bool) @@ -473,6 +475,12 @@ enum GeneralPixel: PixelKitEventV2 { case .crashOnCrashHandlersSetUp: return "m_mac_crash_on_handlers_setup" + case .crashReportCRCIDMissing: + return "m_mac_crashreporting_crcid-missing" + + case .crashReportingSubmissionFailed: + return "m_mac_crashreporting_submission-failed" + case .compileRulesWait(onboardingShown: let onboardingShown, waitTime: let waitTime, result: let result): return "m_mac_cbr-wait_\(onboardingShown)_\(waitTime)_\(result)" diff --git a/DuckDuckGoVPN/Localizable.xcstrings b/DuckDuckGoVPN/Localizable.xcstrings index c18410e568..43636be9c4 100644 --- a/DuckDuckGoVPN/Localizable.xcstrings +++ b/DuckDuckGoVPN/Localizable.xcstrings @@ -557,6 +557,12 @@ "value" : "DuckDuckGo VPN" } }, + "en-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "DuckDuckGo VPN" + } + }, "es" : { "stringUnit" : { "state" : "translated", diff --git a/LocalPackages/DataBrokerProtection/Package.swift b/LocalPackages/DataBrokerProtection/Package.swift index 79e021c472..2734364a32 100644 --- a/LocalPackages/DataBrokerProtection/Package.swift +++ b/LocalPackages/DataBrokerProtection/Package.swift @@ -29,7 +29,7 @@ let package = Package( targets: ["DataBrokerProtection"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "221.3.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "223.0.0"), .package(path: "../SwiftUIExtensions"), .package(path: "../AppKitExtensions"), .package(path: "../XPCHelper"), diff --git a/LocalPackages/FeatureFlags/Package.swift b/LocalPackages/FeatureFlags/Package.swift index dcb3d9194f..5cdd9db7b0 100644 --- a/LocalPackages/FeatureFlags/Package.swift +++ b/LocalPackages/FeatureFlags/Package.swift @@ -32,7 +32,7 @@ let package = Package( targets: ["FeatureFlags"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "221.3.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "223.0.0"), ], targets: [ // Targets are the basic building blocks of a package, defining a module or a test suite. diff --git a/LocalPackages/NetworkProtectionMac/Package.swift b/LocalPackages/NetworkProtectionMac/Package.swift index 3431d62c5a..9cb58c15b0 100644 --- a/LocalPackages/NetworkProtectionMac/Package.swift +++ b/LocalPackages/NetworkProtectionMac/Package.swift @@ -33,7 +33,7 @@ let package = Package( .library(name: "VPNAppLauncher", targets: ["VPNAppLauncher"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "221.3.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "223.0.0"), .package(url: "https://github.com/airbnb/lottie-spm", exact: "4.4.3"), .package(path: "../AppLauncher"), .package(path: "../UDSHelper"), diff --git a/LocalPackages/NewTabPage/Package.swift b/LocalPackages/NewTabPage/Package.swift index 46ba37e00c..b1e912c146 100644 --- a/LocalPackages/NewTabPage/Package.swift +++ b/LocalPackages/NewTabPage/Package.swift @@ -32,7 +32,7 @@ let package = Package( targets: ["NewTabPage"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "221.3.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "223.0.0"), .package(path: "../WebKitExtensions"), .package(path: "../Utilities"), ], diff --git a/LocalPackages/SubscriptionUI/Package.swift b/LocalPackages/SubscriptionUI/Package.swift index 40bb88836f..c16d25e667 100644 --- a/LocalPackages/SubscriptionUI/Package.swift +++ b/LocalPackages/SubscriptionUI/Package.swift @@ -13,7 +13,7 @@ let package = Package( targets: ["SubscriptionUI"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "221.3.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "223.0.0"), .package(path: "../SwiftUIExtensions"), .package(path: "../FeatureFlags") ], diff --git a/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/Preferences/PreferencesSubscriptionModel.swift b/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/Preferences/PreferencesSubscriptionModel.swift index dfed042d42..e0a7bab18c 100644 --- a/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/Preferences/PreferencesSubscriptionModel.swift +++ b/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/Preferences/PreferencesSubscriptionModel.swift @@ -315,6 +315,11 @@ public final class PreferencesSubscriptionModel: ObservableObject { userEventHandler(.openFeedback) } + @MainActor + func openPrivacyPolicy() { + openURLHandler(URL(string: "https://duckduckgo.com/pro/privacy-terms")!) + } + @MainActor func refreshSubscriptionPendingState() { if subscriptionManager.currentEnvironment.purchasePlatform == .appStore { diff --git a/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/Preferences/PreferencesSubscriptionView.swift b/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/Preferences/PreferencesSubscriptionView.swift index ec66c0b134..8bcc0f4ba0 100644 --- a/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/Preferences/PreferencesSubscriptionView.swift +++ b/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/Preferences/PreferencesSubscriptionView.swift @@ -101,11 +101,6 @@ public struct PreferencesSubscriptionView: View { // Help section helpSection - - // Feedback section - if subscriptionFeatureAvailability.usesUnifiedFeedbackForm, state == .subscriptionActive { - feedbackSection - } } .onAppear(perform: { model.didAppear() @@ -303,24 +298,21 @@ public struct PreferencesSubscriptionView: View { private var helpSection: some View { PreferencePaneSection { TextMenuItemHeader(UserText.preferencesSubscriptionFooterTitle, bottomPadding: 0) - HStack(alignment: .top, spacing: 6) { - if !model.isROWLaunched { - TextMenuItemCaption(UserText.preferencesSubscriptionFooterCaption) - } else { - TextMenuItemCaption(UserText.preferencesSubscriptionHelpFooterCaption) - } - Button(UserText.viewFaqsButton) { model.openFAQ() } + if !model.isROWLaunched { + TextMenuItemCaption(UserText.preferencesSubscriptionFooterCaption) + .padding(.bottom, 8) + } else { + TextMenuItemCaption(UserText.preferencesSubscriptionHelpFooterCaption) + .padding(.bottom, 8) } - } - } + VStack(alignment: .leading, spacing: 16) { + TextButton(UserText.viewFaqsButton, weight: .semibold) { model.openFAQ() } - @ViewBuilder - private var feedbackSection: some View { - PreferencePaneSection { - TextMenuItemHeader(UserText.preferencesSubscriptionFeedbackTitle, bottomPadding: 0) - HStack(alignment: .top, spacing: 6) { - TextMenuItemCaption(UserText.preferencesSubscriptionFeedbackCaption) - Button(UserText.preferencesSubscriptionFeedbackButton) { model.openUnifiedFeedbackForm() } + if subscriptionFeatureAvailability.usesUnifiedFeedbackForm, state == .subscriptionActive { + TextButton(UserText.preferencesSubscriptionFeedbackButton, weight: .semibold) { model.openUnifiedFeedbackForm() } + } + + TextButton(UserText.preferencesPrivacyPolicyButton, weight: .semibold) { model.openPrivacyPolicy() } } } } @@ -495,6 +487,7 @@ private struct SubscriptionDialog: View where Buttons: View { .fixMultilineScrollableText() .foregroundColor(Color(.textPrimary)) } buttons: { + Spacer() buttons() } } diff --git a/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/Resources/Localizable.xcstrings b/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/Resources/Localizable.xcstrings index c7836d0be0..f909d5486a 100644 --- a/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/Resources/Localizable.xcstrings +++ b/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/Resources/Localizable.xcstrings @@ -2265,6 +2265,66 @@ } } }, + "subscription.preferences.privacypolicy.button" : { + "comment" : "Title for the privacy policy button", + "extractionState" : "extracted_with_value", + "localizations" : { + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Datenschutzerklärung und Nutzungsbedingungen" + } + }, + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Privacy Policy and Terms of Service" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Política de privacidad y condiciones del servicio" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Politique de confidentialité et conditions d'utilisation" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Privacy policy e Termini del servizio" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Privacybeleid en servicevoorwaarden" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Polityka prywatności i warunki użytkowania" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Política de Privacidade e Termos de Serviço" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Политика конфиденциальности и условия обслуживания" + } + } + } + }, "subscription.preferences.purchase.button" : { "comment" : "Button to open a page where user can learn more and purchase the subscription", "extractionState" : "extracted_with_value", diff --git a/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/UserText.swift b/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/UserText.swift index a0c9d163a0..8f4e5a018a 100644 --- a/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/UserText.swift +++ b/LocalPackages/SubscriptionUI/Sources/SubscriptionUI/UserText.swift @@ -54,6 +54,7 @@ enum UserText { static let preferencesSubscriptionFeedbackTitle = NSLocalizedString("subscription.preferences.feedback.title", bundle: Bundle.module, value: "Send Feedback", comment: "Title for the subscription feedback section") static let preferencesSubscriptionFeedbackCaption = NSLocalizedString("subscription.preferences.feedback.caption", bundle: Bundle.module, value: "Help improve Privacy Pro. Your feedback matters to us. Feel free to report any issues or provide general feedback.", comment: "Caption for the subscription feedback section") static let preferencesSubscriptionFeedbackButton = NSLocalizedString("subscription.preferences.feedback.button", bundle: Bundle.module, value: "Send Feedback", comment: "Title for the subscription feedback button") + static let preferencesPrivacyPolicyButton = NSLocalizedString("subscription.preferences.privacypolicy.button", bundle: Bundle.module, value: "Privacy Policy and Terms of Service", comment: "Title for the privacy policy button") static func preferencesSubscriptionRenewingCaption(billingPeriod: Subscription.BillingPeriod, formattedDate: String) -> String { let localized: String diff --git a/LocalPackages/WebKitExtensions/Package.swift b/LocalPackages/WebKitExtensions/Package.swift index 44e660355b..df1842ee94 100644 --- a/LocalPackages/WebKitExtensions/Package.swift +++ b/LocalPackages/WebKitExtensions/Package.swift @@ -32,7 +32,7 @@ let package = Package( ), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "221.3.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "223.0.0"), .package(path: "../AppKitExtensions") ], targets: [