diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c23a800196..4abe164a27 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -108,6 +108,16 @@ jobs: --form "file=@${asana_dsyms_path};type=application/zip" fi + - name: Upload debug symbols to S3 + if: always() + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_DEFAULT_REGION: ${{ vars.AWS_DEFAULT_REGION }} + DSYM_S3_PATH: s3://${{ vars.DSYM_BUCKET_NAME }}/${{ vars.DSYM_BUCKET_PREFIX }}/ + run: | + aws s3 cp "${{ github.workspace }}/DuckDuckGo-${{ env.app_version }}-dSYM.zip" ${{ env.DSYM_S3_PATH }} + - name: Send Mattermost message if: ${{ success() || failure() }} # Don't execute when cancelled env: diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 4e5a02029b..cb8f3345e2 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -270,6 +270,9 @@ 37A6A8FE2AFD0208008580A3 /* FaviconsFetcherOnboarding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A6A8FD2AFD0208008580A3 /* FaviconsFetcherOnboarding.swift */; }; 37CBCA9E2A8A659C0050218F /* SyncSettingsAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CBCA9D2A8A659C0050218F /* SyncSettingsAdapter.swift */; }; 37CEFCAC2A673B90001EF741 /* CredentialsCleanupErrorHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CEFCAB2A673B90001EF741 /* CredentialsCleanupErrorHandling.swift */; }; + 37CF91602BB4737300BADCAE /* CrashCollectionOnboarding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CF915F2BB4737300BADCAE /* CrashCollectionOnboarding.swift */; }; + 37CF91622BB474AA00BADCAE /* CrashCollectionOnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CF91612BB474AA00BADCAE /* CrashCollectionOnboardingView.swift */; }; + 37CF91642BB4A82A00BADCAE /* CrashCollectionOnboardingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CF91632BB4A82A00BADCAE /* CrashCollectionOnboardingViewModel.swift */; }; 37DF000A29F9C416002B7D3E /* SyncMetadataDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DF000929F9C416002B7D3E /* SyncMetadataDatabase.swift */; }; 37DF000C29F9CA80002B7D3E /* SyncDataProviders in Frameworks */ = {isa = PBXBuildFile; productRef = 37DF000B29F9CA80002B7D3E /* SyncDataProviders */; }; 37DF000F29F9D635002B7D3E /* SyncBookmarksAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DF000E29F9D635002B7D3E /* SyncBookmarksAdapter.swift */; }; @@ -435,7 +438,6 @@ 85449EFD23FDA71F00512AAF /* KeyboardSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85449EFC23FDA71F00512AAF /* KeyboardSettings.swift */; }; 8544C37C250B827300A0FE73 /* UserText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8544C37A250B823600A0FE73 /* UserText.swift */; }; 8546A54A2A672959003929BF /* MainViewController+Email.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8546A5492A672959003929BF /* MainViewController+Email.swift */; }; - 85480CB429226B3B007E8F13 /* CrashCollectionExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85480CB229226B1E007E8F13 /* CrashCollectionExtensionTests.swift */; }; 85482D8D2462DCD100EDEDD1 /* ActionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85482D8C2462DCD100EDEDD1 /* ActionViewController.swift */; }; 85482D902462DCD100EDEDD1 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 85482D8E2462DCD100EDEDD1 /* MainInterface.storyboard */; }; 85482D942462DCD100EDEDD1 /* OpenAction.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 85482D882462DCD100EDEDD1 /* OpenAction.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; @@ -1428,6 +1430,9 @@ 37A6A8FD2AFD0208008580A3 /* FaviconsFetcherOnboarding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FaviconsFetcherOnboarding.swift; sourceTree = ""; }; 37CBCA9D2A8A659C0050218F /* SyncSettingsAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncSettingsAdapter.swift; sourceTree = ""; }; 37CEFCAB2A673B90001EF741 /* CredentialsCleanupErrorHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialsCleanupErrorHandling.swift; sourceTree = ""; }; + 37CF915F2BB4737300BADCAE /* CrashCollectionOnboarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrashCollectionOnboarding.swift; sourceTree = ""; }; + 37CF91612BB474AA00BADCAE /* CrashCollectionOnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrashCollectionOnboardingView.swift; sourceTree = ""; }; + 37CF91632BB4A82A00BADCAE /* CrashCollectionOnboardingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrashCollectionOnboardingViewModel.swift; sourceTree = ""; }; 37DF000929F9C416002B7D3E /* SyncMetadataDatabase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncMetadataDatabase.swift; sourceTree = ""; }; 37DF000E29F9D635002B7D3E /* SyncBookmarksAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncBookmarksAdapter.swift; sourceTree = ""; }; 37E615742A5F533E00ACD63D /* SyncCredentialsAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncCredentialsAdapter.swift; sourceTree = ""; }; @@ -1585,7 +1590,6 @@ 85449F0023FEAF3000512AAF /* UserDefaultsExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsExtension.swift; sourceTree = ""; }; 8544C37A250B823600A0FE73 /* UserText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserText.swift; sourceTree = ""; }; 8546A5492A672959003929BF /* MainViewController+Email.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainViewController+Email.swift"; sourceTree = ""; }; - 85480CB229226B1E007E8F13 /* CrashCollectionExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrashCollectionExtensionTests.swift; sourceTree = ""; }; 85481A6A2BA46AFB00F9EFB0 /* AppRatingPrompt 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AppRatingPrompt 2.xcdatamodel"; sourceTree = ""; }; 85482D882462DCD100EDEDD1 /* OpenAction.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = OpenAction.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 85482D8C2462DCD100EDEDD1 /* ActionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionViewController.swift; sourceTree = ""; }; @@ -3650,6 +3654,16 @@ name = SettingSyncHandlers; sourceTree = ""; }; + 37CF915E2BB4735F00BADCAE /* Crashes */ = { + isa = PBXGroup; + children = ( + 37CF915F2BB4737300BADCAE /* CrashCollectionOnboarding.swift */, + 37CF91612BB474AA00BADCAE /* CrashCollectionOnboardingView.swift */, + 37CF91632BB4A82A00BADCAE /* CrashCollectionOnboardingViewModel.swift */, + ); + name = Crashes; + sourceTree = ""; + }; 37DF000829F9C3F0002B7D3E /* Sync */ = { isa = PBXGroup; children = ( @@ -3946,6 +3960,7 @@ 9830A05725ED0C5D00DB64DE /* BrowsingMenu */, CB258D1129A4F1BB00DEBA24 /* Configuration */, B652DF02287C01EE00C12A9C /* ContentBlocking */, + 37CF915E2BB4735F00BADCAE /* Crashes */, D6E0C1812B7A2B0700D5E1E9 /* DesktopDownloads */, 310D09192799EF5C00DC0060 /* Downloads */, F143C2C51E4A08F300CFDE3A /* DuckDuckGo.entitlements */, @@ -5391,7 +5406,6 @@ children = ( 85BA58561F34F61C00C6E8CA /* AppUserDefaultsTests.swift */, 4B62C4B925B930DD008912C6 /* AppConfigurationFetchTests.swift */, - 85480CB229226B1E007E8F13 /* CrashCollectionExtensionTests.swift */, 85AFA1202B45D14F0028A504 /* BookmarksMigrationAssertionTests.swift */, ); name = Application; @@ -6824,6 +6838,7 @@ CB9B873C278C8FEA001F4906 /* WidgetEducationView.swift in Sources */, 85F200002215C17B006BB258 /* FindInPage.swift in Sources */, F1386BA41E6846C40062FC3C /* TabDelegate.swift in Sources */, + 37CF91602BB4737300BADCAE /* CrashCollectionOnboarding.swift in Sources */, C1B924B72ACD6E6800EE7B06 /* AutofillNeverSavedTableViewCell.swift in Sources */, 020108A929A7C1CD00644F9D /* AppTrackerImageCache.swift in Sources */, 4B78074E2B183A1F009DB2CF /* SurveyURLBuilder.swift in Sources */, @@ -6905,6 +6920,7 @@ F4D7221026F29A70007D6193 /* BookmarkDetailsCell.swift in Sources */, F1617C131E572E0300DEDCAF /* TabSwitcherViewController.swift in Sources */, 83BE9BC3215D69C1009844D9 /* AppConfigurationFetch.swift in Sources */, + 37CF91622BB474AA00BADCAE /* CrashCollectionOnboardingView.swift in Sources */, 1EEC460627A9499600E75FCB /* DownloadsList.swift in Sources */, D6E83C5A2B2213ED006C8AFB /* SettingsPrivacyView.swift in Sources */, 85B9CB8921AEBDD5009001F1 /* FavoriteHomeCell.swift in Sources */, @@ -6942,6 +6958,7 @@ D66F683D2BB333C100AE93E2 /* SubscriptionContainerView.swift in Sources */, 851B128822200575004781BC /* Onboarding.swift in Sources */, 3151F0EE2735800800226F58 /* VoiceSearchFeedbackView.swift in Sources */, + 37CF91642BB4A82A00BADCAE /* CrashCollectionOnboardingViewModel.swift in Sources */, 857EEB752095FFAC008A005C /* HomeRowInstructionsViewController.swift in Sources */, 311BD1AF2836BB4200AEF6C1 /* AutofillItemsLockedView.swift in Sources */, 85DE681A2B6A8BB000DED4FE /* MainViewCoordinator.swift in Sources */, @@ -7345,7 +7362,6 @@ 851DFD8A212C5EE800D95F20 /* TabSwitcherButtonTests.swift in Sources */, 98983096255B5019003339A2 /* BookmarksCachingSearchTests.swift in Sources */, EE7917912A83DE93008DFF28 /* CombineTestUtilities.swift in Sources */, - 85480CB429226B3B007E8F13 /* CrashCollectionExtensionTests.swift in Sources */, 8540BD5223D8C2220057FDD2 /* PreserveLoginsTests.swift in Sources */, 85F200072217032E006BB258 /* AddressDisplayHelperTests.swift in Sources */, B6AD9E3728D4510A0019CDE9 /* ContentBlockingUpdatingTests.swift in Sources */, @@ -10245,7 +10261,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 136.0.0; + version = 137.0.0; }; }; 9F8FE9472BAE50E50071E372 /* XCRemoteSwiftPackageReference "lottie-spm" */ = { @@ -10285,7 +10301,7 @@ repositoryURL = "https://github.com/duckduckgo/DesignResourcesKit"; requirement = { kind = exactVersion; - version = 2.0.0; + version = 3.0.0; }; }; F486D2EF25069482002D07D7 /* XCRemoteSwiftPackageReference "Kingfisher" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 2bab1f8a41..b0c0f29b29 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" : { - "revision" : "7eab61a08c9535b1a8e59c622fe57d33a310a2fc", - "version" : "136.0.0" + "revision" : "4ce049682cb47a9fb510237070666e5e8bf1e07b", + "version" : "137.0.0" } }, { @@ -59,8 +59,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/DesignResourcesKit", "state" : { - "revision" : "d7ea2561ec7624c224f52e1c9b349075ddf1c782", - "version" : "2.0.0" + "revision" : "ae83941bb277a2750abc2d6697fa278f8c8c5f5e", + "version" : "3.0.0" } }, { @@ -138,7 +138,7 @@ { "identity" : "swift-argument-parser", "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-argument-parser", + "location" : "https://github.com/apple/swift-argument-parser.git", "state" : { "revision" : "46989693916f56d1186bd59ac15124caef896560", "version" : "1.3.1" diff --git a/DuckDuckGo/AboutView.swift b/DuckDuckGo/AboutView.swift index 572c7e6b98..142ae36265 100644 --- a/DuckDuckGo/AboutView.swift +++ b/DuckDuckGo/AboutView.swift @@ -74,9 +74,13 @@ struct AboutViewVersion: View { @EnvironmentObject var viewModel: SettingsViewModel var body: some View { - Section(header: Text("DuckDuckGo for iOS")) { + Section(header: Text("DuckDuckGo for iOS"), footer: Text(UserText.settingsSendCrashReportsDescription)) { SettingsCellView(label: UserText.settingsVersion, accesory: .rightDetail(viewModel.state.version)) + + // Send Crash Reports + SettingsCellView(label: UserText.settingsSendCrashReports, + accesory: .toggle(isOn: viewModel.crashCollectionOptInStatusBinding)) } } } diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index be3bbc7501..7099010a43 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -86,6 +86,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { private var syncStateCancellable: AnyCancellable? private var isSyncInProgressCancellable: AnyCancellable? + private let crashCollection = CrashCollection(platform: .iOS, log: .generalLog) + private var crashReportUploaderOnboarding: CrashCollectionOnboarding? + // MARK: lifecycle @UserDefaultsWrapper(key: .privacyConfigCustomURL, defaultValue: nil) @@ -130,8 +133,21 @@ class AppDelegate: UIResponder, UIApplicationDelegate { Configuration.setURLProvider(AppConfigurationURLProvider()) } - CrashCollection.start { - Pixel.fire(pixel: .dbCrashDetected, withAdditionalParameters: $0, includedParameters: []) + crashCollection.start { pixelParameters, payloads, sendReport in + pixelParameters.forEach { params in + Pixel.fire(pixel: .dbCrashDetected, withAdditionalParameters: params, includedParameters: []) + } + + // Async dispatch because rootViewController may otherwise be nil here + DispatchQueue.main.async { + guard let viewController = self.window?.rootViewController else { + return + } + let dataPayloads = payloads.map { $0.jsonRepresentation() } + let crashReportUploaderOnboarding = CrashCollectionOnboarding(appSettings: AppDependencyProvider.shared.appSettings) + crashReportUploaderOnboarding.presentOnboardingIfNeeded(for: dataPayloads, from: viewController, sendReport: sendReport) + self.crashReportUploaderOnboarding = crashReportUploaderOnboarding + } } clearTmp() diff --git a/DuckDuckGo/AppSettings.swift b/DuckDuckGo/AppSettings.swift index 18422b3487..1bd5c16ea7 100644 --- a/DuckDuckGo/AppSettings.swift +++ b/DuckDuckGo/AppSettings.swift @@ -76,4 +76,6 @@ protocol AppSettings: AnyObject { var isSyncBookmarksPaused: Bool { get } var isSyncCredentialsPaused: Bool { get } + + var crashCollectionOptInStatus: CrashCollectionOptInStatus { get set } } diff --git a/DuckDuckGo/AppUserDefaults.swift b/DuckDuckGo/AppUserDefaults.swift index 3dbd30d89d..5920db2e56 100644 --- a/DuckDuckGo/AppUserDefaults.swift +++ b/DuckDuckGo/AppUserDefaults.swift @@ -23,6 +23,7 @@ import Core import WidgetKit // swiftlint:disable file_length +// swiftlint:disable:next type_body_length public class AppUserDefaults: AppSettings { public struct Notifications { @@ -71,6 +72,8 @@ public class AppUserDefaults: AppSettings { static let autofillIsNewInstallForOnByDefault = "com.duckduckgo.ios.autofillIsNewInstallForOnByDefault" static let favoritesDisplayMode = "com.duckduckgo.ios.favoritesDisplayMode" + + static let crashCollectionOptInStatus = "com.duckduckgo.ios.crashCollectionOptInStatus" } private struct DebugKeys { @@ -343,6 +346,19 @@ public class AppUserDefaults: AppSettings { } } + var crashCollectionOptInStatus: CrashCollectionOptInStatus { + get { + guard let string = userDefaults?.string(forKey: Keys.crashCollectionOptInStatus), + let optInStatus = CrashCollectionOptInStatus(rawValue: string) + else { + return .undetermined + } + return optInStatus + } + set { + userDefaults?.setValue(newValue.rawValue, forKey: Keys.crashCollectionOptInStatus) + } + } } extension AppUserDefaults: AppConfigurationFetchStatistics { diff --git a/DuckDuckGo/Assets.xcassets/Breakage-128.imageset/Breakage-128.pdf b/DuckDuckGo/Assets.xcassets/Breakage-128.imageset/Breakage-128.pdf index cbbcbba634..d23a4d3cf3 100644 Binary files a/DuckDuckGo/Assets.xcassets/Breakage-128.imageset/Breakage-128.pdf and b/DuckDuckGo/Assets.xcassets/Breakage-128.imageset/Breakage-128.pdf differ diff --git a/DuckDuckGo/Assets.xcassets/ChevronDown.imageset/Chevron-Small-Down-16.svg b/DuckDuckGo/Assets.xcassets/ChevronDown.imageset/Chevron-Small-Down-16.svg new file mode 100644 index 0000000000..c0ea2c95d2 --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/ChevronDown.imageset/Chevron-Small-Down-16.svg @@ -0,0 +1,3 @@ + + + diff --git a/DuckDuckGo/Assets.xcassets/ChevronDown.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/ChevronDown.imageset/Contents.json new file mode 100644 index 0000000000..8ede30929f --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/ChevronDown.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Chevron-Small-Down-16.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/DuckDuckGo/Assets.xcassets/ChevronUp.imageset/Chevron-Small-Up-16.svg b/DuckDuckGo/Assets.xcassets/ChevronUp.imageset/Chevron-Small-Up-16.svg new file mode 100644 index 0000000000..28662cd685 --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/ChevronUp.imageset/Chevron-Small-Up-16.svg @@ -0,0 +1,3 @@ + + + diff --git a/DuckDuckGo/Assets.xcassets/ChevronUp.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/ChevronUp.imageset/Contents.json new file mode 100644 index 0000000000..fdcaf843fc --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/ChevronUp.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Chevron-Small-Up-16.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/DuckDuckGo/CrashCollectionOnboarding.swift b/DuckDuckGo/CrashCollectionOnboarding.swift new file mode 100644 index 0000000000..98a364220b --- /dev/null +++ b/DuckDuckGo/CrashCollectionOnboarding.swift @@ -0,0 +1,112 @@ +// +// CrashCollectionOnboarding.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 Combine +import Foundation +import SwiftUI + +enum CrashCollectionOptInStatus: String { + case undetermined, optedIn, optedOut +} + +final class CrashCollectionOnboardingViewController: UIHostingController { + override var supportedInterfaceOrientations: UIInterfaceOrientationMask { + if UIDevice.current.userInterfaceIdiom == .pad { + return .all + } + return .portrait + } +} + +final class CrashCollectionOnboarding: NSObject { + + init(appSettings: AppSettings) { + self.appSettings = appSettings + self.viewModel = CrashCollectionOnboardingViewModel(appSettings: appSettings) + super.init() + } + + @MainActor + func presentOnboardingIfNeeded(for payloads: [Data], from viewController: UIViewController, sendReport: @escaping () -> Void) { + let isCurrentlyPresenting = viewController.presentedViewController != nil + guard shouldPresentOnboarding, !isCurrentlyPresenting else { + if appSettings.crashCollectionOptInStatus == .optedIn { + sendReport() + } + return + } + + let controller = CrashCollectionOnboardingViewController(rootView: CrashCollectionOnboardingView(model: viewModel)) + + if #available(iOS 16.0, *) { + let identifier = UISheetPresentationController.Detent.Identifier("crashReportHidden") + controller.sheetPresentationController?.detents = [.custom(identifier: identifier, resolver: { _ in return 560 }), .large()] + controller.sheetPresentationController?.delegate = self + + detailsCancellable = viewModel.$isViewExpanded + .dropFirst() + .removeDuplicates() + .sink { [weak controller] isViewExpanded in + guard let sheet = controller?.sheetPresentationController else { + return + } + let newDetentIdentifier: UISheetPresentationController.Detent.Identifier = isViewExpanded ? .large : identifier + DispatchQueue.main.async { + sheet.animateChanges { + sheet.selectedDetentIdentifier = newDetentIdentifier + } + } + } + } + + viewModel.setReportDetails(with: payloads) + viewModel.onDismiss = { [weak self, weak viewController] optInStatus in + if optInStatus == .optedIn { + sendReport() + } + viewController?.dismiss(animated: true) + self?.detailsCancellable = nil + } + + viewController.present(controller, animated: true) + } + + private var shouldPresentOnboarding: Bool { + appSettings.crashCollectionOptInStatus == .undetermined + } + + private let appSettings: AppSettings + private let viewModel: CrashCollectionOnboardingViewModel + private var detailsCancellable: AnyCancellable? +} + +@available(iOS 16.0, *) +extension CrashCollectionOnboarding: UISheetPresentationControllerDelegate { + func sheetPresentationControllerDidChangeSelectedDetentIdentifier(_ sheetPresentationController: UISheetPresentationController) { + if sheetPresentationController.selectedDetentIdentifier == .large { + // When the view is expanded manually, only show the report after a slight delay in order to avoid UI glitches + // See also `CrashCollectionOnboardingViewModel.isReportVisible`. + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + self.viewModel.showReport() + } + } else { + viewModel.hideReport(animated: false) + } + } +} diff --git a/DuckDuckGo/CrashCollectionOnboardingView.swift b/DuckDuckGo/CrashCollectionOnboardingView.swift new file mode 100644 index 0000000000..5790714770 --- /dev/null +++ b/DuckDuckGo/CrashCollectionOnboardingView.swift @@ -0,0 +1,168 @@ +// +// CrashCollectionOnboardingView.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 +import DesignResourcesKit + +struct CrashCollectionOnboardingView: View { + + @ObservedObject var model: CrashCollectionOnboardingViewModel + + var body: some View { + if #available(iOS 16.0, *) { + // Using NavigationStack here in order to fix presentation on the iPad, where `Navigation` would use Split Navigation view. + NavigationStack { + contents + } + .background(Color(designSystemColor: .backgroundSheets)) + } else { + NavigationView { + contents + } + .background(Color(designSystemColor: .backgroundSheets)) + } + } + + var contents: some View { + VStack(spacing: 0) { + ScrollView { + VStack(spacing: 24) { + Image(decorative: "Breakage-128") + + Text(UserText.crashReportDialogTitle) + .daxTitle1() + .multilineTextAlignment(.center) + .foregroundColor(Color(designSystemColor: .textPrimary)) + + Text(UserText.crashReportDialogMessage) + .multilineTextAlignment(.center) + .daxBodyRegular() + .foregroundColor(Color(designSystemColor: .textPrimary)) + + if let reportDetails = model.reportDetails { + VStack(spacing: 4) { + + reportDetailsButton + + if model.isReportVisible { + ZStack { + Rectangle() + .foregroundColor(Color(designSystemColor: .container)) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .cornerRadius(4.0) + + Text(reportDetails) + .multilineTextAlignment(.leading) + .font(.crashReport) + .foregroundColor(Color(designSystemColor: .textSecondary)) + .padding(24) + } + } + } + } + } + .padding(.horizontal, 24) + .padding(.vertical, 20) + } + .modifier(ScrollDisabledIfAvailable(isDisabled: !model.isReportVisible)) + + VStack(spacing: 8) { + Button { + withAnimation { + model.crashCollectionOptInStatus = .optedIn + model.onDismiss(.optedIn) + } + } label: { + Text(UserText.crashReportAlwaysSend) + } + .buttonStyle(PrimaryButtonStyle()) + .frame(maxWidth: 360) + + Button { + withAnimation { + model.crashCollectionOptInStatus = .optedOut + model.onDismiss(.optedOut) + } + } label: { + Text(UserText.crashReportNeverSend) + } + .buttonStyle(GhostButtonStyle()) + .frame(maxWidth: 360) + } + .padding(.init(top: 24, leading: 24, bottom: 0, trailing: 24)) + } + .toolbar { + ToolbarItemGroup(placement: .topBarLeading) { + Button { + withAnimation { + model.onDismiss(.undetermined) + } + } label: { + Text(UserText.keyCommandClose) + } + .buttonStyle(.plain) + } + } + } + + var reportDetailsButton: some View { + Button { + model.toggleReportVisible() + } label: { + HStack { + if model.showReportButtonMode == .hideDetails { + Text(UserText.crashReportHideDetails).daxButton() + Image("ChevronUp").frame(width: 7, height: 12) + } else { + Text(UserText.crashReportShowDetails).daxButton() + Image("ChevronDown").frame(width: 7, height: 12) + } + } + } + .buttonStyle(.plain) + .foregroundColor(Color(designSystemColor: .textSecondary)) + .frame(height: 44) + } +} + +private extension Font { + static var crashReport: Font { + let descriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .caption2) + return Font(uiFont: .monospacedSystemFont(ofSize: descriptor.pointSize, weight: .regular)) + } +} + +private struct ScrollDisabledIfAvailable: ViewModifier { + let isDisabled: Bool + + func body(content: Content) -> some View { + if #available(iOS 16.0, *) { + return content.scrollDisabled(isDisabled) + } + return content + } +} + +#Preview { + let model = CrashCollectionOnboardingViewModel(appSettings: AppDependencyProvider.shared.appSettings) + // swiftlint:disable:next line_length + model.setReportDetails(with: ["test report details test report details test report details test report details test report details\n\ntest report details\ntest report details\ntest report details\ntest report details\ntest report details\ntest report details\ntest report details\ntest report details\ntest report details\ntest report details\ntest report details\ntest report details\ntest report details\ntest report details\ntest report details\ntest report details\ntest report details\ntest report details".data(using: .utf8)!]) + return CrashCollectionOnboardingView(model: model) +} diff --git a/DuckDuckGo/CrashCollectionOnboardingViewModel.swift b/DuckDuckGo/CrashCollectionOnboardingViewModel.swift new file mode 100644 index 0000000000..bd641f257b --- /dev/null +++ b/DuckDuckGo/CrashCollectionOnboardingViewModel.swift @@ -0,0 +1,111 @@ +// +// CrashCollectionOnboardingViewModel.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 SwiftUI + +final class CrashCollectionOnboardingViewModel: ObservableObject { + + init(appSettings: AppSettings) { + self.appSettings = appSettings + } + + let appSettings: AppSettings + var onDismiss: (CrashCollectionOptInStatus) -> Void = { _ in } + + func toggleReportVisible(animated: Bool = true) { + if isReportVisible { + hideReport(animated: animated) + } else { + showReport(animated: animated) + } + } + + func showReport(animated: Bool = true) { + withAnimation(animated ? .default : nil) { + isReportVisible = true + } + showReportButtonMode = .hideDetails + } + + func hideReport(animated: Bool = true) { + withAnimation(animated ? .default : nil) { + isReportVisible = false + } + showReportButtonMode = .showDetails + } + + func setReportDetails(with payloads: [Data]) { + guard let firstPayload = payloads.first else { + reportDetails = nil + return + } + reportDetails = String(data: firstPayload, encoding: .utf8) + } + + /** + * Boolean value deciding whether the crash report contents are currently presented in the view. + * + * Showing crash report contents causes the modal view to expand to full screen (on iOS 16 and above, older OSes + * present the modal in full screen at all times). Expanding the view while rendering crash report contents + * is causing glitches so we're using 2 properties here: + * - `isReportVisible` controls whether the report is shown on the view + * - `isViewExpanded` controls whether the view is expanded (it's disregarded on pre iOS 16 devices). + * + * Updates to this property are mirrored to `isViewExpanded`, but to avoid glitches in the UI, when showing the report, + * view is expanded after a slight delay. + */ + @Published private(set) var isReportVisible: Bool = false { + didSet { + if isReportVisible { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + self.isViewExpanded = true + } + } else { + isViewExpanded = false + } + } + } + + /** + * Used by the presenting controller. Follows `isReportVisible` but with a delay when setting that one to `true`. + */ + @Published private(set) var isViewExpanded: Bool = false + + /** + * Responsible for the UI of the button toggling crash report details visibility. + * + * Follows `isReportVisible`, but never animates changes. + */ + @Published private(set) var showReportButtonMode: ShowReportButtonMode = .showDetails + enum ShowReportButtonMode { + case showDetails, hideDetails + } + + private(set) var reportDetails: String? + + var crashCollectionOptInStatus: CrashCollectionOptInStatus { + get { + appSettings.crashCollectionOptInStatus + } + set { + appSettings.crashCollectionOptInStatus = newValue + } + } +} diff --git a/DuckDuckGo/Debug.storyboard b/DuckDuckGo/Debug.storyboard index ba66cfd846..1b1a328c55 100644 --- a/DuckDuckGo/Debug.storyboard +++ b/DuckDuckGo/Debug.storyboard @@ -1,9 +1,9 @@ - + - + @@ -21,7 +21,7 @@ - + @@ -305,6 +305,15 @@ + + + + + + + + + @@ -898,34 +907,34 @@ - + - + - + - + diff --git a/DuckDuckGo/RootDebugViewController.swift b/DuckDuckGo/RootDebugViewController.swift index a12bccf6db..d83856a29b 100644 --- a/DuckDuckGo/RootDebugViewController.swift +++ b/DuckDuckGo/RootDebugViewController.swift @@ -37,7 +37,8 @@ class RootDebugViewController: UITableViewController { case toggleInspectableWebViews = 668 case toggleInternalUserState = 669 case openVanillaBrowser = 670 - case refreshConfig = 671 + case resetSendCrashLogs = 671 + case refreshConfig = 672 } @IBOutlet weak var shareButton: UIBarButtonItem! @@ -147,6 +148,8 @@ class RootDebugViewController: UITableViewController { NotificationCenter.default.post(Notification(name: AppUserDefaults.Notifications.inspectableWebViewsToggled)) case .openVanillaBrowser: openVanillaBrowser(nil) + case .resetSendCrashLogs: + AppUserDefaults().crashCollectionOptInStatus = .undetermined case .refreshConfig: fetchAssets() } diff --git a/DuckDuckGo/SettingsAboutViewOld.swift b/DuckDuckGo/SettingsAboutViewOld.swift index 9415bb048a..bcca5adf35 100644 --- a/DuckDuckGo/SettingsAboutViewOld.swift +++ b/DuckDuckGo/SettingsAboutViewOld.swift @@ -25,8 +25,8 @@ struct SettingsAboutViewOld: View { @EnvironmentObject var viewModel: SettingsViewModel var body: some View { - Section(header: Text(UserText.settingsAboutSection)) { - + Section(header: Text(UserText.settingsAboutSection), footer: Text(UserText.settingsSendCrashReportsDescription)) { + SettingsCellView(label: UserText.settingsAboutDDG, action: { viewModel.presentLegacyView(.about) }, disclosureIndicator: true, @@ -38,7 +38,9 @@ struct SettingsAboutViewOld: View { SettingsCellView(label: UserText.settingsFeedback, action: { viewModel.presentLegacyView(.feedback) }, isButton: true) - + + SettingsCellView(label: UserText.settingsSendCrashReports, + accesory: .toggle(isOn: viewModel.crashCollectionOptInStatusBinding)) } } diff --git a/DuckDuckGo/SettingsState.swift b/DuckDuckGo/SettingsState.swift index 3bb858a390..bb0922f218 100644 --- a/DuckDuckGo/SettingsState.swift +++ b/DuckDuckGo/SettingsState.swift @@ -80,7 +80,8 @@ struct SettingsState { // About properties var version: String - + var crashCollectionOptInStatus: CrashCollectionOptInStatus + // Features var debugModeEnabled: Bool var voiceSearchEnabled: Bool @@ -113,6 +114,7 @@ struct SettingsState { allowUniversalLinks: true, activeWebsiteAccount: nil, version: "0.0.0.0", + crashCollectionOptInStatus: .undetermined, debugModeEnabled: false, voiceSearchEnabled: false, speechRecognitionAvailable: false, diff --git a/DuckDuckGo/SettingsViewModel.swift b/DuckDuckGo/SettingsViewModel.swift index 05d427f3df..7567ed21f3 100644 --- a/DuckDuckGo/SettingsViewModel.swift +++ b/DuckDuckGo/SettingsViewModel.swift @@ -363,6 +363,16 @@ final class SettingsViewModel: ObservableObject { ) } + var crashCollectionOptInStatusBinding: Binding { + Binding( + get: { self.state.crashCollectionOptInStatus == .optedIn }, + set: { + self.appSettings.crashCollectionOptInStatus = $0 ? .optedIn : .optedOut + self.state.crashCollectionOptInStatus = $0 ? .optedIn : .optedOut + } + ) + } + var cookiePopUpProtectionStatus: StatusIndicator { return appSettings.autoconsentEnabled ? .on : .off } @@ -422,6 +432,7 @@ extension SettingsViewModel { allowUniversalLinks: appSettings.allowUniversalLinks, activeWebsiteAccount: nil, version: versionProvider.versionAndBuildNumber, + crashCollectionOptInStatus: appSettings.crashCollectionOptInStatus, debugModeEnabled: featureFlagger.isFeatureOn(.debugMenu) || isDebugBuild, voiceSearchEnabled: AppDependencyProvider.shared.voiceSearchHelper.isVoiceSearchEnabled, speechRecognitionAvailable: AppDependencyProvider.shared.voiceSearchHelper.isSpeechRecognizerAvailable, diff --git a/DuckDuckGo/UserText.swift b/DuckDuckGo/UserText.swift index ef91ca218d..21e036fced 100644 --- a/DuckDuckGo/UserText.swift +++ b/DuckDuckGo/UserText.swift @@ -1093,7 +1093,18 @@ But if you *do* want a peek under the hood, you can find more information about public static let settingsAboutDDG = NSLocalizedString("settings.about.ddg", value: "About DuckDuckGo", comment: "Settings cell for About DDG") public static let settingsVersion = NSLocalizedString("settings.version", value: "Version", comment: "Settings cell for Version") - + public static let settingsSendCrashReports = NSLocalizedString("settings.send.crash.reports", value: "Send Crash Reports", comment: "Settings cell for Send Crash Reports") + public static let settingsSendCrashReportsDescription = NSLocalizedString("settings.send.crash.reports.description", value: "Automatically send crash reports to DuckDuckGo.", comment: "Explanation of Send Crash Reports settings option") + + // MARK: Crash Reporting + + public static let crashReportDialogTitle = NSLocalizedString("crash.report.dialog.title", value: "Automatically send crash reports?", comment: "Crash Report dialog title") + public static let crashReportDialogMessage = NSLocalizedString("crash.report.dialog.message", value: "Crash reports help DuckDuckGo diagnose issues and improve our products. They contain no personally identifiable information.", comment: "Crash Report dialog message") + public static let crashReportShowDetails = NSLocalizedString("crash.report.dialog.show.details", value: "See what's sent", comment: "Crash Report show details button title") + public static let crashReportHideDetails = NSLocalizedString("crash.report.dialog.hide.details", value: "Hide", comment: "Crash Report hide details button title") + public static let crashReportAlwaysSend = NSLocalizedString("crash.report.dialog.always.send", value: "Always Send Crash Reports", comment: "Crash Report always send button title") + public static let crashReportNeverSend = NSLocalizedString("crash.report.dialog.never.send", value: "Never Send", comment: "Crash Report never send button title") + // MARK: Subscriptions // Loaders diff --git a/DuckDuckGo/bg.lproj/Localizable.strings b/DuckDuckGo/bg.lproj/Localizable.strings index 00053cf3a6..04456e3172 100644 --- a/DuckDuckGo/bg.lproj/Localizable.strings +++ b/DuckDuckGo/bg.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "контейнер: %@"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Винаги да се изпращат доклади за сривове"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Скрий"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Докладите за сривове помагат на DuckDuckGo да диагностицира проблеми и да подобрява продуктите си. Те не съдържат никаква лична информация."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Никога да не се изпращат"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Вижте какво е изпратено"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Автоматично изпращане на доклади за сривове?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Миналия месец"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Настройки за търсене"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Изпращане на доклади за сривове"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Автоматично изпращане на доклади за сривове до DuckDuckGo."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Задайте позицията на адресната лента"; diff --git a/DuckDuckGo/cs.lproj/Localizable.strings b/DuckDuckGo/cs.lproj/Localizable.strings index 5e519e844d..5c4f26a14b 100644 --- a/DuckDuckGo/cs.lproj/Localizable.strings +++ b/DuckDuckGo/cs.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "bucket: % @"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Vždycky odesílat hlášení o selhání"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Skrýt"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Hlášení o selháních pomáhají DuckDuckGo diagnostikovat problémy a zlepšovat služby. Neobsahují žádné osobní údaje umožňující identifikaci."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Nikdy neodesílat"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Podívej se, co se odesílá"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Automaticky odesílat hlášení o selháních?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Minulý měsíc"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Nastavení vyhledávání"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Odesílat hlášení o selháních"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Automaticky DuckDuckGo odesílat hlášení o selháních."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Nastavte pozici adresního řádku"; diff --git a/DuckDuckGo/da.lproj/Localizable.strings b/DuckDuckGo/da.lproj/Localizable.strings index 3f7cc6948c..ac5dfcfdc0 100644 --- a/DuckDuckGo/da.lproj/Localizable.strings +++ b/DuckDuckGo/da.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "bucket: %@"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Send altid fejlrapporter"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Skjul"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Fejlrapporter hjælper DuckDuckGo med at diagnosticere problemer og forbedre vores produkter. De indeholder ingen personligt identificerbare oplysninger."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Send aldrig"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Se, hvad der bliver sendt"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Send automatisk fejlrapporter?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Seneste Måned"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Indstillinger for søgning"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Send fejlrapporter"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Send automatisk fejlrapporter til DuckDuckGo."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Angiv placeringen af adresselinjen"; diff --git a/DuckDuckGo/de.lproj/Localizable.strings b/DuckDuckGo/de.lproj/Localizable.strings index 7adf30eaa1..0b3dcc0ed2 100644 --- a/DuckDuckGo/de.lproj/Localizable.strings +++ b/DuckDuckGo/de.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "Bucket: %@"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Absturzberichte immer senden"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Ausblenden"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Crash-Berichte helfen DuckDuckGo, Probleme zu diagnostizieren und unsere Produkte zu verbessern. Sie enthalten keine personenbezogenen Daten."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Niemals senden"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Sehen, was gesendet wurde"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Automatisch Absturzberichte senden?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Letzter Monat"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Sucheinstellungen"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Absturzberichte senden"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Senden Sie Absturzberichte automatisch an DuckDuckGo."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Position deiner Adressleiste festlegen"; diff --git a/DuckDuckGo/el.lproj/Localizable.strings b/DuckDuckGo/el.lproj/Localizable.strings index 0e456044e2..d4fd5d9c10 100644 --- a/DuckDuckGo/el.lproj/Localizable.strings +++ b/DuckDuckGo/el.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "κάδος: %@"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Να αποστέλλονται πάντα αναφορές σφαλμάτων"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Απόκρυψη"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Οι αναφορές σφαλμάτων βοηθούν το DuckDuckGo να πραγματοποιεί διάγνωση προβλημάτων και να βελτιώνει τα προϊόντα μας. Δεν περιέχουν προσωπικά στοιχεία ταυτοποίησης."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Να μην αποστέλλεται ποτέ"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Δείτε τι έχει αποσταλεί"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Αυτόματη αποστολή αναφορών σφαλμάτων;"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Προηγούμενος μήνας"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Ρυθμίσεις αναζήτησης"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Αποστολή αναφορών σφαλμάτων"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Αυτόματη αποστολή αναφορών σφάλματος στο DuckDuckGo."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Ορίστε τη θέση της γραμμής διευθύνσεών σας"; diff --git a/DuckDuckGo/en.lproj/Localizable.strings b/DuckDuckGo/en.lproj/Localizable.strings index 3aef9d64e7..35a65e5cfe 100644 --- a/DuckDuckGo/en.lproj/Localizable.strings +++ b/DuckDuckGo/en.lproj/Localizable.strings @@ -823,6 +823,24 @@ /* Title of prompt for users where they can choose to import or export an HTML file containing webpage bookmarks */ "bookmarks.importExport.title" = "Import an HTML file of bookmarks from another browser, or export your existing bookmarks."; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Always Send Crash Reports"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Hide"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Crash reports help DuckDuckGo diagnose issues and improve our products. They contain no personally identifiable information."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Never Send"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "See what's sent"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Automatically send crash reports?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Past month"; @@ -1971,6 +1989,12 @@ But if you *do* want a peek under the hood, you can find more information about /* Header of settings related to search */ "settings.search.settings" = "Search Settings"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Send Crash Reports"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Automatically send crash reports to DuckDuckGo."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Set Your Address Bar Position"; diff --git a/DuckDuckGo/es.lproj/Localizable.strings b/DuckDuckGo/es.lproj/Localizable.strings index 20258678ba..432cdfd03b 100644 --- a/DuckDuckGo/es.lproj/Localizable.strings +++ b/DuckDuckGo/es.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "bucket: %@"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Enviar siempre informes de fallos"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Ocultar"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Los informes de fallos ayudan a DuckDuckGo a diagnosticar problemas y a mejorar nuestros productos. No contienen información de identificación personal."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "No enviar nunca"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Ver lo enviado"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "¿Enviar automáticamente informes de fallos?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Mes pasado"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Configuración de búsqueda"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Enviar informes de fallos"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Envía automáticamente informes de fallos a DuckDuckGo."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Establece la posición de la barra de direcciones"; diff --git a/DuckDuckGo/et.lproj/Localizable.strings b/DuckDuckGo/et.lproj/Localizable.strings index e1f77b5e53..2bb86d28bb 100644 --- a/DuckDuckGo/et.lproj/Localizable.strings +++ b/DuckDuckGo/et.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "andmekogum: %@"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Saada krahhiaruanded alati"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Peida"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Krahhiaruanded aitavad DuckDuckGo-l probleeme diagnoosida ja oma tooteid täiustada. Need ei sisalda isikut tuvastavat teavet."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Ära saada kunagi"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Vaata, mida saadetakse"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Kas saata krahhiaruanded automaatselt?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Viimane kuu"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Otsingu seaded"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Saada krahhiaruanded"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Saada krahhiaruanded DuckDuckGo-le automaatselt."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Määra aadressiriba asukoht"; diff --git a/DuckDuckGo/fi.lproj/Localizable.strings b/DuckDuckGo/fi.lproj/Localizable.strings index 4e462459a1..b12566f804 100644 --- a/DuckDuckGo/fi.lproj/Localizable.strings +++ b/DuckDuckGo/fi.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "bucket: %@"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Lähetä aina virheraportit"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Piilota"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Virheraportit auttavat DuckDuckGota diagnosoimaan ongelmia ja parantamaan tuotteitamme. Ne eivät sisällä henkilön tunnistamisen mahdollistavia tietoja."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Älä lähetä koskaan"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Katso, mitä on lähetetty"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Lähetetäänkö virheraportit automaattisesti?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Viime kuukausi"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Hakuasetukset"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Lähetä virheraportit"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Lähetä virheraportit automaattisesti DuckDuckGolle."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Aseta osoitekentän sijainti"; diff --git a/DuckDuckGo/fr.lproj/Localizable.strings b/DuckDuckGo/fr.lproj/Localizable.strings index 5dd2f16fa4..7ddae53c88 100644 --- a/DuckDuckGo/fr.lproj/Localizable.strings +++ b/DuckDuckGo/fr.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "case : %@"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Toujours envoyer des rapports de plantage"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Masquer"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Les rapports de plantage aident DuckDuckGo à diagnostiquer les problèmes et à améliorer ses produits. Ils ne contiennent aucune information susceptible de vous identifier personnellement."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Ne jamais envoyer"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Voir ce qui est envoyé"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Envoyer automatiquement des rapports de plantage ?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Le mois dernier"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Paramètres de recherche"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Envoyer les rapports de plantage"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Envoyez automatiquement des rapports de plantage à DuckDuckGo."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Définissez la position de votre barre d'adresse"; diff --git a/DuckDuckGo/hr.lproj/Localizable.strings b/DuckDuckGo/hr.lproj/Localizable.strings index e9db353d37..e6cb3c7399 100644 --- a/DuckDuckGo/hr.lproj/Localizable.strings +++ b/DuckDuckGo/hr.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "bucket: %@"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Uvijek šalji izvješća o padu programa"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Sakrij"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Izvješća o padovima pomažu DuckDuckGou u dijagnosticiranju problema i poboljšanju naših proizvoda. Oni ne sadrže nikakve osobne podatke koji bi omogućilitvoju identifikaciju."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Nikada ne šalji"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Pogledaj što je poslano"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Automatski slati izvješća o padu?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Prošli mjesec"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Postavke pretraživanja"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Šalji izvješća o padu"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Automatski šalji izvješća o padu DuckDuckGou."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Postavi položaj adresne trake"; diff --git a/DuckDuckGo/hu.lproj/Localizable.strings b/DuckDuckGo/hu.lproj/Localizable.strings index 661fa3fe17..e9fbf48326 100644 --- a/DuckDuckGo/hu.lproj/Localizable.strings +++ b/DuckDuckGo/hu.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "gyűjtő: %@"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Mindig küldjön hibajelentést"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Elrejtés"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "A hibajelentések segítséget nyújtanak ahhoz, hogy a DuckDuckGo diagnosztizálhassa a problémákat, és tökéletesíthesse termékeit. A jelentések nem tartalmaznak személyes azonosításra alkalmas adatokat."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Soha ne küldjön"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Elküldött adatok megtekintése"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Hibajelentések automatikus küldése?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Elmúlt hónapban"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Keresési beállítások"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Hibajelentések küldése"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Hibajelentések automatikus küldése a DuckDuckGo részére."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Állítsd be a címsor elhelyezkedését"; diff --git a/DuckDuckGo/it.lproj/Localizable.strings b/DuckDuckGo/it.lproj/Localizable.strings index 92f3696bf9..98ddfef670 100644 --- a/DuckDuckGo/it.lproj/Localizable.strings +++ b/DuckDuckGo/it.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "bucket: %@"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Invia sempre segnalazioni di arresto anomalo"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Nascondi"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Le segnalazioni sugli arresti anomali aiutano DuckDuckGo a diagnosticare i problemi e a migliorare i nostri prodotti. Non contengono informazioni personali identificabili."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Non inviare mai"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Controlla cosa è stato inviato"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Inviare automaticamente le segnalazioni di arresto anomalo?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Ultimo mese"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Impostazioni di ricerca"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Invia segnalazioni di arresto anomalo"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Invia automaticamente le segnalazioni di arresto anomalo a DuckDuckGo."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Imposta la posizione della barra degli indirizzi"; diff --git a/DuckDuckGo/lt.lproj/Localizable.strings b/DuckDuckGo/lt.lproj/Localizable.strings index 84d57c61fa..d31c502d39 100644 --- a/DuckDuckGo/lt.lproj/Localizable.strings +++ b/DuckDuckGo/lt.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "talpykla: %@"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Visada siųsti strigčių ataskaitas"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Slėpti"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Strigčių ataskaitos padeda „DuckDuckGo“ diagnozuoti problemas ir tobulinti mūsų produktus. Jose nėra jokios asmenį identifikuojančios informacijos."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Niekada nesiųsti"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Žiūrėti, kas išsiųsta"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Automatiškai siųsti pranešimus apie strigtis?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Praėjęs mėnuo"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Paieškos nustatymai"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Siųsti strigčių ataskaitas"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Automatiškai siųskite strigčių ataskaitas „DuckDuckGo“."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Nustatykite adreso juostos padėtį"; diff --git a/DuckDuckGo/lv.lproj/Localizable.strings b/DuckDuckGo/lv.lproj/Localizable.strings index 02c34c5630..4cc6e062bd 100644 --- a/DuckDuckGo/lv.lproj/Localizable.strings +++ b/DuckDuckGo/lv.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "bucket: %@"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Vienmēr sūtīt avārijas ziņojumus"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Paslēpt"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Ziņojumi par avārijām palīdz DuckDuckGo diagnosticēt problēmas un uzlabot mūsu produktus. Tie nesatur nekādu personu identificējošu informāciju."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Nekad nesūtīt"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Skatīt, kas tiek nosūtīts"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Vai automātiski nosūtīt avārijas ziņojumus?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Iepriekšējais mēnesis"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Meklēšanas iestatījumi"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Nosūtīt avārijas ziņojumus"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Automātiski nosūtīt avārijas ziņojumus DuckDuckGo."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Iestati adreses joslas pozīciju"; diff --git a/DuckDuckGo/nb.lproj/Localizable.strings b/DuckDuckGo/nb.lproj/Localizable.strings index ded69da56c..a2ebb6994b 100644 --- a/DuckDuckGo/nb.lproj/Localizable.strings +++ b/DuckDuckGo/nb.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "bucket: %@"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Send alltid krasjrapporter"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Skjul"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Krasjrapporter hjelper DuckDuckGo med å diagnostisere problemer og forbedre produktene våre. De inneholder ingen personlig identifiserbar informasjon."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Aldri send"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Se hva som er sendt"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Sende krasjrapporter automatisk?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Foregående måned"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Søkeinnstillinger"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Send krasjrapporter"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Send krasjrapporter automatisk til DuckDuckGo."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Angi plassering av adressefeltet"; diff --git a/DuckDuckGo/nl.lproj/Localizable.strings b/DuckDuckGo/nl.lproj/Localizable.strings index 29139e5061..0d89299a7a 100644 --- a/DuckDuckGo/nl.lproj/Localizable.strings +++ b/DuckDuckGo/nl.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "bucket: %@"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Crashrapporten altijd verzenden"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Verbergen"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Crashrapporten helpen DuckDuckGo om problemen vast te stellen en onze producten te verbeteren. Ze bevatten geen persoonlijk identificeerbare informatie."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Nooit verzenden"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Bekijk wat er is verzonden"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Automatisch crashrapporten verzenden?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Afgelopen maand"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Zoekinstellingen"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Crashrapporten verzenden"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Verzend automatisch crashrapporten naar DuckDuckGo."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Positie van adresbalk instellen"; diff --git a/DuckDuckGo/pl.lproj/Localizable.strings b/DuckDuckGo/pl.lproj/Localizable.strings index 946aeff2c3..9cd84f98d4 100644 --- a/DuckDuckGo/pl.lproj/Localizable.strings +++ b/DuckDuckGo/pl.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "Zbiór: %@"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Zawsze wysyłaj raporty o awariach"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Ukryj"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Raporty o awariach pomagają zespołowi DuckDuckGo diagnozować problemy i ulepszać produkty. Nie zawierają żadnych danych osobowych."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Nigdy nie wysyłaj"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Zobacz, co wysłano"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Automatycznie wysyłać raporty o awariach?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Ostatni miesiąc"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Ustawienia wyszukiwania"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Wyślij raporty o awariach"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Automatycznie wysyłaj raporty o awariach do DuckDuckGo."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Ustaw pozycję paska adresu"; diff --git a/DuckDuckGo/pt.lproj/Localizable.strings b/DuckDuckGo/pt.lproj/Localizable.strings index 363aa8c51d..09cc109e8a 100644 --- a/DuckDuckGo/pt.lproj/Localizable.strings +++ b/DuckDuckGo/pt.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "grupo: %@"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Enviar sempre relatórios de falhas"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Ocultar"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Os relatórios de falhas ajudam a DuckDuckGo a diagnosticar problemas e a melhorar os nossos produtos. Não contêm nenhuma informação pessoal identificável."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Nunca enviar"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Vê o que foi enviado"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Enviar relatórios de falhas automaticamente?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Mês passado"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Definições de pesquisa"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Enviar relatórios de falhas"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Envia relatórios de falhas automaticamente para a DuckDuckGo."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Define a Posição da Barra de Endereços"; diff --git a/DuckDuckGo/ro.lproj/Localizable.strings b/DuckDuckGo/ro.lproj/Localizable.strings index bf9734e820..8c48167b1f 100644 --- a/DuckDuckGo/ro.lproj/Localizable.strings +++ b/DuckDuckGo/ro.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "bucket: %@"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Trimite întotdeauna rapoarte de cădere"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Ascunde"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Rapoartele de cădere ajută DuckDuckGo să diagnosticheze problemele și să îmbunătățească produsele noastre. Acestea nu conțin informații de identificare personală."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Nu trimite niciodată"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Vezi ce se trimite"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Trimite automat rapoarte de cădere?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Ultima lună"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Setări de căutare"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Trimite rapoarte de cădere"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Trimite automat rapoarte de cădere către DuckDuckGo."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Setează poziția barei de adrese"; diff --git a/DuckDuckGo/ru.lproj/Localizable.strings b/DuckDuckGo/ru.lproj/Localizable.strings index a933ce6198..996abbc0f8 100644 --- a/DuckDuckGo/ru.lproj/Localizable.strings +++ b/DuckDuckGo/ru.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "контейнер: %@"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Всегда отправлять отчеты о сбоях"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Скрыть"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Отчеты о сбоях помогают нам диагностировать проблемы и совершенствовать продукты. Они не содержат информации, по которой можно установить вашу личность."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Не отправлять"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Что было отправлено"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Автоматически отправлять отчеты о сбоях?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "За последний месяц"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Настройки поиска"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Отправлять отчеты о сбоях"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Автоматически отправлять отчеты о сбоях в DuckDuckGo."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Настроить положение адресной строки"; diff --git a/DuckDuckGo/sk.lproj/Localizable.strings b/DuckDuckGo/sk.lproj/Localizable.strings index 1e760bd027..ab0629b230 100644 --- a/DuckDuckGo/sk.lproj/Localizable.strings +++ b/DuckDuckGo/sk.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "vedro: %@"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Vždy odosielať správy o zlyhaní"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Skryť"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Hlásenia o zlyhaniach pomáhajú spoločnosti DuckDuckGo diagnostikovať problémy a zlepšovať naše produkty. Neobsahujú žiadne osobné identifikačné údaje."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Nikdy neposielať"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Pozrite sa, čo bolo odoslané"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Majú sa správy o zlyhaní odosielať automaticky?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Minulý mesiac"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Nastavenia vyhľadávania"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Odosielať hlásenia o zlyhaní"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Automaticky odosielať správy o zlyhaní pre DuckDuckGo."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Nastavenie polohy panela s adresou"; diff --git a/DuckDuckGo/sl.lproj/Localizable.strings b/DuckDuckGo/sl.lproj/Localizable.strings index 7c8a217a51..93f72d73c2 100644 --- a/DuckDuckGo/sl.lproj/Localizable.strings +++ b/DuckDuckGo/sl.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "zbiralnik: %@"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Vedno pošlji poročila o zrušitvah"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Skrij se"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Poročila o zrušitvah pomagajo DuckDuckGo pri diagnosticiranju težav in izboljševanju naših izdelkov. Ne vsebujejo nobenih osebnih podatkov."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Nikoli ne pošilji"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Oglejte si, kaj je poslano"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Želite samodejno pošiljati poročila o zrušitvah?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Pretekli mesec"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Nastavitve iskanja"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Pošljite poročila o zrušitvah"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Poročila o zrušitvi samodejno pošljite DuckDuckGo."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Nastavitev položaja naslovne vrstice"; diff --git a/DuckDuckGo/sv.lproj/Localizable.strings b/DuckDuckGo/sv.lproj/Localizable.strings index 9267c273e9..6760e1f5a6 100644 --- a/DuckDuckGo/sv.lproj/Localizable.strings +++ b/DuckDuckGo/sv.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "bucket: %@"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Skicka alltid kraschrapporter"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Dölj"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Kraschrapporter hjälper DuckDuckGo att diagnostisera problem och förbättra sina produkter. De innehåller ingen personligt identifierbar information."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Skicka aldrig"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Se vad som skickats"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Skicka kraschrapporter automatiskt?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Senaste månaden"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Sökinställningar"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Skicka kraschrapporter"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Skicka kraschrapporter automatiskt till DuckDuckGo."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Ange din adressfältsposition"; diff --git a/DuckDuckGo/tr.lproj/Localizable.strings b/DuckDuckGo/tr.lproj/Localizable.strings index eafd4e90a9..246c57c413 100644 --- a/DuckDuckGo/tr.lproj/Localizable.strings +++ b/DuckDuckGo/tr.lproj/Localizable.strings @@ -826,6 +826,24 @@ /* No comment provided by engineer. */ "bucket: %@" = "kova: %@"; +/* Crash Report always send button title */ +"crash.report.dialog.always.send" = "Kilitlenme Raporlarını Her Zaman Gönder"; + +/* Crash Report hide details button title */ +"crash.report.dialog.hide.details" = "Gizle"; + +/* Crash Report dialog message */ +"crash.report.dialog.message" = "Kilitlenme raporları DuckDuckGo'nun sorunları teşhis etmesine ve ürünlerimizi geliştirmesine yardımcı olur. Kişisel olarak tanımlanabilir hiçbir bilgi içermezler."; + +/* Crash Report never send button title */ +"crash.report.dialog.never.send" = "Hiçbir Zaman Gönderme"; + +/* Crash Report show details button title */ +"crash.report.dialog.show.details" = "Ne gönderildiğini gör"; + +/* Crash Report dialog title */ +"crash.report.dialog.title" = "Kilitlenme raporları otomatik olarak gönderilsin mi?"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Geçen ay"; @@ -1980,6 +1998,12 @@ /* Header of settings related to search */ "settings.search.settings" = "Arama Ayarları"; +/* Settings cell for Send Crash Reports */ +"settings.send.crash.reports" = "Kilitlenme Raporlarını Gönder"; + +/* Explanation of Send Crash Reports settings option */ +"settings.send.crash.reports.description" = "Kilitlenme raporlarını DuckDuckGo'ya otomatik olarak gönderin."; + /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Adres Çubuğu Konumunuzu Ayarlayın"; diff --git a/DuckDuckGoTests/AppSettingsMock.swift b/DuckDuckGoTests/AppSettingsMock.swift index 6158e60eff..d318d3d86c 100644 --- a/DuckDuckGoTests/AppSettingsMock.swift +++ b/DuckDuckGoTests/AppSettingsMock.swift @@ -70,4 +70,6 @@ class AppSettingsMock: AppSettings { } var autoconsentEnabled = true + + var crashCollectionOptInStatus: CrashCollectionOptInStatus = .undetermined } diff --git a/DuckDuckGoTests/CrashCollectionExtensionTests.swift b/DuckDuckGoTests/CrashCollectionExtensionTests.swift deleted file mode 100644 index eb279cfb4d..0000000000 --- a/DuckDuckGoTests/CrashCollectionExtensionTests.swift +++ /dev/null @@ -1,92 +0,0 @@ -// -// CrashCollectionExtensionTests.swift -// DuckDuckGo -// -// Copyright © 2022 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import XCTest -@testable import DuckDuckGo -@testable import Crashes -import MetricKit -import OHHTTPStubs -import OHHTTPStubsSwift - -class CrashCollectionExtensionTests: XCTestCase { - - override func setUp() { - super.setUp() - clearUserDefaults() - } - - override func tearDown() { - super.tearDown() - HTTPStubs.removeAllStubs() - clearUserDefaults() - } - - func testSubsequentPixelsDontSendFirstFlag() { - // 2 pixels with no first parameter - CrashCollection.firstCrash = false - CrashCollection.start { - XCTAssertNil($0["first"]) - } - CrashCollection.collector.didReceive([ - MockPayload(mockCrashes: [ - MXCrashDiagnostic(), - MXCrashDiagnostic() - ]) - ]) - XCTAssertFalse(CrashCollection.firstCrash) - } - - func testFirstCrashFlagSent() { - // 2 pixels with first = true attached - XCTAssertTrue(CrashCollection.firstCrash) - CrashCollection.start { - XCTAssertNotNil($0["first"]) - } - CrashCollection.collector.didReceive([ - MockPayload(mockCrashes: [ - MXCrashDiagnostic(), - MXCrashDiagnostic() - ]) - ]) - XCTAssertFalse(CrashCollection.firstCrash) - } - - private func clearUserDefaults() { - UserDefaults().removeObject(forKey: CrashCollection.firstCrashKey) - } -} - -class MockPayload: MXDiagnosticPayload { - - var mockCrashes: [MXCrashDiagnostic]? - - init(mockCrashes: [MXCrashDiagnostic]?) { - self.mockCrashes = mockCrashes - super.init() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override var crashDiagnostics: [MXCrashDiagnostic]? { - return mockCrashes - } - -} diff --git a/LocalPackages/SyncUI/Package.swift b/LocalPackages/SyncUI/Package.swift index fb7f2e8a5b..72196aeadb 100644 --- a/LocalPackages/SyncUI/Package.swift +++ b/LocalPackages/SyncUI/Package.swift @@ -33,7 +33,8 @@ let package = Package( ], dependencies: [ .package(path: "../DuckUI"), - .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0"), + .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "3.0.0"), + .package(url: "https://github.com/duckduckgo/apple-toolbox.git", exact: "3.1.1"), ], targets: [ .target( diff --git a/LocalPackages/Waitlist/Package.swift b/LocalPackages/Waitlist/Package.swift index 5cbe098876..66c84034d6 100644 --- a/LocalPackages/Waitlist/Package.swift +++ b/LocalPackages/Waitlist/Package.swift @@ -15,7 +15,8 @@ let package = Package( targets: ["Waitlist", "WaitlistMocks"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0"), + .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "3.0.0"), + .package(url: "https://github.com/duckduckgo/apple-toolbox.git", exact: "3.1.1"), ], targets: [ .target(