From eb1dae79175896933354849b47a609b40927a9f4 Mon Sep 17 00:00:00 2001 From: Daniel Bernal Date: Wed, 17 Jan 2024 02:11:02 +0100 Subject: [PATCH 01/70] Daniel/subscriptions/3.manage subscription (#2336) Task/Issue URL: https://app.asana.com/0/72649045549333/1205054784245717/f Implement Subscription Management View Adds labels to the transaction progress box and reduces the opacity of the background view. --- Core/FeatureFlag.swift | 4 +- Core/UserDefaultsPropertyWrapper.swift | 2 +- DuckDuckGo.xcodeproj/project.pbxproj | 32 +++-- .../Views/PurchaseInProgressView.swift | 45 ------- DuckDuckGo/SettingsAboutView.swift | 9 +- DuckDuckGo/SettingsAppeareanceView.swift | 8 +- DuckDuckGo/SettingsCell.swift | 121 ++++++++---------- DuckDuckGo/SettingsCustomizeView.swift | 4 +- DuckDuckGo/SettingsDebugView.swift | 4 +- DuckDuckGo/SettingsGeneralView.swift | 4 +- DuckDuckGo/SettingsLoginsView.swift | 4 +- DuckDuckGo/SettingsMoreView.swift | 16 +-- DuckDuckGo/SettingsPrivacyView.swift | 27 ++-- DuckDuckGo/SettingsState.swift | 8 +- ...w.swift => SettingsSubscriptionView.swift} | 27 ++-- DuckDuckGo/SettingsSyncView.swift | 4 +- DuckDuckGo/SettingsView.swift | 2 +- DuckDuckGo/SettingsViewModel.swift | 24 ++-- .../WKUserContentController+Handler.swift | 2 - .../Subscription.storekit} | 0 .../Subscription/AccountManager.swift | 0 .../AccountKeychainStorage.swift | 0 .../AccountStorage/AccountStorage.swift | 0 .../AppStoreAccountManagementFlow.swift | 0 .../Flows/AppStore/AppStorePurchaseFlow.swift | 0 .../Flows/AppStore/AppStoreRestoreFlow.swift | 0 .../Subscription/Flows/PurchaseFlow.swift | 0 .../Subscription/Logging.swift | 0 .../Subscription/PurchaseManager.swift | 0 .../Subscription/Services/APIService.swift | 0 .../Subscription/Services/AuthService.swift | 0 .../Services/SubscriptionService.swift | 0 .../SubscriptionPurchaseEnvironment.swift | 0 .../Subscription/URL+Subscription.swift | 4 + ...scriptionPagesUseSubscriptionFeature.swift | 82 ++++++------ .../SubscriptionPagesUserScript.swift | 0 .../ViewModel/SubscriptionFlowViewModel.swift | 19 ++- .../SubscriptionSettingsViewModel.swift | 92 +++++++++++++ .../Views/HeadlessWebView.swift | 0 .../Views/PurchaseInProgressView.swift | 92 +++++++++++++ .../Views/SubscriptionFlowView.swift | 35 +++-- .../Views/SubscriptionSettingsView.swift | 99 ++++++++++++++ DuckDuckGo/UserText.swift | 46 +++++-- DuckDuckGo/en.lproj/Localizable.strings | 92 ++++++++++--- 44 files changed, 627 insertions(+), 281 deletions(-) delete mode 100644 DuckDuckGo/PrivacyPro/Views/PurchaseInProgressView.swift rename DuckDuckGo/{SettingsPrivacyProView.swift => SettingsSubscriptionView.swift} (73%) rename DuckDuckGo/{PrivacyPro => Subscription}/Extensions/WKUserContentController+Handler.swift (98%) rename DuckDuckGo/{PrivacyPro/PrivacyPro.storekit => Subscription/Subscription.storekit} (100%) rename DuckDuckGo/{PrivacyPro => Subscription}/Subscription/AccountManager.swift (100%) rename DuckDuckGo/{PrivacyPro => Subscription}/Subscription/AccountStorage/AccountKeychainStorage.swift (100%) rename DuckDuckGo/{PrivacyPro => Subscription}/Subscription/AccountStorage/AccountStorage.swift (100%) rename DuckDuckGo/{PrivacyPro => Subscription}/Subscription/Flows/AppStore/AppStoreAccountManagementFlow.swift (100%) rename DuckDuckGo/{PrivacyPro => Subscription}/Subscription/Flows/AppStore/AppStorePurchaseFlow.swift (100%) rename DuckDuckGo/{PrivacyPro => Subscription}/Subscription/Flows/AppStore/AppStoreRestoreFlow.swift (100%) rename DuckDuckGo/{PrivacyPro => Subscription}/Subscription/Flows/PurchaseFlow.swift (100%) rename DuckDuckGo/{PrivacyPro => Subscription}/Subscription/Logging.swift (100%) rename DuckDuckGo/{PrivacyPro => Subscription}/Subscription/PurchaseManager.swift (100%) rename DuckDuckGo/{PrivacyPro => Subscription}/Subscription/Services/APIService.swift (100%) rename DuckDuckGo/{PrivacyPro => Subscription}/Subscription/Services/AuthService.swift (100%) rename DuckDuckGo/{PrivacyPro => Subscription}/Subscription/Services/SubscriptionService.swift (100%) rename DuckDuckGo/{PrivacyPro => Subscription}/Subscription/SubscriptionPurchaseEnvironment.swift (100%) rename DuckDuckGo/{PrivacyPro => Subscription}/Subscription/URL+Subscription.swift (91%) rename DuckDuckGo/{PrivacyPro => Subscription}/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift (78%) rename DuckDuckGo/{PrivacyPro => Subscription}/UserScripts/SubscriptionPagesUserScript.swift (100%) rename DuckDuckGo/{PrivacyPro => Subscription}/ViewModel/SubscriptionFlowViewModel.swift (81%) create mode 100644 DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift rename DuckDuckGo/{PrivacyPro => Subscription}/Views/HeadlessWebView.swift (100%) create mode 100644 DuckDuckGo/Subscription/Views/PurchaseInProgressView.swift rename DuckDuckGo/{PrivacyPro => Subscription}/Views/SubscriptionFlowView.swift (61%) create mode 100644 DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift diff --git a/Core/FeatureFlag.swift b/Core/FeatureFlag.swift index 531e694283..f5c96b04ad 100644 --- a/Core/FeatureFlag.swift +++ b/Core/FeatureFlag.swift @@ -34,13 +34,13 @@ public enum FeatureFlag: String { case networkProtection case networkProtectionWaitlistAccess case networkProtectionWaitlistActive - case privacyPro + case subscription } extension FeatureFlag: FeatureFlagSourceProviding { public var source: FeatureFlagSource { switch self { - case .debugMenu, .appTrackingProtection, .privacyPro: + case .debugMenu, .appTrackingProtection, .subscription: return .internalOnly case .sync: return .remoteReleasable(.subfeature(SyncSubfeature.level0ShowSync)) diff --git a/Core/UserDefaultsPropertyWrapper.swift b/Core/UserDefaultsPropertyWrapper.swift index 192bb83d82..850a3a9738 100644 --- a/Core/UserDefaultsPropertyWrapper.swift +++ b/Core/UserDefaultsPropertyWrapper.swift @@ -122,7 +122,7 @@ public struct UserDefaultsWrapper { case privacyConfigCustomURL = "com.duckduckgo.ios.privacyConfigCustomURL" - case privacyProHasActiveSubscription = "com.duckduckgo.ios.privacyPro.hasActiveSubscription" + case subscriptionIsActive = "com.duckduckgo.ios.subscruption.isActive" } private let key: Key diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 27cfa043c0..7b0e2e27d2 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -768,8 +768,9 @@ CBEFB9142AE0844700DEDE7B /* CriticalAlerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEFB9102ADFFE7900DEDE7B /* CriticalAlerts.swift */; }; CBFCB30E2B2CD47800253E9E /* ConfigurationURLDebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBFCB30D2B2CD47800253E9E /* ConfigurationURLDebugViewController.swift */; }; D63657192A7BAE7C001AF19D /* EmailManagerRequestDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63657182A7BAE7C001AF19D /* EmailManagerRequestDelegate.swift */; }; + D652498E2B515A6A0056B0DE /* SubscriptionSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D652498D2B515A6A0056B0DE /* SubscriptionSettingsViewModel.swift */; }; D664C7B62B289AA200CBFA76 /* SubscriptionFlowViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664C7942B289AA000CBFA76 /* SubscriptionFlowViewModel.swift */; }; - D664C7B72B289AA200CBFA76 /* PrivacyPro.storekit in Resources */ = {isa = PBXBuildFile; fileRef = D664C7952B289AA000CBFA76 /* PrivacyPro.storekit */; }; + D664C7B72B289AA200CBFA76 /* Subscription.storekit in Resources */ = {isa = PBXBuildFile; fileRef = D664C7952B289AA000CBFA76 /* Subscription.storekit */; }; D664C7B92B289AA200CBFA76 /* WKUserContentController+Handler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664C7982B289AA000CBFA76 /* WKUserContentController+Handler.swift */; }; D664C7C72B289AA200CBFA76 /* PurchaseInProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664C7AD2B289AA000CBFA76 /* PurchaseInProgressView.swift */; }; D664C7C82B289AA200CBFA76 /* SubscriptionFlowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664C7AE2B289AA000CBFA76 /* SubscriptionFlowView.swift */; }; @@ -777,7 +778,7 @@ D664C7CC2B289AA200CBFA76 /* SubscriptionPagesUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664C7B32B289AA000CBFA76 /* SubscriptionPagesUserScript.swift */; }; D664C7CE2B289AA200CBFA76 /* SubscriptionPagesUseSubscriptionFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664C7B52B289AA000CBFA76 /* SubscriptionPagesUseSubscriptionFeature.swift */; }; D664C7DD2B28A02800CBFA76 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D664C7DC2B28A02800CBFA76 /* StoreKit.framework */; }; - D69FBF762B28BE3600B505F1 /* SettingsPrivacyProView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69FBF752B28BE3600B505F1 /* SettingsPrivacyProView.swift */; }; + D69FBF762B28BE3600B505F1 /* SettingsSubscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69FBF752B28BE3600B505F1 /* SettingsSubscriptionView.swift */; }; D6D12C9F2B291CA90054390C /* URL+Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C8B2B291CA90054390C /* URL+Subscription.swift */; }; D6D12CA02B291CA90054390C /* SubscriptionPurchaseEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C8C2B291CA90054390C /* SubscriptionPurchaseEnvironment.swift */; }; D6D12CA12B291CA90054390C /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C8D2B291CA90054390C /* Logging.swift */; }; @@ -809,6 +810,7 @@ D6E83C662B23936F006C8AFB /* SettingsDebugView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E83C652B23936F006C8AFB /* SettingsDebugView.swift */; }; D6E83C682B23B6A3006C8AFB /* FontSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E83C672B23B6A3006C8AFB /* FontSettings.swift */; }; D6F93E3C2B4FFA97004C268D /* SubscriptionDebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F93E3B2B4FFA97004C268D /* SubscriptionDebugViewController.swift */; }; + D6F93E3E2B50A8A0004C268D /* SubscriptionSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6F93E3D2B50A8A0004C268D /* SubscriptionSettingsView.swift */; }; EA39B7E2268A1A35000C62CD /* privacy-reference-tests in Resources */ = {isa = PBXBuildFile; fileRef = EA39B7E1268A1A35000C62CD /* privacy-reference-tests */; }; EAB19EDA268963510015D3EA /* DomainMatchingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB19ED9268963510015D3EA /* DomainMatchingTests.swift */; }; EE0153E12A6EABE0002A8B26 /* NetworkProtectionConvenienceInitialisers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0153E02A6EABE0002A8B26 /* NetworkProtectionConvenienceInitialisers.swift */; }; @@ -2407,8 +2409,9 @@ CBF14FC627970C8A001D94D0 /* HomeMessageCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeMessageCollectionViewCell.swift; sourceTree = ""; }; CBFCB30D2B2CD47800253E9E /* ConfigurationURLDebugViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationURLDebugViewController.swift; sourceTree = ""; }; D63657182A7BAE7C001AF19D /* EmailManagerRequestDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmailManagerRequestDelegate.swift; sourceTree = ""; }; + D652498D2B515A6A0056B0DE /* SubscriptionSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionSettingsViewModel.swift; sourceTree = ""; }; D664C7942B289AA000CBFA76 /* SubscriptionFlowViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionFlowViewModel.swift; sourceTree = ""; }; - D664C7952B289AA000CBFA76 /* PrivacyPro.storekit */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = PrivacyPro.storekit; sourceTree = ""; }; + D664C7952B289AA000CBFA76 /* Subscription.storekit */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Subscription.storekit; sourceTree = ""; }; D664C7982B289AA000CBFA76 /* WKUserContentController+Handler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "WKUserContentController+Handler.swift"; sourceTree = ""; }; D664C7AD2B289AA000CBFA76 /* PurchaseInProgressView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PurchaseInProgressView.swift; sourceTree = ""; }; D664C7AE2B289AA000CBFA76 /* SubscriptionFlowView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionFlowView.swift; sourceTree = ""; }; @@ -2416,7 +2419,7 @@ D664C7B32B289AA000CBFA76 /* SubscriptionPagesUserScript.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionPagesUserScript.swift; sourceTree = ""; }; D664C7B52B289AA000CBFA76 /* SubscriptionPagesUseSubscriptionFeature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionPagesUseSubscriptionFeature.swift; sourceTree = ""; }; D664C7DC2B28A02800CBFA76 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; - D69FBF752B28BE3600B505F1 /* SettingsPrivacyProView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPrivacyProView.swift; sourceTree = ""; }; + D69FBF752B28BE3600B505F1 /* SettingsSubscriptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionView.swift; sourceTree = ""; }; D6D12C8B2B291CA90054390C /* URL+Subscription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URL+Subscription.swift"; sourceTree = ""; }; D6D12C8C2B291CA90054390C /* SubscriptionPurchaseEnvironment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionPurchaseEnvironment.swift; sourceTree = ""; }; D6D12C8D2B291CA90054390C /* Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = ""; }; @@ -2448,6 +2451,7 @@ D6E83C652B23936F006C8AFB /* SettingsDebugView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsDebugView.swift; sourceTree = ""; }; D6E83C672B23B6A3006C8AFB /* FontSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontSettings.swift; sourceTree = ""; }; D6F93E3B2B4FFA97004C268D /* SubscriptionDebugViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionDebugViewController.swift; sourceTree = ""; }; + D6F93E3D2B50A8A0004C268D /* SubscriptionSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionSettingsView.swift; sourceTree = ""; }; EA39B7E1268A1A35000C62CD /* privacy-reference-tests */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "privacy-reference-tests"; path = "submodules/privacy-reference-tests"; sourceTree = SOURCE_ROOT; }; EAB19ED9268963510015D3EA /* DomainMatchingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainMatchingTests.swift; sourceTree = ""; }; EE0153E02A6EABE0002A8B26 /* NetworkProtectionConvenienceInitialisers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionConvenienceInitialisers.swift; sourceTree = ""; }; @@ -3766,7 +3770,7 @@ 85AE668C20971FCA0014CF04 /* Notifications */, F1C4A70C1E5771F800A6CA1B /* OmniBar */, F1AE54DB1F0425BB00D9A700 /* Privacy */, - D664C7922B289AA000CBFA76 /* PrivacyPro */, + D664C7922B289AA000CBFA76 /* Subscription */, F1DF09502B039E6E008CC908 /* PrivacyDashboard */, 02ECEC602A965074009F0654 /* PrivacyInfo.xcprivacy */, C1B7B51D28941F160098FD6A /* RemoteMessaging */, @@ -4503,23 +4507,24 @@ name = Resources; sourceTree = ""; }; - D664C7922B289AA000CBFA76 /* PrivacyPro */ = { + D664C7922B289AA000CBFA76 /* Subscription */ = { isa = PBXGroup; children = ( - D664C7952B289AA000CBFA76 /* PrivacyPro.storekit */, + D664C7952B289AA000CBFA76 /* Subscription.storekit */, D664C7932B289AA000CBFA76 /* ViewModel */, D664C7AC2B289AA000CBFA76 /* Views */, D664C7B02B289AA000CBFA76 /* UserScripts */, D664C7962B289AA000CBFA76 /* Extensions */, D6D12C8A2B291CA90054390C /* Subscription */, ); - path = PrivacyPro; + path = Subscription; sourceTree = ""; }; D664C7932B289AA000CBFA76 /* ViewModel */ = { isa = PBXGroup; children = ( D664C7942B289AA000CBFA76 /* SubscriptionFlowViewModel.swift */, + D652498D2B515A6A0056B0DE /* SubscriptionSettingsViewModel.swift */, ); path = ViewModel; sourceTree = ""; @@ -4535,9 +4540,10 @@ D664C7AC2B289AA000CBFA76 /* Views */ = { isa = PBXGroup; children = ( + D664C7AF2B289AA000CBFA76 /* HeadlessWebView.swift */, D664C7AD2B289AA000CBFA76 /* PurchaseInProgressView.swift */, D664C7AE2B289AA000CBFA76 /* SubscriptionFlowView.swift */, - D664C7AF2B289AA000CBFA76 /* HeadlessWebView.swift */, + D6F93E3D2B50A8A0004C268D /* SubscriptionSettingsView.swift */, ); path = Views; sourceTree = ""; @@ -4612,7 +4618,7 @@ D6E83C3C2B1F2C03006C8AFB /* SettingsLoginsView.swift */, D6E83C402B1FC285006C8AFB /* SettingsAppeareanceView.swift */, D6E83C592B2213ED006C8AFB /* SettingsPrivacyView.swift */, - D69FBF752B28BE3600B505F1 /* SettingsPrivacyProView.swift */, + D69FBF752B28BE3600B505F1 /* SettingsSubscriptionView.swift */, D6E83C5D2B224676006C8AFB /* SettingsCustomizeView.swift */, D6E83C612B23298B006C8AFB /* SettingsMoreView.swift */, D6E83C632B238432006C8AFB /* SettingsAboutView.swift */, @@ -6081,7 +6087,7 @@ 1EE411FD2858B9300003FE64 /* dark-trackers-2.json in Resources */, AA4D6ABC23DE4D15007E8790 /* AppIconYellow60x60@3x.png in Resources */, 98D98A9B25ED954100D8E3DF /* BrowsingMenuButton.xib in Resources */, - D664C7B72B289AA200CBFA76 /* PrivacyPro.storekit in Resources */, + D664C7B72B289AA200CBFA76 /* Subscription.storekit in Resources */, AA4D6AA823DE4CC4007E8790 /* AppIconBlue40x40@2x.png in Resources */, AA4D6AE723DE4D33007E8790 /* AppIconGreen29x29@2x.png in Resources */, 1EE412002858B9300003FE64 /* dark-shield-dot.json in Resources */, @@ -6563,7 +6569,7 @@ 1E908BF329827C480008C8F3 /* AutoconsentManagement.swift in Sources */, CB9B8739278C8E72001F4906 /* WidgetEducationViewController.swift in Sources */, F4D9C4FA25117A0F00814B71 /* HomeMessageStorage.swift in Sources */, - D69FBF762B28BE3600B505F1 /* SettingsPrivacyProView.swift in Sources */, + D69FBF762B28BE3600B505F1 /* SettingsSubscriptionView.swift in Sources */, D664C7CC2B289AA200CBFA76 /* SubscriptionPagesUserScript.swift in Sources */, AA3D854523D9942200788410 /* AppIconSettingsViewController.swift in Sources */, 85C297042476C1FD0063A335 /* DaxDialogsSettings.swift in Sources */, @@ -6606,6 +6612,7 @@ 98AA92B32456FBE100ED4B9E /* SearchFieldContainerView.swift in Sources */, 3157B43827F4C8490042D3D7 /* FaviconsHelper.swift in Sources */, 85F200042216F5D8006BB258 /* FindInPageView.swift in Sources */, + D652498E2B515A6A0056B0DE /* SubscriptionSettingsViewModel.swift in Sources */, 8548D95E25262B1B005AAE49 /* ViewHighlighter.swift in Sources */, F4D7221026F29A70007D6193 /* BookmarkDetailsCell.swift in Sources */, D6D12CA22B291CA90054390C /* AccountManager.swift in Sources */, @@ -6618,6 +6625,7 @@ C13B32D22A0E750700A59236 /* AutofillSettingStatus.swift in Sources */, D6D12CA52B291CAA0054390C /* AppStorePurchaseFlow.swift in Sources */, F4F6DFB426E6B63700ED7E12 /* BookmarkFolderCell.swift in Sources */, + D6F93E3E2B50A8A0004C268D /* SubscriptionSettingsView.swift in Sources */, 851B12CC22369931004781BC /* AtbAndVariantCleanup.swift in Sources */, 85F2FFCF2211F8E5006BB258 /* TabSwitcherViewController+KeyCommands.swift in Sources */, 3157B43327F497E90042D3D7 /* SaveLoginView.swift in Sources */, diff --git a/DuckDuckGo/PrivacyPro/Views/PurchaseInProgressView.swift b/DuckDuckGo/PrivacyPro/Views/PurchaseInProgressView.swift deleted file mode 100644 index 796ae24342..0000000000 --- a/DuckDuckGo/PrivacyPro/Views/PurchaseInProgressView.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// PurchaseInProgressView.swift -// DuckDuckGo -// -// Copyright © 2023 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -import SwiftUI - -struct PurchaseInProgressView: View { - @Environment(\.colorScheme) var colorScheme - - // TODO: Update colors and design - var body: some View { - ZStack { - Color(colorScheme == .dark ? .black : .white) - .opacity(0.0) - .edgesIgnoringSafeArea(.all) - .disabled(true) - - ZStack { - RoundedRectangle(cornerRadius: 12) - .fill(colorScheme == .dark ? .black : .white) - .frame(width: 120, height: 120) - .shadow(color: colorScheme == .dark ? .black : .gray30, radius: 10) - - SwiftUI.ProgressView() - .scaleEffect(2) - .progressViewStyle(CircularProgressViewStyle(tint: Color(colorScheme == .dark ? .gray30 : .gray70))) - } - - } - } -} diff --git a/DuckDuckGo/SettingsAboutView.swift b/DuckDuckGo/SettingsAboutView.swift index 8e5b20eb6c..c0fd44206d 100644 --- a/DuckDuckGo/SettingsAboutView.swift +++ b/DuckDuckGo/SettingsAboutView.swift @@ -29,16 +29,15 @@ struct SettingsAboutView: View { SettingsCellView(label: UserText.settingsAboutDDG, action: { viewModel.presentLegacyView(.about) }, - asLink: true, - disclosureIndicator: true) + disclosureIndicator: true, + isButton: true) SettingsCellView(label: UserText.settingsVersion, - accesory: .rightDetail(viewModel.state.version), - asLink: true) + accesory: .rightDetail(viewModel.state.version)) SettingsCellView(label: UserText.settingsFeedback, action: { viewModel.presentLegacyView(.feedback) }, - asLink: true) + isButton: true) } diff --git a/DuckDuckGo/SettingsAppeareanceView.swift b/DuckDuckGo/SettingsAppeareanceView.swift index 4a2f374f4c..d04bd1a08a 100644 --- a/DuckDuckGo/SettingsAppeareanceView.swift +++ b/DuckDuckGo/SettingsAppeareanceView.swift @@ -35,8 +35,8 @@ struct SettingsAppeareanceView: View { SettingsCellView(label: UserText.settingsIcon, action: { viewModel.presentLegacyView(.appIcon ) }, accesory: .image(image), - asLink: true, - disclosureIndicator: true) + disclosureIndicator: true, + isButton: true) SettingsPickerCellView(label: UserText.settingsFirebutton, options: FireButtonAnimationType.allCases, @@ -46,8 +46,8 @@ struct SettingsAppeareanceView: View { SettingsCellView(label: UserText.settingsText, action: { viewModel.presentLegacyView(.textSize) }, accesory: .rightDetail("\(viewModel.state.textSize.size)%"), - asLink: true, - disclosureIndicator: true) + disclosureIndicator: true, + isButton: true) } if viewModel.state.addressbar.enabled { diff --git a/DuckDuckGo/SettingsCell.swift b/DuckDuckGo/SettingsCell.swift index 0e9aac945e..c556659215 100644 --- a/DuckDuckGo/SettingsCell.swift +++ b/DuckDuckGo/SettingsCell.swift @@ -27,6 +27,7 @@ struct SettingsCellComponents { .foregroundColor(Color(UIColor.tertiaryLabel)) } } + /// Encapsulates a View representing a Cell with different configurations struct SettingsCellView: View, Identifiable { @@ -44,9 +45,9 @@ struct SettingsCellView: View, Identifiable { var action: () -> Void = {} var enabled: Bool = true var accesory: Accessory - var asLink: Bool var disclosureIndicator: Bool var id: UUID = UUID() + var isButton: Bool /// Initializes a `SettingsCellView` with the specified label and accesory. /// @@ -58,17 +59,17 @@ struct SettingsCellView: View, Identifiable { /// - action: The closure to execute when the view is tapped. (If not embedded in a NavigationLink) /// - accesory: The type of cell to display. Excludes the custom cell type. /// - enabled: A Boolean value that determines whether the cell is enabled. - /// - asLink: Wraps the view inside a Button. Used for views not wrapped in a NavigationLink /// - disclosureIndicator: Forces Adds a disclosure indicator on the right (chevron) - init(label: String, subtitle: String? = nil, image: Image? = nil, action: @escaping () -> Void = {}, accesory: Accessory = .none, enabled: Bool = true, asLink: Bool = false, disclosureIndicator: Bool = false) { + /// - isButton: Disables the tap actions on the cell if true + init(label: String, subtitle: String? = nil, image: Image? = nil, action: @escaping () -> Void = {}, accesory: Accessory = .none, enabled: Bool = true, disclosureIndicator: Bool = false, isButton: Bool = false) { self.label = label self.subtitle = subtitle self.image = image self.action = action self.enabled = enabled self.accesory = accesory - self.asLink = asLink self.disclosureIndicator = disclosureIndicator + self.isButton = isButton } /// Initializes a `SettingsCellView` for custom content. @@ -84,21 +85,24 @@ struct SettingsCellView: View, Identifiable { self.action = action self.enabled = enabled self.accesory = .custom(customView()) - self.asLink = false self.disclosureIndicator = false + self.isButton = false } var body: some View { - if asLink { - Button(action: action) { + Group { + if isButton { + Button(action: action) { + cellContent + .disabled(!enabled) + } + .buttonStyle(PlainButtonStyle()) + .contentShape(Rectangle()) + } else { cellContent - .disabled(!enabled) } - .buttonStyle(PlainButtonStyle()) - .contentShape(Rectangle()) - } else { - cellContent - } + }.frame(maxWidth: .infinity) + } private var cellContent: some View { @@ -233,35 +237,42 @@ struct SettingsPickerCellView: View { var content: Content var action: () -> Void - var asLink: Bool var disclosureIndicator: Bool + var isButton: Bool /// Initializes a `SettingsCustomCell`. /// - Parameters: /// - content: A SwiftUI View to be displayed in the cell. /// - action: The closure to execute when the view is tapped. - /// - asLink: A Boolean value that determines if the cell behaves like a link. - /// - disclosureIndicator: A Boolean value that determines if the cell shows a disclosure indicator. - init(@ViewBuilder content: () -> Content, action: @escaping () -> Void = {}, asLink: Bool = false, disclosureIndicator: Bool = false) { + /// - disclosureIndicator: A Boolean value that determines if the cell shows a disclosure + /// indicator. + /// - isButton: Disables the tap actions on the cell if true + init(@ViewBuilder content: () -> Content, action: @escaping () -> Void = {}, disclosureIndicator: Bool = false, isButton: Bool = false) { self.content = content() self.action = action - self.asLink = asLink self.disclosureIndicator = disclosureIndicator + self.isButton = isButton } var body: some View { - if asLink { - Button(action: action) { - cellContent + if isButton { + ZStack { + Button(action: action) { + cellContent + } + .buttonStyle(PlainButtonStyle()) + .contentShape(Rectangle()) + .frame(maxWidth: .infinity) + .onTapGesture { + action() // We need this to make sute tap target is expanded to frame + } } - .buttonStyle(PlainButtonStyle()) - .contentShape(Rectangle()) + .frame(maxWidth: .infinity) } else { cellContent } } - private var cellContent: some View { HStack { content @@ -288,94 +299,72 @@ struct SettingsCellView_Previews: PreviewProvider { static var previews: some View { Group { List { - SettingsCellView(label: "Nulla commodo augue nec", - asLink: true, + SettingsCellView(label: "Cell with disclosure", disclosureIndicator: true) .previewLayout(.sizeThatFits) - SettingsCellView(label: "Nulla commodo augue nec", + SettingsCellView(label: "Multi-line Cell with disclosure \nLine 2\nLine 3", subtitle: "Curabitur erat massa, cursus sed velit", - asLink: true, disclosureIndicator: true) .previewLayout(.sizeThatFits) - SettingsCellView(label: "Maecenas ac purus", + SettingsCellView(label: "Image cell with disclosure ", accesory: .image(Image(systemName: "person.circle")), - asLink: true, disclosureIndicator: true) .previewLayout(.sizeThatFits) - SettingsCellView(label: "Maecenas ac purus", - subtitle: "Curabitur erat massa", + SettingsCellView(label: "Subtitle image cell with disclosure", + subtitle: "This is the subtitle", accesory: .image(Image(systemName: "person.circle")), - asLink: true, disclosureIndicator: true) .previewLayout(.sizeThatFits) - SettingsCellView(label: "Curabitur erat", - accesory: .rightDetail("Curabi"), - asLink: true, - disclosureIndicator: true) - .previewLayout(.sizeThatFits) - - SettingsCellView(label: "Curabitur erat", - subtitle: "Nulla commodo augue", - accesory: .rightDetail("Aagittis"), - asLink: true, + SettingsCellView(label: "Right Detail cell with disclosure", + accesory: .rightDetail("Detail"), disclosureIndicator: true) .previewLayout(.sizeThatFits) - SettingsCellView(label: "Proin tempor urna", - accesory: .toggle(isOn: .constant(true)), - asLink: false, - disclosureIndicator: false) + SettingsCellView(label: "Switch Cell", + accesory: .toggle(isOn: .constant(true))) .previewLayout(.sizeThatFits) - SettingsCellView(label: "Proin tempor urna", - subtitle: "Fusce elementum quis", - accesory: .toggle(isOn: .constant(true)), - asLink: false, - disclosureIndicator: false) + SettingsCellView(label: "Switch Cell", + subtitle: "Subtitle goes here", + accesory: .toggle(isOn: .constant(true))) .previewLayout(.sizeThatFits) + @State var selectedOption: SampleOption = .optionOne SettingsPickerCellView(label: "Proin tempor urna", options: SampleOption.allCases, selectedOption: $selectedOption) .previewLayout(.sizeThatFits) - SettingsCellView(label: "Proin tempor urna", - subtitle: "Fusce elementum quis", - accesory: .toggle(isOn: .constant(true)), - asLink: false, - disclosureIndicator: false) - .previewLayout(.sizeThatFits) - let cellContent: () -> some View = { HStack(spacing: 15) { - Image(systemName: "hand.wave") + Image(systemName: "bird.circle") .foregroundColor(.orange) .imageScale(.large) - Image(systemName: "hand.wave") + Image(systemName: "bird.circle") .foregroundColor(.orange) .imageScale(.medium) Spacer() - VStack(alignment: .leading) { + VStack(alignment: .center) { Text("LOREM IPSUM") .font(.headline) } Spacer() - Image(systemName: "hand.wave") + Image(systemName: "bird.circle") .foregroundColor(.orange) .imageScale(.medium) - Image(systemName: "hand.wave") + Image(systemName: "bird.circle") .foregroundColor(.orange) .imageScale(.large) } } // For some unknown reason, this breaks on CI, but works normally // Perhaps an XCODE version issue? - // SettingsCustomCell(content: cellContent) - // .previewLayout(.sizeThatFits) + SettingsCustomCell(content: cellContent) + .previewLayout(.sizeThatFits) } diff --git a/DuckDuckGo/SettingsCustomizeView.swift b/DuckDuckGo/SettingsCustomizeView.swift index 699398f20b..034b268090 100644 --- a/DuckDuckGo/SettingsCustomizeView.swift +++ b/DuckDuckGo/SettingsCustomizeView.swift @@ -31,8 +31,8 @@ struct SettingsCustomizeView: View { SettingsCellView(label: UserText.settingsKeyboard, action: { viewModel.presentLegacyView(.keyboard) }, - asLink: true, - disclosureIndicator: true) + disclosureIndicator: true, + isButton: true) SettingsCellView(label: UserText.settingsAutocomplete, accesory: .toggle(isOn: viewModel.autocompleteBinding)) diff --git a/DuckDuckGo/SettingsDebugView.swift b/DuckDuckGo/SettingsDebugView.swift index 53a325b988..39615314c2 100644 --- a/DuckDuckGo/SettingsDebugView.swift +++ b/DuckDuckGo/SettingsDebugView.swift @@ -29,8 +29,8 @@ struct SettingsDebugView: View { SettingsCellView(label: "All debug options", action: { viewModel.presentLegacyView(.debug) }, - asLink: true, - disclosureIndicator: true) + disclosureIndicator: true, + isButton: true) } diff --git a/DuckDuckGo/SettingsGeneralView.swift b/DuckDuckGo/SettingsGeneralView.swift index 66aae4e741..db6de8cc18 100644 --- a/DuckDuckGo/SettingsGeneralView.swift +++ b/DuckDuckGo/SettingsGeneralView.swift @@ -28,11 +28,11 @@ struct SettingsGeneralView: View { Section { SettingsCellView(label: UserText.settingsSetDefault, action: { viewModel.setAsDefaultBrowser() }, - asLink: true) + isButton: true) SettingsCellView(label: UserText.settingsAddToDock, action: { viewModel.presentLegacyView(.addToDock) }, - asLink: true) + isButton: true) NavigationLink(destination: WidgetEducationView()) { SettingsCellView(label: UserText.settingsAddWidget) diff --git a/DuckDuckGo/SettingsLoginsView.swift b/DuckDuckGo/SettingsLoginsView.swift index cae2f09076..a24a3bfaaf 100644 --- a/DuckDuckGo/SettingsLoginsView.swift +++ b/DuckDuckGo/SettingsLoginsView.swift @@ -32,8 +32,8 @@ struct SettingsLoginsView: View { Section { SettingsCellView(label: UserText.settingsLogins, action: { viewModel.presentLegacyView(.logins) }, - asLink: true, - disclosureIndicator: true) + disclosureIndicator: true, + isButton: true) } } diff --git a/DuckDuckGo/SettingsMoreView.swift b/DuckDuckGo/SettingsMoreView.swift index 0f983ddaed..d3a770fa19 100644 --- a/DuckDuckGo/SettingsMoreView.swift +++ b/DuckDuckGo/SettingsMoreView.swift @@ -31,28 +31,28 @@ struct SettingsMoreView: View { SettingsCellView(label: UserText.settingsEmailProtection, subtitle: UserText.settingsEmailProtectionDescription, action: { viewModel.openEmailProtection() }, - asLink: true, - disclosureIndicator: true) + disclosureIndicator: true, + isButton: true) SettingsCellView(label: UserText.macBrowserTitle, subtitle: UserText.macWaitlistBrowsePrivately, action: { viewModel.presentLegacyView(.macApp) }, - asLink: true, - disclosureIndicator: true) + disclosureIndicator: true, + isButton: true) SettingsCellView(label: UserText.windowsWaitlistTitle, subtitle: UserText.windowsWaitlistBrowsePrivately, action: { viewModel.presentLegacyView(.windowsApp) }, - asLink: true, - disclosureIndicator: true) + disclosureIndicator: true, + isButton: true) #if NETWORK_PROTECTION if viewModel.state.networkProtection.enabled { SettingsCellView(label: UserText.netPNavTitle, subtitle: viewModel.state.networkProtection.status != "" ? viewModel.state.networkProtection.status : nil, action: { viewModel.presentLegacyView(.netP) }, - asLink: true, - disclosureIndicator: true) + disclosureIndicator: true, + isButton: true) } #endif } diff --git a/DuckDuckGo/SettingsPrivacyView.swift b/DuckDuckGo/SettingsPrivacyView.swift index df869e9bd8..deee886df0 100644 --- a/DuckDuckGo/SettingsPrivacyView.swift +++ b/DuckDuckGo/SettingsPrivacyView.swift @@ -33,36 +33,37 @@ struct SettingsPrivacyView: View { accesory: .rightDetail(viewModel.state.sendDoNotSell ? UserText.doNotSellEnabled : UserText.doNotSellDisabled), - asLink: true, - disclosureIndicator: true) + disclosureIndicator: true, + isButton: true) SettingsCellView(label: UserText.settingsCookiePopups, action: { viewModel.presentLegacyView(.autoconsent) }, accesory: .rightDetail(viewModel.state.autoconsentEnabled ? UserText.autoconsentEnabled : UserText.autoconsentDisabled), - asLink: true, - disclosureIndicator: true) + disclosureIndicator: true, + isButton: true) SettingsCellView(label: UserText.settingsUnprotectedSites, action: { viewModel.presentLegacyView(.unprotectedSites) }, - asLink: true, - disclosureIndicator: true) + disclosureIndicator: true, + isButton: true) SettingsCellView(label: UserText.settingsFireproofSites, action: { viewModel.presentLegacyView(.fireproofSites) }, - asLink: true, - disclosureIndicator: true) - + disclosureIndicator: true, + isButton: true) + SettingsCellView(label: UserText.settingsClearData, action: { viewModel.presentLegacyView(.autoclearData) }, accesory: .rightDetail(viewModel.state.autoclearDataEnabled ? UserText.autoClearAccessoryOn : UserText.autoClearAccessoryOff), - asLink: true, - disclosureIndicator: true) - - SettingsCellView(label: UserText.settingsAutolock, accesory: .toggle(isOn: viewModel.applicationLockBinding)) + disclosureIndicator: true, + isButton: true) + + SettingsCellView(label: UserText.settingsAutolock, + accesory: .toggle(isOn: viewModel.applicationLockBinding)) } } diff --git a/DuckDuckGo/SettingsState.swift b/DuckDuckGo/SettingsState.swift index 95fd708149..9d9ff181a5 100644 --- a/DuckDuckGo/SettingsState.swift +++ b/DuckDuckGo/SettingsState.swift @@ -21,7 +21,7 @@ import BrowserServicesKit struct SettingsState { - enum PrivacyProSubscriptionStatus { + enum SubscriptionStatus { case active, inactive, unknown } @@ -40,7 +40,7 @@ struct SettingsState { var status: String } - struct PrivacyPro { + struct Subscription { var enabled: Bool var canPurchase: Bool var hasActiveSubscription: Bool @@ -85,7 +85,7 @@ struct SettingsState { var networkProtection: NetworkProtection // Subscriptions Properties - var privacyPro: PrivacyPro + var subscription: Subscription // Sync Propertiers var sync: SyncSettings @@ -111,7 +111,7 @@ struct SettingsState { speechRecognitionEnabled: false, loginsEnabled: false, networkProtection: NetworkProtection(enabled: false, status: ""), - privacyPro: PrivacyPro(enabled: false, canPurchase: false, + subscription: Subscription(enabled: false, canPurchase: false, hasActiveSubscription: false), sync: SyncSettings(enabled: false, title: "") ) diff --git a/DuckDuckGo/SettingsPrivacyProView.swift b/DuckDuckGo/SettingsSubscriptionView.swift similarity index 73% rename from DuckDuckGo/SettingsPrivacyProView.swift rename to DuckDuckGo/SettingsSubscriptionView.swift index 2c439d92a9..53069a89c2 100644 --- a/DuckDuckGo/SettingsPrivacyProView.swift +++ b/DuckDuckGo/SettingsSubscriptionView.swift @@ -1,5 +1,5 @@ // -// SettingsPrivacyProView.swift +// SettingsSubscriptionView.swift // DuckDuckGo // // Copyright © 2023 DuckDuckGo. All rights reserved. @@ -22,11 +22,11 @@ import UIKit #if SUBSCRIPTION @available(iOS 15.0, *) -struct SettingsPrivacyProView: View { +struct SettingsSubscriptionView: View { @EnvironmentObject var viewModel: SettingsViewModel - private var privacyProDescriptionView: some View { + private var subscriptionDescriptionView: some View { VStack(alignment: .leading) { Text(UserText.settingsPProSubscribe).daxBodyRegular() Group { @@ -51,7 +51,7 @@ struct SettingsPrivacyProView: View { private var purchaseSubscriptionView: some View { return Group { - SettingsCustomCell(content: { privacyProDescriptionView }) + SettingsCustomCell(content: { subscriptionDescriptionView }) NavigationLink(destination: SubscriptionFlowView(viewModel: SubscriptionFlowViewModel())) { SettingsCustomCell(content: { learnMoreView }) } @@ -63,13 +63,18 @@ struct SettingsPrivacyProView: View { SettingsCellView(label: UserText.settingsPProVPNTitle, subtitle: viewModel.state.networkProtection.status != "" ? viewModel.state.networkProtection.status : nil, action: { viewModel.presentLegacyView(.netP) }, - asLink: true, - disclosureIndicator: true) + disclosureIndicator: true, + isButton: true) - SettingsCellView(label: UserText.settingsPProDBPTitle, subtitle: UserText.settingsPProDBPSubTitle) - SettingsCellView(label: UserText.settingsPProITRTitle, subtitle: UserText.settingsPProITRSubTitle) + NavigationLink(destination: Text("Data Broker Protection")) { + SettingsCellView(label: UserText.settingsPProDBPTitle, subtitle: UserText.settingsPProDBPSubTitle) + } - NavigationLink(destination: SubscriptionFlowView(viewModel: SubscriptionFlowViewModel())) { + NavigationLink(destination: Text("Identity Theft Restoration")) { + SettingsCellView(label: UserText.settingsPProITRTitle, subtitle: UserText.settingsPProITRSubTitle) + } + + NavigationLink(destination: SubscriptionSettingsView(viewModel: SubscriptionSettingsViewModel())) { SettingsCustomCell(content: { manageSubscriptionView }) } } @@ -77,9 +82,9 @@ struct SettingsPrivacyProView: View { var body: some View { - if viewModel.state.privacyPro.enabled { + if viewModel.state.subscription.enabled { Section(header: Text(UserText.settingsPProSection)) { - if viewModel.state.privacyPro.hasActiveSubscription { + if viewModel.state.subscription.hasActiveSubscription { subscriptionDetailsView } else { purchaseSubscriptionView diff --git a/DuckDuckGo/SettingsSyncView.swift b/DuckDuckGo/SettingsSyncView.swift index bda8d96bcd..0cd75de4fc 100644 --- a/DuckDuckGo/SettingsSyncView.swift +++ b/DuckDuckGo/SettingsSyncView.swift @@ -36,8 +36,8 @@ struct SettingsSyncView: View { Section { SettingsCellView(label: SyncUI.UserText.syncTitle, action: { viewModel.presentLegacyView(.sync) }, - asLink: true, - disclosureIndicator: true) + disclosureIndicator: true, + isButton: true) } } diff --git a/DuckDuckGo/SettingsView.swift b/DuckDuckGo/SettingsView.swift index d5372591a4..0bfd697ef4 100644 --- a/DuckDuckGo/SettingsView.swift +++ b/DuckDuckGo/SettingsView.swift @@ -35,7 +35,7 @@ struct SettingsView: View { SettingsPrivacyView() #if SUBSCRIPTION if #available(iOS 15, *) { - SettingsPrivacyProView() + SettingsSubscriptionView() } #endif SettingsCustomizeView() diff --git a/DuckDuckGo/SettingsViewModel.swift b/DuckDuckGo/SettingsViewModel.swift index 41a4b964ec..bff53aae81 100644 --- a/DuckDuckGo/SettingsViewModel.swift +++ b/DuckDuckGo/SettingsViewModel.swift @@ -53,7 +53,7 @@ final class SettingsViewModel: ObservableObject { private var cancellables = Set() // Defaults - @UserDefaultsWrapper(key: .privacyProHasActiveSubscription, defaultValue: false) + @UserDefaultsWrapper(key: .subscriptionIsActive, defaultValue: false) static private var cachedHasActiveSubscription: Bool // Closures to interact with legacy view controllers throught the container @@ -217,7 +217,7 @@ extension SettingsViewModel { speechRecognitionEnabled: AppDependencyProvider.shared.voiceSearchHelper.isSpeechRecognizerAvailable, loginsEnabled: featureFlagger.isFeatureOn(.autofillAccessCredentialManagement), networkProtection: getNetworkProtectionState(), - privacyPro: getPrivacyProState(), + subscription: getSubscriptionState(), sync: getSyncState() ) @@ -226,7 +226,7 @@ extension SettingsViewModel { #if SUBSCRIPTION if #available(iOS 15, *) { Task { - if state.privacyPro.enabled { + if state.subscription.enabled { await setupSubscriptionEnvironment() } } @@ -245,15 +245,15 @@ extension SettingsViewModel { return SettingsState.NetworkProtection(enabled: enabled, status: "") } - private func getPrivacyProState() -> SettingsState.PrivacyPro { + private func getSubscriptionState() -> SettingsState.Subscription { var enabled = false var canPurchase = false - var hasActiveSubscription = Self.cachedHasActiveSubscription + let hasActiveSubscription = Self.cachedHasActiveSubscription #if SUBSCRIPTION - enabled = featureFlagger.isFeatureOn(.privacyPro) + enabled = featureFlagger.isFeatureOn(.subscription) canPurchase = SubscriptionPurchaseEnvironment.canPurchase #endif - return SettingsState.PrivacyPro(enabled: enabled, + return SettingsState.Subscription(enabled: enabled, canPurchase: canPurchase, hasActiveSubscription: hasActiveSubscription) } @@ -307,13 +307,13 @@ extension SettingsViewModel { // Check for valid entitlements let hasEntitlements = await AccountManager().hasEntitlement(for: Self.entitlementNames.first!) - self.state.privacyPro.hasActiveSubscription = hasEntitlements ? true : false + self.state.subscription.hasActiveSubscription = hasEntitlements ? true : false // Cache Subscription state - Self.cachedHasActiveSubscription = self.state.privacyPro.hasActiveSubscription + Self.cachedHasActiveSubscription = self.state.subscription.hasActiveSubscription // Enable Subscription purchase if there's no active subscription - if self.state.privacyPro.hasActiveSubscription == false { + if self.state.subscription.hasActiveSubscription == false { setupSubscriptionPurchaseOptions() } } @@ -324,11 +324,11 @@ extension SettingsViewModel { @available(iOS 15.0, *) private func setupSubscriptionPurchaseOptions() { - self.state.privacyPro.hasActiveSubscription = false + self.state.subscription.hasActiveSubscription = false PurchaseManager.shared.$availableProducts .receive(on: RunLoop.main) .sink { [weak self] products in - self?.state.privacyPro.canPurchase = !products.isEmpty + self?.state.subscription.canPurchase = !products.isEmpty }.store(in: &cancellables) } #endif diff --git a/DuckDuckGo/PrivacyPro/Extensions/WKUserContentController+Handler.swift b/DuckDuckGo/Subscription/Extensions/WKUserContentController+Handler.swift similarity index 98% rename from DuckDuckGo/PrivacyPro/Extensions/WKUserContentController+Handler.swift rename to DuckDuckGo/Subscription/Extensions/WKUserContentController+Handler.swift index 9bfae41627..b156804a4e 100644 --- a/DuckDuckGo/PrivacyPro/Extensions/WKUserContentController+Handler.swift +++ b/DuckDuckGo/Subscription/Extensions/WKUserContentController+Handler.swift @@ -17,8 +17,6 @@ // limitations under the License. // -// TODO: Move to BSK - import Foundation import WebKit import UserScript diff --git a/DuckDuckGo/PrivacyPro/PrivacyPro.storekit b/DuckDuckGo/Subscription/Subscription.storekit similarity index 100% rename from DuckDuckGo/PrivacyPro/PrivacyPro.storekit rename to DuckDuckGo/Subscription/Subscription.storekit diff --git a/DuckDuckGo/PrivacyPro/Subscription/AccountManager.swift b/DuckDuckGo/Subscription/Subscription/AccountManager.swift similarity index 100% rename from DuckDuckGo/PrivacyPro/Subscription/AccountManager.swift rename to DuckDuckGo/Subscription/Subscription/AccountManager.swift diff --git a/DuckDuckGo/PrivacyPro/Subscription/AccountStorage/AccountKeychainStorage.swift b/DuckDuckGo/Subscription/Subscription/AccountStorage/AccountKeychainStorage.swift similarity index 100% rename from DuckDuckGo/PrivacyPro/Subscription/AccountStorage/AccountKeychainStorage.swift rename to DuckDuckGo/Subscription/Subscription/AccountStorage/AccountKeychainStorage.swift diff --git a/DuckDuckGo/PrivacyPro/Subscription/AccountStorage/AccountStorage.swift b/DuckDuckGo/Subscription/Subscription/AccountStorage/AccountStorage.swift similarity index 100% rename from DuckDuckGo/PrivacyPro/Subscription/AccountStorage/AccountStorage.swift rename to DuckDuckGo/Subscription/Subscription/AccountStorage/AccountStorage.swift diff --git a/DuckDuckGo/PrivacyPro/Subscription/Flows/AppStore/AppStoreAccountManagementFlow.swift b/DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStoreAccountManagementFlow.swift similarity index 100% rename from DuckDuckGo/PrivacyPro/Subscription/Flows/AppStore/AppStoreAccountManagementFlow.swift rename to DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStoreAccountManagementFlow.swift diff --git a/DuckDuckGo/PrivacyPro/Subscription/Flows/AppStore/AppStorePurchaseFlow.swift b/DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStorePurchaseFlow.swift similarity index 100% rename from DuckDuckGo/PrivacyPro/Subscription/Flows/AppStore/AppStorePurchaseFlow.swift rename to DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStorePurchaseFlow.swift diff --git a/DuckDuckGo/PrivacyPro/Subscription/Flows/AppStore/AppStoreRestoreFlow.swift b/DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStoreRestoreFlow.swift similarity index 100% rename from DuckDuckGo/PrivacyPro/Subscription/Flows/AppStore/AppStoreRestoreFlow.swift rename to DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStoreRestoreFlow.swift diff --git a/DuckDuckGo/PrivacyPro/Subscription/Flows/PurchaseFlow.swift b/DuckDuckGo/Subscription/Subscription/Flows/PurchaseFlow.swift similarity index 100% rename from DuckDuckGo/PrivacyPro/Subscription/Flows/PurchaseFlow.swift rename to DuckDuckGo/Subscription/Subscription/Flows/PurchaseFlow.swift diff --git a/DuckDuckGo/PrivacyPro/Subscription/Logging.swift b/DuckDuckGo/Subscription/Subscription/Logging.swift similarity index 100% rename from DuckDuckGo/PrivacyPro/Subscription/Logging.swift rename to DuckDuckGo/Subscription/Subscription/Logging.swift diff --git a/DuckDuckGo/PrivacyPro/Subscription/PurchaseManager.swift b/DuckDuckGo/Subscription/Subscription/PurchaseManager.swift similarity index 100% rename from DuckDuckGo/PrivacyPro/Subscription/PurchaseManager.swift rename to DuckDuckGo/Subscription/Subscription/PurchaseManager.swift diff --git a/DuckDuckGo/PrivacyPro/Subscription/Services/APIService.swift b/DuckDuckGo/Subscription/Subscription/Services/APIService.swift similarity index 100% rename from DuckDuckGo/PrivacyPro/Subscription/Services/APIService.swift rename to DuckDuckGo/Subscription/Subscription/Services/APIService.swift diff --git a/DuckDuckGo/PrivacyPro/Subscription/Services/AuthService.swift b/DuckDuckGo/Subscription/Subscription/Services/AuthService.swift similarity index 100% rename from DuckDuckGo/PrivacyPro/Subscription/Services/AuthService.swift rename to DuckDuckGo/Subscription/Subscription/Services/AuthService.swift diff --git a/DuckDuckGo/PrivacyPro/Subscription/Services/SubscriptionService.swift b/DuckDuckGo/Subscription/Subscription/Services/SubscriptionService.swift similarity index 100% rename from DuckDuckGo/PrivacyPro/Subscription/Services/SubscriptionService.swift rename to DuckDuckGo/Subscription/Subscription/Services/SubscriptionService.swift diff --git a/DuckDuckGo/PrivacyPro/Subscription/SubscriptionPurchaseEnvironment.swift b/DuckDuckGo/Subscription/Subscription/SubscriptionPurchaseEnvironment.swift similarity index 100% rename from DuckDuckGo/PrivacyPro/Subscription/SubscriptionPurchaseEnvironment.swift rename to DuckDuckGo/Subscription/Subscription/SubscriptionPurchaseEnvironment.swift diff --git a/DuckDuckGo/PrivacyPro/Subscription/URL+Subscription.swift b/DuckDuckGo/Subscription/Subscription/URL+Subscription.swift similarity index 91% rename from DuckDuckGo/PrivacyPro/Subscription/URL+Subscription.swift rename to DuckDuckGo/Subscription/Subscription/URL+Subscription.swift index 8b2fded391..2451ab5055 100644 --- a/DuckDuckGo/PrivacyPro/Subscription/URL+Subscription.swift +++ b/DuckDuckGo/Subscription/Subscription/URL+Subscription.swift @@ -47,4 +47,8 @@ public extension URL { static var manageSubscriptionsInAppStoreAppURL: URL { URL(string: "macappstores://apps.apple.com/account/subscriptions")! } + + static var manageSubscriptionsIniOSAppStoreAppURL: URL { + URL(string: "https://apps.apple.com/account/subscriptions")! + } } diff --git a/DuckDuckGo/PrivacyPro/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift similarity index 78% rename from DuckDuckGo/PrivacyPro/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift rename to DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift index 97ba439af3..04f6d26d95 100644 --- a/DuckDuckGo/PrivacyPro/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift +++ b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift @@ -59,7 +59,11 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec static let year = "yearly" } - @Published var transactionInProgress = false + enum TransactionStatus { + case idle, purchasing, restoring, polling + } + + @Published var transactionStatus: TransactionStatus = .idle @Published var hasActiveSubscription = false @Published var purchaseError: AppStorePurchaseFlow.Error? @@ -108,9 +112,9 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec // Manage transation in progress flag private func withTransactionInProgress(_ work: () async throws -> T) async rethrows -> T { - transactionInProgress = true + transactionStatus = transactionStatus defer { - transactionInProgress = false + transactionStatus = .idle } return try await work() } @@ -129,13 +133,14 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec await withTransactionInProgress { + transactionStatus = .purchasing resetSubscriptionFlow() switch await AppStorePurchaseFlow.subscriptionOptions() { case .success(let subscriptionOptions): return subscriptionOptions case .failure: - // TODO: handle errors - no products found + return nil } @@ -146,6 +151,7 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec await withTransactionInProgress { + transactionStatus = .purchasing resetSubscriptionFlow() struct SubscriptionSelection: Decodable { @@ -153,42 +159,36 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec } let message = original + guard let subscriptionSelection: SubscriptionSelection = DecodableHelper.decode(from: params) else { + assertionFailure("SubscriptionPagesUserScript: expected JSON representation of SubscriptionSelection") + return nil + } - if #available(iOS 15, *) { - guard let subscriptionSelection: SubscriptionSelection = DecodableHelper.decode(from: params) else { - assertionFailure("SubscriptionPagesUserScript: expected JSON representation of SubscriptionSelection") - return nil - } - - print("Selected: \(subscriptionSelection.id)") - - // Check for active subscriptions - if await PurchaseManager.hasActiveSubscription() { - hasActiveSubscription = true - return nil - } - - let emailAccessToken = try? EmailManager().getToken() + // Check for active subscriptions + if await PurchaseManager.hasActiveSubscription() { + hasActiveSubscription = true + return nil + } + + let emailAccessToken = try? EmailManager().getToken() - switch await AppStorePurchaseFlow.purchaseSubscription(with: subscriptionSelection.id, emailAccessToken: emailAccessToken) { - case .success: - break - case .failure: - purchaseError = .purchaseFailed - originalMessage = original - return nil - } - - switch await AppStorePurchaseFlow.completeSubscriptionPurchase() { - case .success(let purchaseUpdate): - await pushPurchaseUpdate(originalMessage: message, purchaseUpdate: purchaseUpdate) - case .failure: - // TODO: handle errors - missing entitlements on post purchase check - purchaseError = .missingEntitlements - await pushPurchaseUpdate(originalMessage: message, purchaseUpdate: PurchaseUpdate(type: "completed")) - } + switch await AppStorePurchaseFlow.purchaseSubscription(with: subscriptionSelection.id, emailAccessToken: emailAccessToken) { + case .success: + break + case .failure: + purchaseError = .purchaseFailed + originalMessage = original + return nil } + transactionStatus = .polling + switch await AppStorePurchaseFlow.completeSubscriptionPurchase() { + case .success(let purchaseUpdate): + await pushPurchaseUpdate(originalMessage: message, purchaseUpdate: purchaseUpdate) + case .failure: + purchaseError = .missingEntitlements + await pushPurchaseUpdate(originalMessage: message, purchaseUpdate: PurchaseUpdate(type: "completed")) + } return nil } } @@ -217,13 +217,10 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec accountManager.storeAccount(token: accessToken, email: accountDetails.email, externalID: accountDetails.externalID) } - // TODO: Navigate back to settings - return nil } func activateSubscription(params: Any, original: WKScriptMessage) async throws -> Encodable? { - print(">>> Selected to activate a subscription -- show the activation settings screen") return nil } @@ -236,8 +233,7 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec assertionFailure("SubscriptionPagesUserScript: expected JSON representation of FeatureSelection") return nil } - - print(">>> Selected a feature -- show the corresponding UI", featureSelection) + return nil } @@ -254,14 +250,14 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec func pushAction(method: SubscribeActionName, webView: WKWebView, params: Encodable) { let broker = UserScriptMessageBroker(context: SubscriptionPagesUserScript.context, requiresRunInPageContentWorld: true ) - - print(">>> Pushing into WebView:", method.rawValue, String(describing: params)) + broker.push(method: method.rawValue, params: params, for: self, into: webView) } func restoreAccountFromAppStorePurchase() async -> Bool { await withTransactionInProgress { + transactionStatus = .restoring switch await AppStoreRestoreFlow.restoreAccountFromPastPurchase() { case .success(let update): return true diff --git a/DuckDuckGo/PrivacyPro/UserScripts/SubscriptionPagesUserScript.swift b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUserScript.swift similarity index 100% rename from DuckDuckGo/PrivacyPro/UserScripts/SubscriptionPagesUserScript.swift rename to DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUserScript.swift diff --git a/DuckDuckGo/PrivacyPro/ViewModel/SubscriptionFlowViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift similarity index 81% rename from DuckDuckGo/PrivacyPro/ViewModel/SubscriptionFlowViewModel.swift rename to DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift index 60550e87ea..440b4060de 100644 --- a/DuckDuckGo/PrivacyPro/ViewModel/SubscriptionFlowViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift @@ -37,7 +37,7 @@ final class SubscriptionFlowViewModel: ObservableObject { // State variables var purchaseURL = URL.purchaseSubscription @Published var hasActiveSubscription = false - @Published var transactionInProgress = false + @Published var transactionStatus: SubscriptionPagesUseSubscriptionFeature.TransactionStatus = .idle @Published var shouldReloadWebview = false init(userScript: SubscriptionPagesUserScript = SubscriptionPagesUserScript(), @@ -51,10 +51,18 @@ final class SubscriptionFlowViewModel: ObservableObject { // Observe transaction status private func setupTransactionObserver() async { - subFeature.$transactionInProgress + subFeature.$transactionStatus .sink { [weak self] status in guard let self = self else { return } - Task { await self.setTransactionInProgress(status) } + Task { await self.setTransactionStatus(status) } + + } + .store(in: &cancellables) + + subFeature.$hasActiveSubscription + .receive(on: DispatchQueue.main) + .sink { [weak self] value in + self?.hasActiveSubscription = value } .store(in: &cancellables) @@ -67,8 +75,8 @@ final class SubscriptionFlowViewModel: ObservableObject { } @MainActor - private func setTransactionInProgress(_ inProgress: Bool) { - self.transactionInProgress = inProgress + private func setTransactionStatus(_ status: SubscriptionPagesUseSubscriptionFeature.TransactionStatus) { + self.transactionStatus = status } func initializeViewData() async { @@ -81,7 +89,6 @@ final class SubscriptionFlowViewModel: ObservableObject { await MainActor.run { shouldReloadWebview = true } } else { await MainActor.run { - // TODO: Display error when restoring subscription } } } diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift new file mode 100644 index 0000000000..243e046534 --- /dev/null +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift @@ -0,0 +1,92 @@ +// +// SubscriptionSettingsViewModel.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 +import StoreKit + +#if SUBSCRIPTION +@available(iOS 15.0, *) +final class SubscriptionSettingsViewModel: ObservableObject { + + private let accountManager: AccountManager + + var subscriptionDetails: String = "" + @Published var shouldDisplayRemovalNotice: Bool = false + + init(accountManager: AccountManager = AccountManager()) { + self.accountManager = accountManager + fetchAndUpdateSubscriptionDetails() + } + + private var dateFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.dateFormat = "MMM dd, yyyy" // Jan 12, 2024" + return formatter + }() + + private func fetchAndUpdateSubscriptionDetails() { + Task { + guard let token = accountManager.accessToken else { return } + + if let cachedDate = SubscriptionService.cachedSubscriptionDetailsResponse?.expiresOrRenewsAt { + updateSubscriptionDetails(date: cachedDate) + } + + if case .success(let response) = await SubscriptionService.getSubscriptionDetails(token: token) { + if !response.isSubscriptionActive { + AccountManager().signOut() + return + } + } + } + } + + private func updateSubscriptionDetails(date: Date) { + self.subscriptionDetails = UserText.subscriptionInfo(expiration: dateFormatter.string(from: date)) + } + + func removeSubscription() { + AccountManager().signOut() + } + + func manageSubscription() { + if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene { + Task { + do { + try await AppStore.showManageSubscriptions(in: windowScene) + } catch { + openSubscriptionManagementURL() + } + } + } else { + openSubscriptionManagementURL() + } + } + + private func openSubscriptionManagementURL() { + let url = URL.manageSubscriptionsIniOSAppStoreAppURL + if UIApplication.shared.canOpenURL(url) { + UIApplication.shared.open(url, options: [:], completionHandler: nil) + } + } + + +} +#endif diff --git a/DuckDuckGo/PrivacyPro/Views/HeadlessWebView.swift b/DuckDuckGo/Subscription/Views/HeadlessWebView.swift similarity index 100% rename from DuckDuckGo/PrivacyPro/Views/HeadlessWebView.swift rename to DuckDuckGo/Subscription/Views/HeadlessWebView.swift diff --git a/DuckDuckGo/Subscription/Views/PurchaseInProgressView.swift b/DuckDuckGo/Subscription/Views/PurchaseInProgressView.swift new file mode 100644 index 0000000000..8b4aed988e --- /dev/null +++ b/DuckDuckGo/Subscription/Views/PurchaseInProgressView.swift @@ -0,0 +1,92 @@ +// +// PurchaseInProgressView.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +import SwiftUI +import DesignResourcesKit + +struct PurchaseInProgressView: View { + @Environment(\.colorScheme) var colorScheme + + var status: String + enum DesignConstants { + static let coverOpacity = 0.6 + static let cornerRadius = 12.0 + static let shadowRadius = 10.0 + static let lightShadowColor = Color.gray50 + static let darkShadowColor = Color.black + static let spinnerScale = 2.0 + static let internalZStackWidth = 220.0 + static let horizontalPadding = 20.0 + static let verticalPadding = 100.0 + } + + @State private var viewHeight: CGFloat = 0 // State to store the height of the VStack + + var body: some View { + ZStack { + Color(designSystemColor: .background) + .opacity(Self.DesignConstants.coverOpacity) + .edgesIgnoringSafeArea(.all) + .disabled(true) + + ZStack { + RoundedRectangle(cornerRadius: DesignConstants.cornerRadius) + .fill(Color(designSystemColor: .background)) + .frame(width: DesignConstants.internalZStackWidth, height: viewHeight) // Use the dynamic height + .shadow(color: colorScheme == .dark ? DesignConstants.darkShadowColor : DesignConstants.lightShadowColor, + radius: DesignConstants.shadowRadius) + + VStack { + SwiftUI.ProgressView() + .scaleEffect(DesignConstants.spinnerScale) + .progressViewStyle(CircularProgressViewStyle(tint: Color(designSystemColor: DesignSystemColor.icons))) + .padding(.bottom, 18) + .padding(.top, 10) + + Text(status) + .daxSubheadRegular() + .foregroundColor(Color(designSystemColor: .textPrimary)) + .frame(width: DesignConstants.internalZStackWidth - 2 * DesignConstants.horizontalPadding) + .multilineTextAlignment(.center) + .background(GeometryReader { geometry in + Color.clear.onAppear { + viewHeight = geometry.size.height + DesignConstants.verticalPadding + } + }) + } + .frame(width: DesignConstants.internalZStackWidth) + } + } + } +} + +// Preference key to store the height of the content +struct ViewHeightKey: PreferenceKey { + static var defaultValue: CGFloat = 0 + static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { + value = max(value, nextValue()) + } +} + +struct PurchaseInProgressView_Previews: PreviewProvider { + static var previews: some View { + PurchaseInProgressView(status: "Completing Purchase... ") + .previewLayout(.sizeThatFits) + .padding() + } +} diff --git a/DuckDuckGo/PrivacyPro/Views/SubscriptionFlowView.swift b/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift similarity index 61% rename from DuckDuckGo/PrivacyPro/Views/SubscriptionFlowView.swift rename to DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift index 4060d53e57..07d7571e0a 100644 --- a/DuckDuckGo/PrivacyPro/Views/SubscriptionFlowView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift @@ -26,6 +26,19 @@ struct SubscriptionFlowView: View { @ObservedObject var viewModel: SubscriptionFlowViewModel + private func getTransactionStatus() -> String { + switch viewModel.transactionStatus { + case .polling: + return UserText.subscriptionCompletingPurchaseTitle + case .purchasing: + return UserText.subscriptionPurchasingTitle + case .restoring: + return UserText.subscriptionPestoringTitle + case .idle: + return "" + } + } + var body: some View { ZStack { AsyncHeadlessWebView(url: $viewModel.purchaseURL, @@ -34,13 +47,17 @@ struct SubscriptionFlowView: View { shouldReload: $viewModel.shouldReloadWebview).background() // Overlay that appears when transaction is in progress - if viewModel.transactionInProgress { - PurchaseInProgressView() + if viewModel.transactionStatus != .idle { + PurchaseInProgressView(status: getTransactionStatus()) + } + } + .onChange(of: viewModel.shouldReloadWebview) { shouldReload in + if shouldReload { + viewModel.shouldReloadWebview = false } } .onChange(of: viewModel.shouldReloadWebview) { shouldReload in if shouldReload { - print("WebView reload triggered") viewModel.shouldReloadWebview = false } } @@ -48,21 +65,21 @@ struct SubscriptionFlowView: View { Task { await viewModel.initializeViewData() } }) .navigationTitle(viewModel.viewTitle) - .navigationBarBackButtonHidden(viewModel.transactionInProgress) + .navigationBarBackButtonHidden(viewModel.transactionStatus != .idle) // Active subscription found Alert .alert(isPresented: $viewModel.hasActiveSubscription) { Alert( - title: Text("Subscription Found"), - message: Text("We found a subscription associated with this Apple ID."), - primaryButton: .cancel(Text("Cancel")) { - // TODO: Handle subscription Restore cancellation + title: Text(UserText.subscriptionFoundTitle), + message: Text(UserText.subscriptionFoundText), + primaryButton: .cancel(Text(UserText.subscriptionFoundCancel)) { }, - secondaryButton: .default(Text("Restore")) { + secondaryButton: .default(Text(UserText.subscriptionFoundCancel)) { viewModel.restoreAppstoreTransaction() } ) } + .navigationBarBackButtonHidden(viewModel.transactionStatus != .idle) } } #endif diff --git a/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift b/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift new file mode 100644 index 0000000000..769041e705 --- /dev/null +++ b/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift @@ -0,0 +1,99 @@ +// +// SubscriptionSettingsView.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 +import DesignResourcesKit + +class SceneEnvironment: ObservableObject { + weak var windowScene: UIWindowScene? +} + +#if SUBSCRIPTION +@available(iOS 15.0, *) +struct SubscriptionSettingsView: View { + + @ObservedObject var viewModel: SubscriptionSettingsViewModel + @Environment(\.presentationMode) var presentationMode + @StateObject var sceneEnvironment = SceneEnvironment() + + var body: some View { + List { + Section(header: Text(viewModel.subscriptionDetails) + .lineLimit(nil) + .daxBodyRegular() + .fixedSize(horizontal: false, vertical: true)) { + EmptyView() + .frame(height: 0) + .hidden() + }.textCase(nil) + Section(header: Text(UserText.subscriptionManageDevices)) { + SettingsCustomCell(content: { + Text(UserText.subscriptionAddDevice) + .daxBodyRegular() + .foregroundColor(Color.init(designSystemColor: .accent)) + }, + action: {}) + + SettingsCustomCell(content: { + Text(UserText.subscriptionRemoveFromDevice) + .daxBodyRegular() + .foregroundColor(Color.init(designSystemColor: .accent))}, + action: { viewModel.shouldDisplayRemovalNotice.toggle() }, + isButton: true) + + } + Section(header: Text(UserText.subscriptionManagePlan)) { + SettingsCustomCell(content: { + Text(UserText.subscriptionChangePlan) + .daxBodyRegular() + }, + action: { Task { viewModel.manageSubscription() } }, + isButton: true) + } + Section(header: Text(UserText.subscriptionHelpAndSupport), + footer: Text(UserText.subscriptionFAQFooter)) { + NavigationLink(destination: Text(UserText.subscriptionFAQ)) { + SettingsCustomCell(content: { + Text(UserText.subscriptionFAQ) + .daxBodyRegular() + }, + action: {}, + disclosureIndicator: false) + } + } + } + .navigationTitle(UserText.settingsPProManageSubscription) + + // Remove subscription + .alert(isPresented: $viewModel.shouldDisplayRemovalNotice) { + Alert( + title: Text(UserText.subscriptionRemoveFromDeviceConfirmTitle), + message: Text(UserText.subscriptionRemoveFromDeviceConfirmText), + primaryButton: .cancel(Text(UserText.subscriptionRemoveCancel)) { + }, + secondaryButton: .destructive(Text(UserText.subscriptionRemove)) { + viewModel.removeSubscription() + presentationMode.wrappedValue.dismiss() + } + ) + } + } +} +#endif diff --git a/DuckDuckGo/UserText.swift b/DuckDuckGo/UserText.swift index cf49931d46..dc0705d9e9 100644 --- a/DuckDuckGo/UserText.swift +++ b/DuckDuckGo/UserText.swift @@ -961,25 +961,24 @@ But if you *do* want a peek under the hood, you can find more information about // Privacy Pro Section public static let settingsPProSection = NSLocalizedString("settings.ppro", value: "Privacy Pro", comment: "Product name for the subscription bundle") - public static let settingsPProSubscribe = NSLocalizedString("settings.ppro.subscribe", value: "Subscribe to Privacy Pro", comment: "Call to action title for Privacy Pro") - public static let settingsPProDescription = NSLocalizedString("settings.ppro.description", value:"More seamless privacy with three new protections, including:", comment: "Privacy pro description subtext") - public static let settingsPProFeatures = NSLocalizedString("settings.ppro.features", value: + public static let settingsPProSubscribe = NSLocalizedString("settings.subscription.subscribe", value: "Subscribe to Privacy Pro", comment: "Call to action title for Privacy Pro") + public static let settingsPProDescription = NSLocalizedString("settings.subscription.description", value:"More seamless privacy with three new protections, including:", comment: "Privacy pro description subtext") + public static let settingsPProFeatures = NSLocalizedString("settings.subscription.features", value: """ • VPN (Virtual Private Network) • Personal Information Removal • Identity Theft Restoration """, comment: "Privacy pro features list") - public static let settingsPProLearnMore = NSLocalizedString("settings.ppro.learn.more", value: "Learn More", comment: "Learn more button text for privacy pro") + public static let settingsPProLearnMore = NSLocalizedString("settings.subscription.learn.more", value: "Learn More", comment: "Learn more button text for privacy pro") - public static let settingsPProManageSubscription = NSLocalizedString("settings.ppro.manage", value: "Subscription Settings", comment: "Subscription Settings button text for privacy pro") - - public static let settingsPProVPNTitle = NSLocalizedString("settings.ppro.VPN.title", value: "VPN", comment: "VPN cell title for privacy pro") - public static let settingsPProDBPTitle = NSLocalizedString("settings.ppro.DBP.title", value: "Personal Information Removal", comment: "Data Broker protection cell title for privacy pro") - public static let settingsPProDBPSubTitle = NSLocalizedString("settings.ppro.DBP.subtitle", value: "Remove your info from sites that sell it", comment: "Data Broker protection cell subtitle for privacy pro") - public static let settingsPProITRTitle = NSLocalizedString("settings.ppro.ITR.title", value: "Identity Theft Restoration", comment: "Identity theft restoration cell title for privacy pro") - public static let settingsPProITRSubTitle = NSLocalizedString("settings.ppro.ITR.subtitle", value: "If your identity is stolen, we'll help restore it", comment: "Identity theft restoration cell subtitle for privacy pro") + public static let settingsPProManageSubscription = NSLocalizedString("settings.subscription.manage", value: "Subscription Settings", comment: "Subscription Settings button text for privacy pro") + public static let settingsPProVPNTitle = NSLocalizedString("settings.subscription.VPN.title", value: "VPN", comment: "VPN cell title for privacy pro") + public static let settingsPProDBPTitle = NSLocalizedString("settings.subscription.DBP.title", value: "Personal Information Removal", comment: "Data Broker protection cell title for privacy pro") + public static let settingsPProDBPSubTitle = NSLocalizedString("settings.subscription.DBP.subtitle", value: "Remove your info from sites that sell it", comment: "Data Broker protection cell subtitle for privacy pro") + public static let settingsPProITRTitle = NSLocalizedString("settings.subscription.ITR.title", value: "Identity Theft Restoration", comment: "Identity theft restoration cell title for privacy pro") + public static let settingsPProITRSubTitle = NSLocalizedString("settings.subscription.ITR.subtitle", value: "If your identity is stolen, we'll help restore it", comment: "Identity theft restoration cell subtitle for privacy pro") // Customize Section @@ -1002,6 +1001,31 @@ But if you *do* want a peek under the hood, you can find more information about public static let settingsVersion = NSLocalizedString("settings.version", value: "Version", comment: "Settings cell for Version") public static let settingsFeedback = NSLocalizedString("settings.feedback", value: "Share Feedback", comment: "Settings cell for Feedback") + // Privacy Pro Subscriptions + + static let subscriptionPurchasingTitle = NSLocalizedString("subscription.progress.view.purchasing.subscription", value: "Purchase in progress...", comment: "Progress view title when starting the purchase") + static let subscriptionPestoringTitle = NSLocalizedString("subscription.progress.view.restoring.subscription", value: "Restoring subscription...", comment: "Progress view title when restoring past subscription purchase") + static let subscriptionCompletingPurchaseTitle = NSLocalizedString("subscription.progress.view.completing.purchase", value: "Completing purchase...", comment: "Progress view title when completing the purchase") + static func subscriptionInfo(expiration: String) -> String { + let localized = NSLocalizedString("subscription.subscription.active.caption", value: "Your Privacy Pro subscription renews on %@", comment: "Subscription Expiration Data") + return String(format: localized, expiration) + } + public static let subscriptionManageDevices = NSLocalizedString("subscription.manage.device", value: "Manage Devices", comment: "Header for the device management section") + public static let subscriptionAddDevice = NSLocalizedString("subscription.add.device", value: "Add to Another Device", comment: "Add to another device button") + public static let subscriptionRemoveFromDevice = NSLocalizedString("subscription.remove.from.device", value: "Remove From This Device", comment: "Remove from this device button") + public static let subscriptionManagePlan = NSLocalizedString("subscription.manage.plan", value: "Manage Plan", comment: "Manage Plan header") + public static let subscriptionChangePlan = NSLocalizedString("subscription.change.plan", value: "Change Plan Or Billing", comment: "Change plan or billing title") + public static let subscriptionHelpAndSupport = NSLocalizedString("subscription.help", value: "Help and support", comment: "Help and support Section header") + public static let subscriptionFAQ = NSLocalizedString("subscription.faq", value: "Privacy Pro FAQ", comment: "FAQ Button") + public static let subscriptionFAQFooter = NSLocalizedString("subscription.faq.description", value: "Visit our Privacy Pro help pages for answers to frequently asked questions", comment: "FAQ Description") + public static let subscriptionRemoveFromDeviceConfirmTitle = NSLocalizedString("subscription.remove.from.device.title", value: "Remove From This Device?", comment: "Remove from device confirmation dialog title") + public static let subscriptionRemoveFromDeviceConfirmText = NSLocalizedString("subscription.remove.from.device.text", value: "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices.", comment: "Remove from device confirmation dialog text") + public static let subscriptionRemove = NSLocalizedString("subscription.remove.subscription", value: "Remove Subscription", comment: "Remove subscription button text") + public static let subscriptionRemoveCancel = NSLocalizedString("subscription.remove.subscription.cancel", value: "Cancel", comment: "Remove subscription cancel button text") + public static let subscriptionFoundTitle = NSLocalizedString("subscription.subscription.found.tite", value: "Subscription Found", comment: "Title for the existing subscription dialog") + public static let subscriptionFoundText = NSLocalizedString("subscription.subscription.found.text", value: "We found a subscription associated with this Apple ID.", comment: "Message for the existing subscription dialog") + public static let subscriptionFoundCancel = NSLocalizedString("subscription.subscription.found.cancel", value: "Cancel", comment: "Cancel action for the existing subscription dialog") + public static let subscriptionFoundRestore = NSLocalizedString("subscription.subscription.found.restore", value: "Restore", comment: "Restore action for the existing subscription dialog") } diff --git a/DuckDuckGo/en.lproj/Localizable.strings b/DuckDuckGo/en.lproj/Localizable.strings index b4aaf23976..34845d2775 100644 --- a/DuckDuckGo/en.lproj/Localizable.strings +++ b/DuckDuckGo/en.lproj/Localizable.strings @@ -1837,43 +1837,43 @@ But if you *do* want a peek under the hood, you can find more information about /* Product name for the subscription bundle */ "settings.ppro" = "Privacy Pro"; +/* Settings screen cell for long press previews */ +"settings.previews" = "Long-Press Previews"; + +/* Settings title for the privacy section */ +"settings.privacy" = "Privacy"; + /* Data Broker protection cell subtitle for privacy pro */ -"settings.ppro.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; /* Data Broker protection cell title for privacy pro */ -"settings.ppro.DBP.title" = "Personal Information Removal"; +"settings.subscription.DBP.title" = "Personal Information Removal"; /* Privacy pro description subtext */ -"settings.ppro.description" = "More seamless privacy with three new protections, including:"; +"settings.subscription.description" = "More seamless privacy with three new protections, including:"; /* Privacy pro features list */ -"settings.ppro.features" = " • VPN (Virtual Private Network) +"settings.subscription.features" = " • VPN (Virtual Private Network) • Personal Information Removal • Identity Theft Restoration"; /* Identity theft restoration cell subtitle for privacy pro */ -"settings.ppro.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; /* Identity theft restoration cell title for privacy pro */ -"settings.ppro.ITR.title" = "Identity Theft Restoration"; +"settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Learn more button text for privacy pro */ -"settings.ppro.learn.more" = "Learn More"; +"settings.subscription.learn.more" = "Learn More"; /* Subscription Settings button text for privacy pro */ -"settings.ppro.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Subscription Settings"; /* Call to action title for Privacy Pro */ -"settings.ppro.subscribe" = "Subscribe to Privacy Pro"; +"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; /* VPN cell title for privacy pro */ -"settings.ppro.VPN.title" = "VPN"; - -/* Settings screen cell for long press previews */ -"settings.previews" = "Long-Press Previews"; - -/* Settings title for the privacy section */ -"settings.privacy" = "Privacy"; +"settings.subscription.VPN.title" = "VPN"; /* Settings screen cell text for sync and backup */ "settings.sync" = "Sync & Backup"; @@ -1914,6 +1914,66 @@ But if you *do* want a peek under the hood, you can find more information about /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Which website is broken?"; +/* Add to another device button */ +"subscription.add.device" = "Add to Another Device"; + +/* Change plan or billing title */ +"subscription.change.plan" = "Change Plan Or Billing"; + +/* FAQ Button */ +"subscription.faq" = "Privacy Pro FAQ"; + +/* FAQ Description */ +"subscription.faq.description" = "Visit our Privacy Pro help pages for answers to frequently asked questions"; + +/* Help and support Section header */ +"subscription.help" = "Help and support"; + +/* Header for the device management section */ +"subscription.manage.device" = "Manage Devices"; + +/* Manage Plan header */ +"subscription.manage.plan" = "Manage Plan"; + +/* Progress view title when completing the purchase */ +"subscription.progress.view.completing.purchase" = "Completing purchase..."; + +/* Progress view title when starting the purchase */ +"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; + +/* Progress view title when restoring past subscription purchase */ +"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; + +/* Remove from this device button */ +"subscription.remove.from.device" = "Remove From This Device"; + +/* Remove from device confirmation dialog text */ +"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; + +/* Remove from device confirmation dialog title */ +"subscription.remove.from.device.title" = "Remove From This Device?"; + +/* Remove subscription button text */ +"subscription.remove.subscription" = "Remove Subscription"; + +/* Remove subscription cancel button text */ +"subscription.remove.subscription.cancel" = "Cancel"; + +/* Subscription Expiration Data */ +"subscription.subscription.active.caption" = "Your Privacy Pro subscription renews on %@"; + +/* Cancel action for the existing subscription dialog */ +"subscription.subscription.found.cancel" = "Cancel"; + +/* Restore action for the existing subscription dialog */ +"subscription.subscription.found.restore" = "Restore"; + +/* Message for the existing subscription dialog */ +"subscription.subscription.found.text" = "We found a subscription associated with this Apple ID."; + +/* Title for the existing subscription dialog */ +"subscription.subscription.found.tite" = "Subscription Found"; + /* Message confirming that recovery code was copied to clipboard */ "sync.code.copied" = "Recovery code copied to clipboard"; From 49c0c32f5fe51f7fcd6a0c56735517c37c625bc9 Mon Sep 17 00:00:00 2001 From: bwaresiak Date: Wed, 17 Jan 2024 16:42:41 +0100 Subject: [PATCH 02/70] Fix concurrent timestamp updates (#2350) Task/Issue URL: https://app.asana.com/0/1201493110486074/1206372583396814/f Tech Design URL: CC: Description: Make sure that direct updates from sync does not invoke the generic timestamp-bumping logic. Steps to test this PR: Run the connect flow. Ensure no pixel about local override is fired. --- DuckDuckGo/FavoritesDisplayModeSyncHandler.swift | 7 ++++++- DuckDuckGo/SyncSettingsViewController.swift | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/DuckDuckGo/FavoritesDisplayModeSyncHandler.swift b/DuckDuckGo/FavoritesDisplayModeSyncHandler.swift index cfe949f3b3..3d8ee67f68 100644 --- a/DuckDuckGo/FavoritesDisplayModeSyncHandler.swift +++ b/DuckDuckGo/FavoritesDisplayModeSyncHandler.swift @@ -31,13 +31,18 @@ final class FavoritesDisplayModeSyncHandler: FavoritesDisplayModeSyncHandlerBase override func setValue(_ value: String?, shouldDetectOverride: Bool) throws { if let value, let displayMode = FavoritesDisplayMode(value) { appSettings.favoritesDisplayMode = displayMode - NotificationCenter.default.post(name: AppUserDefaults.Notifications.favoritesDisplayModeChange, object: nil) + NotificationCenter.default.post(name: AppUserDefaults.Notifications.favoritesDisplayModeChange, object: self) } } override var valueDidChangePublisher: AnyPublisher { NotificationCenter.default .publisher(for: AppUserDefaults.Notifications.favoritesDisplayModeChange) + .filter({ [weak self] n in + guard let self else { return false } + guard let object = n.object as? AnyObject else { return true } + return object !== self + }) .map { _ in } .eraseToAnyPublisher() } diff --git a/DuckDuckGo/SyncSettingsViewController.swift b/DuckDuckGo/SyncSettingsViewController.swift index d74642813e..20f9238a31 100644 --- a/DuckDuckGo/SyncSettingsViewController.swift +++ b/DuckDuckGo/SyncSettingsViewController.swift @@ -135,7 +135,7 @@ class SyncSettingsViewController: UIHostingController { private func setUpFavoritesDisplayModeSwitch(_ viewModel: SyncSettingsViewModel, _ appSettings: AppSettings) { viewModel.isUnifiedFavoritesEnabled = appSettings.favoritesDisplayMode.isDisplayUnified - viewModel.$isUnifiedFavoritesEnabled.dropFirst() + viewModel.$isUnifiedFavoritesEnabled.dropFirst().removeDuplicates() .sink { [weak self] isEnabled in appSettings.favoritesDisplayMode = isEnabled ? .displayUnified(native: .mobile) : .displayNative(.mobile) NotificationCenter.default.post(name: AppUserDefaults.Notifications.favoritesDisplayModeChange, object: self) From 6aad3347d027165adb6481878e3f9558865a46ac Mon Sep 17 00:00:00 2001 From: Brian Hall Date: Wed, 17 Jan 2024 15:02:45 -0600 Subject: [PATCH 03/70] Bump BSK version (#2341) Co-authored-by: Fernando Bunn --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 8 ++++---- LocalPackages/DuckUI/Package.swift | 2 +- LocalPackages/SyncUI/Package.swift | 2 +- LocalPackages/Waitlist/Package.swift | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 7b0e2e27d2..3acee7f6cb 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -9910,7 +9910,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 101.1.2; + version = 101.1.3; }; }; C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index da0247ee22..e3490e21cb 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "44569e233945c099887266c1d7b8e07b25558a02", - "version" : "101.1.2" + "revision" : "13c6ccd65f45e9940371c28a1a3498ec29ac9d0b", + "version" : "101.1.3" } }, { @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/content-scope-scripts", "state" : { - "revision" : "bb027f14bec7fbb1a85d308139e7a66686da160e", - "version" : "4.59.0" + "revision" : "0b68b0d404d8d4f32296cd84fa160b18b0aeaf44", + "version" : "4.59.1" } }, { diff --git a/LocalPackages/DuckUI/Package.swift b/LocalPackages/DuckUI/Package.swift index bd15696976..e663006baa 100644 --- a/LocalPackages/DuckUI/Package.swift +++ b/LocalPackages/DuckUI/Package.swift @@ -31,7 +31,7 @@ let package = Package( targets: ["DuckUI"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.1.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.1.3"), ], targets: [ .target( diff --git a/LocalPackages/SyncUI/Package.swift b/LocalPackages/SyncUI/Package.swift index 5fc0e855a0..2e2aafac4a 100644 --- a/LocalPackages/SyncUI/Package.swift +++ b/LocalPackages/SyncUI/Package.swift @@ -33,7 +33,7 @@ let package = Package( ], dependencies: [ .package(path: "../DuckUI"), - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.1.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.1.3"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ diff --git a/LocalPackages/Waitlist/Package.swift b/LocalPackages/Waitlist/Package.swift index 4f02d33740..125e62f7d7 100644 --- a/LocalPackages/Waitlist/Package.swift +++ b/LocalPackages/Waitlist/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["Waitlist", "WaitlistMocks"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.1.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.1.3"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ From 2093b691c6e216654b7aa81f03ce52fae58dba40 Mon Sep 17 00:00:00 2001 From: Sabrina Tardio <44158575+SabrinaTardio@users.noreply.github.com> Date: Thu, 18 Jan 2024 10:45:11 +0100 Subject: [PATCH 04/70] fix copy (#2346) --- DuckDuckGo/UserText.swift | 2 +- DuckDuckGo/en.lproj/Localizable.strings | 2 +- .../Sources/SyncUI/Resources/en.lproj/Localizable.strings | 8 ++++---- .../SyncUI/Sources/SyncUI/Views/Internal/UserText.swift | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/DuckDuckGo/UserText.swift b/DuckDuckGo/UserText.swift index dc0705d9e9..d289dc1220 100644 --- a/DuckDuckGo/UserText.swift +++ b/DuckDuckGo/UserText.swift @@ -863,7 +863,7 @@ But if you *do* want a peek under the hood, you can find more information about public static let syncErrorAlertTitle = NSLocalizedString("alert.sync-error", value: "Sync & Backup Error", comment: "Title for sync error alert") public static let unableToSyncToServerDescription = NSLocalizedString("alert.unable-to-sync-to-server-description", value: "Unable to connect to the server.", comment: "Description for unable to sync to server error") public static let unableToSyncWithOtherDeviceDescription = NSLocalizedString("alert.unable-to-sync-with-other-device-description", value: "Unable to Sync with another device.", comment: "Description for unable to sync with another device error") - public static let unableToMergeTwoAccountsErrorDescription = NSLocalizedString("alert.unable-to-merge-two-accounts-description", value: "To pair these devices, turn off Sync & Backup on one device then tap \"Sync with Another Device\" on the other device.", comment: "Description for unable to merge two accounts error") + public static let unableToMergeTwoAccountsErrorDescription = NSLocalizedString("alert.unable-to-merge-two-accounts-description", value: "To pair these devices, turn off Sync & Backup on one device then tap \"Sync With Another Device\" on the other device.", comment: "Description for unable to merge two accounts error") public static let unableToUpdateDeviceNameDescription = NSLocalizedString("alert.unable-to-update-device-name-description", value: "Unable to update the device name.", comment: "Description for unable to update device name error") public static let unableToTurnSyncOffDescription = NSLocalizedString("alert.unable-to-turn-sync-off-description", value: "Unable to turn Sync & Backup off.", comment: "Description for unable to turn sync off error") public static let unableToDeleteDataDescription = NSLocalizedString("alert.unable-to-delete-data-description", value: "Unable to delete data on the server.", comment: "Description for unable to delete data error") diff --git a/DuckDuckGo/en.lproj/Localizable.strings b/DuckDuckGo/en.lproj/Localizable.strings index 34845d2775..8b6c074d19 100644 --- a/DuckDuckGo/en.lproj/Localizable.strings +++ b/DuckDuckGo/en.lproj/Localizable.strings @@ -170,7 +170,7 @@ "alert.unable-to-delete-data-description" = "Unable to delete data on the server."; /* Description for unable to merge two accounts error */ -"alert.unable-to-merge-two-accounts-description" = "To pair these devices, turn off Sync & Backup on one device then tap \"Sync with Another Device\" on the other device."; +"alert.unable-to-merge-two-accounts-description" = "To pair these devices, turn off Sync & Backup on one device then tap \"Sync With Another Device\" on the other device."; /* Description for unable to remove device error */ "alert.unable-to-remove-device-description" = "Unable to remove this device from Sync & Backup."; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/en.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/en.lproj/Localizable.strings index 734189b9b3..68f7f7409b 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/en.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/en.lproj/Localizable.strings @@ -176,10 +176,10 @@ "scan.or.see.code.footer" = "Can’t Scan?"; /* Scan or See Code View - Instruction */ -"scan.or.see.code.instruction" = "Go to Settings › Sync & Backup in the DuckDuckGo Browser on another device and select ”Sync with Another Device.”"; +"scan.or.see.code.instruction" = "Go to Settings › Sync & Backup in the DuckDuckGo Browser on another device and select ”Sync With Another Device.”"; /* Scan or See Code View - Instruction with syncMenuPath */ -"scan.or.see.code.instruction.attributed" = "Go to %@ in the DuckDuckGo Browser on another device and select ”Sync with Another Device.”."; +"scan.or.see.code.instruction.attributed" = "Go to %@ in the DuckDuckGo Browser on another device and select ”Sync With Another Device.”."; /* Scan or See Code View - Manually Enter Code Link */ "scan.or.see.code.manually.enter.code.link" = "Manually Enter Code"; @@ -191,7 +191,7 @@ "scan.or.see.code.scan.code.instructions.title" = "Mobile-to-Mobile?"; /* Scan or See Code View - Share Code Link */ -"scan.or.see.code.share.code.link" = "Share Text Code?"; +"scan.or.see.code.share.code.link" = "Share Text Code"; /* Scan or See Code View - Title */ "scan.or.see.code.title" = "Scan QR Code"; @@ -236,7 +236,7 @@ "synced.devices.section.header" = "Synced Devices"; /* Synced Devices - Sync with Another Device Label */ -"synced.devices.sync.with.another.device.label" = "Sync with Another Device"; +"synced.devices.sync.with.another.device.label" = "Sync With Another Device"; /* Synced Devices - This Device Label */ "synced.devices.this.device.label" = "This Device"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/UserText.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/UserText.swift index 8de7361c7c..b36ab14850 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/UserText.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/UserText.swift @@ -51,7 +51,7 @@ public struct UserText { // Synced Devices static let syncedDevicesSectionHeader = NSLocalizedString("synced.devices.section.header", bundle: Bundle.module, value: "Synced Devices", comment: "Synced Devices - Section Header") static let syncedDevicesThisDeviceLabel = NSLocalizedString("synced.devices.this.device.label", bundle: Bundle.module, value: "This Device", comment: "Synced Devices - This Device Label") - static let syncedDevicesSyncWithAnotherDeviceLabel = NSLocalizedString("synced.devices.sync.with.another.device.label", bundle: Bundle.module, value: "Sync with Another Device", comment: "Synced Devices - Sync with Another Device Label") + static let syncedDevicesSyncWithAnotherDeviceLabel = NSLocalizedString("synced.devices.sync.with.another.device.label", bundle: Bundle.module, value: "Sync With Another Device", comment: "Synced Devices - Sync with Another Device Label") // Options static let optionsSectionHeader = NSLocalizedString("options.section.header", bundle: Bundle.module, value: "Options", comment: "Options - Section Header") static let unifiedFavoritesTitle = NSLocalizedString("unified.favorites.title", bundle: Bundle.module, value: "Unify Favorites Across Devices", comment: "Options - Unify Favorites Title") @@ -119,9 +119,9 @@ public struct UserText { // Scan or See Code View static let scanOrSeeCodeTitle = NSLocalizedString("scan.or.see.code.title", bundle: Bundle.module, value: "Scan QR Code", comment: "Scan or See Code View - Title") - static let scanOrSeeCodeInstruction = NSLocalizedString("scan.or.see.code.instruction", bundle: Bundle.module, value: "Go to Settings › Sync & Backup in the DuckDuckGo Browser on another device and select ”Sync with Another Device.”", comment: "Scan or See Code View - Instruction") + static let scanOrSeeCodeInstruction = NSLocalizedString("scan.or.see.code.instruction", bundle: Bundle.module, value: "Go to Settings › Sync & Backup in the DuckDuckGo Browser on another device and select ”Sync With Another Device.”", comment: "Scan or See Code View - Instruction") static func scanOrSeeCodeInstructionAttributed(syncMenuPath: String) -> String { - let localized = NSLocalizedString("scan.or.see.code.instruction.attributed", bundle: Bundle.module, value: "Go to %@ in the DuckDuckGo Browser on another device and select ”Sync with Another Device.”.", comment: "Scan or See Code View - Instruction with syncMenuPath") + let localized = NSLocalizedString("scan.or.see.code.instruction.attributed", bundle: Bundle.module, value: "Go to %@ in the DuckDuckGo Browser on another device and select ”Sync With Another Device.”.", comment: "Scan or See Code View - Instruction with syncMenuPath") return String(format: localized, syncMenuPath) } @@ -129,7 +129,7 @@ public struct UserText { static let scanOrSeeCodeScanCodeInstructionsTitle = NSLocalizedString("scan.or.see.code.scan.code.instructions.title", bundle: Bundle.module, value: "Mobile-to-Mobile?", comment: "Scan or See Code View - Scan Code Instructions Title") static let scanOrSeeCodeScanCodeInstructionsBody = NSLocalizedString("scan.or.see.code.scan.code.instructions.body", bundle: Bundle.module, value: "Scan this code with another device to sync.", comment: "Scan or See Code View - Scan Code Instructions Body") static let scanOrSeeCodeFooter = NSLocalizedString("scan.or.see.code.footer", bundle: Bundle.module, value: "Can’t Scan?", comment: "Scan or See Code View - Footer") - static let scanOrSeeCodeShareCodeLink = NSLocalizedString("scan.or.see.code.share.code.link", bundle: Bundle.module, value: "Share Text Code?", comment: "Scan or See Code View - Share Code Link") + static let scanOrSeeCodeShareCodeLink = NSLocalizedString("scan.or.see.code.share.code.link", bundle: Bundle.module, value: "Share Text Code", comment: "Scan or See Code View - Share Code Link") // Edit Device View static let editDeviceHeader = NSLocalizedString("edit.device.header", bundle: Bundle.module, value: "Device Name", comment: "Edit Device View - Header") From 9230ce86f04b88ffd87a31df6ad44ef22e9cda1f Mon Sep 17 00:00:00 2001 From: Sabrina Tardio <44158575+SabrinaTardio@users.noreply.github.com> Date: Thu, 18 Jan 2024 10:54:02 +0100 Subject: [PATCH 05/70] adjust sync banner (#2355) Task/Issue URL: https://app.asana.com/0/38424471409662/1206221852119331/f Description: Adjust banner according to comment in task (centre box and non capitalised) --- .../SyncUI/Views/SyncSettingsView.swift | 5 +--- .../Views/SyncSettingsViewExtension.swift | 23 +++++++++---------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsView.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsView.swift index 2deec431f7..fc442ce77c 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsView.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsView.swift @@ -43,10 +43,8 @@ public struct SyncSettingsView: View { } } } else { + rolloutBanner() List { - - rolloutBanner() - if model.isSyncEnabled { syncUnavailableViewWhileLoggedIn() @@ -76,7 +74,6 @@ public struct SyncSettingsView: View { syncWithAnotherDeviceView() otherOptions() - } } .navigationTitle(UserText.syncTitle) diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsViewExtension.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsViewExtension.swift index b1b6556264..b4f5c9e990 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsViewExtension.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsViewExtension.swift @@ -306,19 +306,18 @@ extension SyncSettingsView { @ViewBuilder func rolloutBanner() -> some View { - Section { - EmptyView() - } header: { - HStack(alignment: .top, spacing: 16) { - Image("Info-Color-16") - Text(UserText.syncRollOutBannerDescription) - .font(.system(size: 12)) - .foregroundColor(.primary) - } - .padding() - .background(RoundedRectangle(cornerRadius: 8).foregroundColor(Color("RolloutBannerBackground"))) - .padding(.bottom, 10) + HStack(alignment: .top, spacing: 16) { + Image("Info-Color-16") + Text(UserText.syncRollOutBannerDescription) + .font(.system(size: 12)) + .foregroundColor(.primary) + .multilineTextAlignment(.leading) + .fixedSize(horizontal: false, vertical: true) } + .padding() + .background(RoundedRectangle(cornerRadius: 8).foregroundColor(Color("RolloutBannerBackground"))) + .padding(.bottom, 10) + .padding(.horizontal, 14) } enum LimitedItemType { From 8c25617812c06dd295718025b730458abc132dfd Mon Sep 17 00:00:00 2001 From: Maxim Tsoy Date: Thu, 18 Jan 2024 11:36:46 +0100 Subject: [PATCH 06/70] Bump autoconsent to 9.1.0 (#2344) --- DuckDuckGo/Autoconsent/autoconsent-bundle.js | 2 +- package-lock.json | 8 ++++---- package.json | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/DuckDuckGo/Autoconsent/autoconsent-bundle.js b/DuckDuckGo/Autoconsent/autoconsent-bundle.js index 549a4a732c..a61c1559d1 100644 --- a/DuckDuckGo/Autoconsent/autoconsent-bundle.js +++ b/DuckDuckGo/Autoconsent/autoconsent-bundle.js @@ -1 +1 @@ -!function(){"use strict";var e=class e{static setBase(t){e.base=t}static findElement(t,o=null,c=!1){let i=null;return i=null!=o?Array.from(o.querySelectorAll(t.selector)):null!=e.base?Array.from(e.base.querySelectorAll(t.selector)):Array.from(document.querySelectorAll(t.selector)),null!=t.textFilter&&(i=i.filter((e=>{const o=e.textContent.toLowerCase();if(Array.isArray(t.textFilter)){let e=!1;for(const c of t.textFilter)if(-1!==o.indexOf(c.toLowerCase())){e=!0;break}return e}if(null!=t.textFilter)return-1!==o.indexOf(t.textFilter.toLowerCase())}))),null!=t.styleFilters&&(i=i.filter((e=>{const o=window.getComputedStyle(e);let c=!0;for(const e of t.styleFilters){const t=o[e.option];c=e.negated?c&&t!==e.value:c&&t===e.value}return c}))),null!=t.displayFilter&&(i=i.filter((e=>t.displayFilter?0!==e.offsetHeight:0===e.offsetHeight))),null!=t.iframeFilter&&(i=i.filter((()=>t.iframeFilter?window.location!==window.parent.location:window.location===window.parent.location))),null!=t.childFilter&&(i=i.filter((o=>{const c=e.base;e.setBase(o);const i=e.find(t.childFilter);return e.setBase(c),null!=i.target}))),c?i:(i.length>1&&console.warn("Multiple possible targets: ",i,t,o),i[0])}static find(t,o=!1){const c=[];if(null!=t.parent){const i=e.findElement(t.parent,null,o);if(null!=i){if(i instanceof Array)return i.forEach((i=>{const n=e.findElement(t.target,i,o);n instanceof Array?n.forEach((e=>{c.push({parent:i,target:e})})):c.push({parent:i,target:n})})),c;{const n=e.findElement(t.target,i,o);n instanceof Array?n.forEach((e=>{c.push({parent:i,target:e})})):c.push({parent:i,target:n})}}}else{const i=e.findElement(t.target,null,o);i instanceof Array?i.forEach((e=>{c.push({parent:null,target:e})})):c.push({parent:null,target:i})}return 0===c.length&&c.push({parent:null,target:null}),o?c:(1!==c.length&&console.warn("Multiple results found, even though multiple false",c),c[0])}};e.base=null;var t=e;function o(e){const o=t.find(e);return"css"===e.type?!!o.target:"checkbox"===e.type?!!o.target&&o.target.checked:void 0}async function c(e,a){switch(e.type){case"click":return async function(e){const o=t.find(e);null!=o.target&&o.target.click();return n(i)}(e);case"list":return async function(e,t){for(const o of e.actions)await c(o,t)}(e,a);case"consent":return async function(e,t){for(const i of e.consents){const e=-1!==t.indexOf(i.type);if(i.matcher&&i.toggleAction){o(i.matcher)!==e&&await c(i.toggleAction)}else e?await c(i.trueAction):await c(i.falseAction)}}(e,a);case"ifcss":return async function(e,o){const i=t.find(e);i.target?e.falseAction&&await c(e.falseAction,o):e.trueAction&&await c(e.trueAction,o)}(e,a);case"waitcss":return async function(e){await new Promise((o=>{let c=e.retries||10;const i=e.waitTime||250,n=()=>{const a=t.find(e);(e.negated&&a.target||!e.negated&&!a.target)&&c>0?(c-=1,setTimeout(n,i)):o()};n()}))}(e);case"foreach":return async function(e,o){const i=t.find(e,!0),n=t.base;for(const n of i)n.target&&(t.setBase(n.target),await c(e.action,o));t.setBase(n)}(e,a);case"hide":return async function(e){const o=t.find(e);o.target&&o.target.classList.add("Autoconsent-Hidden")}(e);case"slide":return async function(e){const o=t.find(e),c=t.find(e.dragTarget);if(o.target){const e=o.target.getBoundingClientRect(),t=c.target.getBoundingClientRect();let i=t.top-e.top,n=t.left-e.left;"y"===this.config.axis.toLowerCase()&&(n=0),"x"===this.config.axis.toLowerCase()&&(i=0);const a=window.screenX+e.left+e.width/2,s=window.screenY+e.top+e.height/2,r=e.left+e.width/2,l=e.top+e.height/2,p=document.createEvent("MouseEvents");p.initMouseEvent("mousedown",!0,!0,window,0,a,s,r,l,!1,!1,!1,!1,0,o.target);const d=document.createEvent("MouseEvents");d.initMouseEvent("mousemove",!0,!0,window,0,a+n,s+i,r+n,l+i,!1,!1,!1,!1,0,o.target);const u=document.createEvent("MouseEvents");u.initMouseEvent("mouseup",!0,!0,window,0,a+n,s+i,r+n,l+i,!1,!1,!1,!1,0,o.target),o.target.dispatchEvent(p),await this.waitTimeout(10),o.target.dispatchEvent(d),await this.waitTimeout(10),o.target.dispatchEvent(u)}}(e);case"close":return async function(){window.close()}();case"wait":return async function(e){await n(e.waitTime)}(e);case"eval":return async function(e){return console.log("eval!",e.code),new Promise((t=>{try{e.async?(window.eval(e.code),setTimeout((()=>{t(window.eval("window.__consentCheckResult"))}),e.timeout||250)):t(window.eval(e.code))}catch(o){console.warn("eval error",o,e.code),t(!1)}}))}(e);default:throw"Unknown action type: "+e.type}}var i=0;function n(e){return new Promise((t=>{setTimeout((()=>{t()}),e)}))}function a(e="autoconsent-css-rules"){const t=`style#${e}`,o=document.querySelector(t);if(o&&o instanceof HTMLStyleElement)return o;{const t=document.head||document.getElementsByTagName("head")[0]||document.documentElement,o=document.createElement("style");return o.id=e,t.appendChild(o),o}}function s(e,t,o="display"){const c="opacity"===o?"opacity: 0":"display: none",i=`${t.join(",")} { ${c} !important; z-index: -1 !important; pointer-events: none !important; } `;return e instanceof HTMLStyleElement&&(e.innerText+=i,t.length>0)}async function r(e,t,o){const c=await e();return!c&&t>0?new Promise((c=>{setTimeout((async()=>{c(r(e,t-1,o))}),o)})):Promise.resolve(c)}function l(e){if(!e)return!1;if(null!==e.offsetParent)return!0;{const t=window.getComputedStyle(e);if("fixed"===t.position&&"none"!==t.display)return!0}return!1}function p(e,t=!1){const o=g(e);return o.length>0&&(t?o.forEach((e=>e.click())):o[0].click()),o.length>0}function d(e){return g(e).length>0}function u(e,t){const o=g(e),c=new Array(o.length);return o.forEach(((e,t)=>{c[t]=l(e)})),"none"===t?c.every((e=>!e)):0!==c.length&&("any"===t?c.some((e=>e)):c.every((e=>e)))}function m(e,t=1e4){return r((()=>g(e).length>0),Math.ceil(t/200),200)}function h(e,t=1e4,o="any"){return r((()=>u(e,o)),Math.ceil(t/200),200)}async function k(e,t=1e4,o=!1){return await m(e,t),p(e,o)}function b(e){return new Promise((t=>{setTimeout((()=>{t(!0)}),e)}))}function _(e,t=document){if(e.startsWith("aria/"))return[];if(e.startsWith("xpath/")){const o=e.slice(6),c=document.evaluate(o,t,null,XPathResult.ANY_TYPE,null);let i=null;const n=[];for(;i=c.iterateNext();)n.push(i);return n}return e.startsWith("text/")||e.startsWith("pierce/")?[]:t.shadowRoot?Array.from(t.shadowRoot.querySelectorAll(e)):Array.from(t.querySelectorAll(e))}function g(e){return"string"==typeof e?_(e):function(e){let t,o=document;for(const c of e){if(t=_(c,o),0===t.length)return[];o=t[0]}return t}(e)}function y(){return crypto&&void 0!==crypto.randomUUID?crypto.randomUUID():Math.random().toString()}var w=class{constructor(e,t=1e3){this.id=e,this.promise=new Promise(((e,t)=>{this.resolve=e,this.reject=t})),this.timer=window.setTimeout((()=>{this.reject(new Error("timeout"))}),t)}},C={pending:new Map,sendContentMessage:null};var v={EVAL_0:()=>console.log(1),EVAL_CONSENTMANAGER_1:()=>window.__cmp&&"object"==typeof __cmp("getCMPData"),EVAL_CONSENTMANAGER_2:()=>!__cmp("consentStatus").userChoiceExists,EVAL_CONSENTMANAGER_3:()=>__cmp("setConsent",0),EVAL_CONSENTMANAGER_4:()=>__cmp("setConsent",1),EVAL_CONSENTMANAGER_5:()=>__cmp("consentStatus").userChoiceExists,EVAL_COOKIEBOT_1:()=>!0!==window.CookieConsent.hasResponse,EVAL_COOKIEBOT_2:()=>window.Cookiebot.dialog.submitConsent(),EVAL_COOKIEBOT_3:()=>endCookieProcess(),EVAL_COOKIEBOT_4:()=>!0===window.CookieConsent.declined,EVAL_KLARO_1:()=>klaro.getManager().config.services.every((e=>e.required||!klaro.getManager().consents[e.name])),EVAL_ONETRUST_1:()=>window.OnetrustActiveGroups.split(",").filter((e=>e.length>0)).length<=1,EVAL_TRUSTARC_TOP:()=>window&&window.truste&&"0"===window.truste.eu.bindMap.prefCookie,EVAL_ADROLL_0:()=>!document.cookie.includes("__adroll_fpc"),EVAL_ALMACMP_0:()=>document.cookie.includes('"name":"Google","consent":false'),EVAL_AFFINITY_SERIF_COM_0:()=>document.cookie.includes("serif_manage_cookies_viewed")&&!document.cookie.includes("serif_allow_analytics"),EVAL_AXEPTIO_0:()=>document.cookie.includes("axeptio_authorized_vendors=%2C%2C"),EVAL_BING_0:()=>document.cookie.includes("AL=0")&&document.cookie.includes("AD=0")&&document.cookie.includes("SM=0"),EVAL_BORLABS_0:()=>!JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>-1!==e.indexOf("borlabs-cookie"))).split("=",2)[1])).consents.statistics,EVAL_BUNDESREGIERUNG_DE_0:()=>document.cookie.match("cookie-allow-tracking=0"),EVAL_CANVA_0:()=>!document.cookie.includes("gtm_fpc_engagement_event"),EVAL_CC_BANNER2_0:()=>!!document.cookie.match(/sncc=[^;]+D%3Dtrue/),EVAL_CLICKIO_0:()=>document.cookie.includes("__lxG__consent__v2_daisybit="),EVAL_CLINCH_0:()=>document.cookie.includes("ctc_rejected=1"),EVAL_COINBASE_0:()=>JSON.parse(decodeURIComponent(document.cookie.match(/cm_(eu|default)_preferences=([0-9a-zA-Z\\{\\}\\[\\]%:]*);?/)[2])).consent.length<=1,EVAL_COMPLIANZ_BANNER_0:()=>document.cookie.includes("cmplz_banner-status=dismissed"),EVAL_COMPLIANZ_CATEGORIES_0:()=>!!document.cookie.match(/cmplz_[^=]+=deny/),EVAL_COMPLIANZ_OPTIN_0:()=>!!document.cookie.match(/cookieconsent_preferences_disabled=[^;]+/),EVAL_COOKIE_LAW_INFO_0:()=>CLI.disableAllCookies()||CLI.reject_close()||!0,EVAL_COOKIE_LAW_INFO_1:()=>-1===document.cookie.indexOf("cookielawinfo-checkbox-non-necessary=yes"),EVAL_COOKIE_MANAGER_POPUP_0:()=>!1===JSON.parse(document.cookie.split(";").find((e=>e.trim().startsWith("CookieLevel"))).split("=")[1]).social,EVAL_COOKIEALERT_0:()=>document.querySelector("body").removeAttribute("style")||!0,EVAL_COOKIEALERT_1:()=>document.querySelector("body").removeAttribute("style")||!0,EVAL_COOKIEALERT_2:()=>!0===window.CookieConsent.declined,EVAL_COOKIEFIRST_0:()=>{return!1===(e=JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>-1!==e.indexOf("cookiefirst"))).trim()).split("=")[1])).performance&&!1===e.functional&&!1===e.advertising;var e},EVAL_COOKIEFIRST_1:()=>document.querySelectorAll("button[data-cookiefirst-accent-color=true][role=checkbox]:not([disabled])").forEach((e=>"true"==e.getAttribute("aria-checked")&&e.click()))||!0,EVAL_COOKIEINFORMATION_0:()=>CookieInformation.declineAllCategories()||!0,EVAL_COOKIEINFORMATION_1:()=>CookieInformation.submitAllCategories()||!0,EVAL_COOKIEINFORMATION_2:()=>document.cookie.includes("CookieInformationConsent="),EVAL_DAILYMOTION_0:()=>!!document.cookie.match("dm-euconsent-v2"),EVAL_DSGVO_0:()=>!document.cookie.includes("sp_dsgvo_cookie_settings"),EVAL_DUNELM_0:()=>document.cookie.includes("cc_functional=0")&&document.cookie.includes("cc_targeting=0"),EVAL_ETSY_0:()=>document.querySelectorAll(".gdpr-overlay-body input").forEach((e=>{e.checked=!1}))||!0,EVAL_ETSY_1:()=>document.querySelector(".gdpr-overlay-view button[data-wt-overlay-close]").click()||!0,EVAL_EU_COOKIE_COMPLIANCE_0:()=>-1===document.cookie.indexOf("cookie-agreed=2"),EVAL_EU_COOKIE_LAW_0:()=>!document.cookie.includes("euCookie"),EVAL_EZOIC_0:()=>ezCMP.handleAcceptAllClick(),EVAL_EZOIC_1:()=>!!document.cookie.match(/ezCMPCookieConsent=[^;]+\|2=0\|3=0\|4=0/),EVAL_GOOGLE_0:()=>!!document.cookie.match(/SOCS=CAE/),EVAL_IUBENDA_0:()=>document.querySelectorAll(".purposes-item input[type=checkbox]:not([disabled])").forEach((e=>{e.checked&&e.click()}))||!0,EVAL_IUBENDA_1:()=>!!document.cookie.match(/_iub_cs-\d+=/),EVAL_JQUERY_COOKIEBAR_0:()=>!document.cookie.includes("cookies-state=accepted"),EVAL_MEDIAVINE_0:()=>document.querySelectorAll('[data-name="mediavine-gdpr-cmp"] input[type=checkbox]').forEach((e=>e.checked&&e.click()))||!0,EVAL_MICROSOFT_0:()=>Array.from(document.querySelectorAll("div > button")).filter((e=>e.innerText.match("Reject|Ablehnen")))[0].click()||!0,EVAL_MICROSOFT_1:()=>Array.from(document.querySelectorAll("div > button")).filter((e=>e.innerText.match("Accept|Annehmen")))[0].click()||!0,EVAL_MICROSOFT_2:()=>!!document.cookie.match("MSCC"),EVAL_MOOVE_0:()=>document.querySelectorAll("#moove_gdpr_cookie_modal input").forEach((e=>{e.disabled||"moove_gdpr_strict_cookies"===e.name||(e.checked=!1)}))||!0,EVAL_ONENINETWO_0:()=>document.cookie.includes("CC_ADVERTISING=NO")&&document.cookie.includes("CC_ANALYTICS=NO"),EVAL_PAYPAL_0:()=>!0===document.cookie.includes("cookie_prefs"),EVAL_PRIMEBOX_0:()=>!document.cookie.includes("cb-enabled=accepted"),EVAL_PUBTECH_0:()=>document.cookie.includes("euconsent-v2")&&(document.cookie.match(/.YAAAAAAAAAAA/)||document.cookie.match(/.aAAAAAAAAAAA/)||document.cookie.match(/.YAAACFgAAAAA/)),EVAL_REDDIT_0:()=>document.cookie.includes("eu_cookie={%22opted%22:true%2C%22nonessential%22:false}"),EVAL_SIBBO_0:()=>!!window.localStorage.getItem("euconsent-v2"),EVAL_SIRDATA_0:()=>document.cookie.includes("euconsent-v2"),EVAL_SNIGEL_0:()=>!!document.cookie.match("snconsent"),EVAL_STEAMPOWERED_0:()=>2===JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>e.trim().startsWith("cookieSettings"))).split("=")[1])).preference_state,EVAL_TAKEALOT_0:()=>document.body.classList.remove("freeze")||(document.body.style="")||!0,EVAL_TARTEAUCITRON_0:()=>tarteaucitron.userInterface.respondAll(!1)||!0,EVAL_TARTEAUCITRON_1:()=>tarteaucitron.userInterface.respondAll(!0)||!0,EVAL_TARTEAUCITRON_2:()=>document.cookie.match(/tarteaucitron=[^;]*/)[0].includes("false"),EVAL_TEALIUM_0:()=>void 0!==window.utag&&"object"==typeof utag.gdpr,EVAL_TEALIUM_1:()=>utag.gdpr.setConsentValue(!1)||!0,EVAL_TEALIUM_2:()=>utag.gdpr.setConsentValue(!0)||!0,EVAL_TEALIUM_3:()=>1!==utag.gdpr.getConsentState(),EVAL_TESTCMP_0:()=>"button_clicked"===window.results.results[0],EVAL_TESTCMP_COSMETIC_0:()=>"banner_hidden"===window.results.results[0],EVAL_THEFREEDICTIONARY_0:()=>cmpUi.showPurposes()||cmpUi.rejectAll()||!0,EVAL_THEFREEDICTIONARY_1:()=>cmpUi.allowAll()||!0,EVAL_THEVERGE_0:()=>document.cookie.includes("_duet_gdpr_acknowledged=1"),EVAL_UBUNTU_COM_0:()=>"_cookies_accepted=essential"===document.cookie,EVAL_UK_COOKIE_CONSENT_0:()=>!document.cookie.includes("catAccCookies"),EVAL_USERCENTRICS_API_0:()=>"object"==typeof UC_UI,EVAL_USERCENTRICS_API_1:()=>!!UC_UI.closeCMP(),EVAL_USERCENTRICS_API_2:()=>!!UC_UI.denyAllConsents(),EVAL_USERCENTRICS_API_3:()=>!!UC_UI.acceptAllConsents(),EVAL_USERCENTRICS_API_4:()=>!!UC_UI.closeCMP(),EVAL_USERCENTRICS_API_5:()=>!0===UC_UI.areAllConsentsAccepted(),EVAL_USERCENTRICS_API_6:()=>!1===UC_UI.areAllConsentsAccepted(),EVAL_USERCENTRICS_BUTTON_0:()=>JSON.parse(localStorage.getItem("usercentrics")).consents.every((e=>e.isEssential||!e.consentStatus)),EVAL_WAITROSE_0:()=>Array.from(document.querySelectorAll("label[id$=cookies-deny-label]")).forEach((e=>e.click()))||!0,EVAL_WAITROSE_1:()=>document.cookie.includes("wtr_cookies_advertising=0")&&document.cookie.includes("wtr_cookies_analytics=0"),EVAL_WP_COOKIE_NOTICE_0:()=>document.cookie.includes("wpl_viewed_cookie=no"),EVAL_XING_0:()=>document.cookie.includes("userConsent=%7B%22marketing%22%3Afalse"),EVAL_YOUTUBE_DESKTOP_0:()=>!!document.cookie.match(/SOCS=CAE/),EVAL_YOUTUBE_MOBILE_0:()=>!!document.cookie.match(/SOCS=CAE/)};var f={main:!0,frame:!1,urlPattern:""},A=class{constructor(e){this.runContext=f,this.autoconsent=e}get hasSelfTest(){throw new Error("Not Implemented")}get isIntermediate(){throw new Error("Not Implemented")}get isCosmetic(){throw new Error("Not Implemented")}mainWorldEval(e){const t=v[e];if(!t)return console.warn("Snippet not found",e),Promise.resolve(!1);if(this.autoconsent.config.isMainWorld){let e=!1;try{e=!!t.call(globalThis)}catch(e){}return Promise.resolve(e)}return function(e){const t=y();C.sendContentMessage({type:"eval",id:t,code:e});const o=new w(t);return C.pending.set(o.id,o),o.promise}(function(e){const t=e.toString();return t.substring(t.indexOf("=>")+2)}(t)).catch((e=>!1))}checkRunContext(){const e={...f,...this.runContext},t=window.top===window;return!(t&&!e.main)&&(!(!t&&!e.frame)&&!(e.urlPattern&&!window.location.href.match(e.urlPattern)))}detectCmp(){throw new Error("Not Implemented")}async detectPopup(){return!1}optOut(){throw new Error("Not Implemented")}optIn(){throw new Error("Not Implemented")}openCmp(){throw new Error("Not Implemented")}async test(){return Promise.resolve(!0)}},E=class extends A{constructor(e,t){super(t),this.config=e,this.name=e.name,this.runContext=e.runContext||f}get hasSelfTest(){return!!this.config.test}get isIntermediate(){return!!this.config.intermediate}get isCosmetic(){return!!this.config.cosmetic}get prehideSelectors(){return this.config.prehideSelectors}async detectCmp(){return!!this.config.detectCmp&&this._runRulesParallel(this.config.detectCmp)}async detectPopup(){return!!this.config.detectPopup&&this._runRulesSequentially(this.config.detectPopup)}async optOut(){return!!this.config.optOut&&this._runRulesSequentially(this.config.optOut)}async optIn(){return!!this.config.optIn&&this._runRulesSequentially(this.config.optIn)}async openCmp(){return!!this.config.openCmp&&this._runRulesSequentially(this.config.openCmp)}async test(){return this.hasSelfTest?this._runRulesSequentially(this.config.test):super.test()}async evaluateRuleStep(e){const t=[];if(e.exists&&t.push(d(e.exists)),e.visible&&t.push(u(e.visible,e.check)),e.eval){const o=this.mainWorldEval(e.eval);t.push(o)}var o,c;if(e.waitFor&&t.push(m(e.waitFor,e.timeout)),e.waitForVisible&&t.push(h(e.waitForVisible,e.timeout,e.check)),e.click&&t.push(p(e.click,e.all)),e.waitForThenClick&&t.push(k(e.waitForThenClick,e.timeout,e.all)),e.wait&&t.push(b(e.wait)),e.hide&&t.push((o=e.hide,c=e.method,s(a(),o,c))),e.if){if(!e.if.exists&&!e.if.visible)return console.error("invalid conditional rule",e.if),!1;await this.evaluateRuleStep(e.if)?t.push(this._runRulesSequentially(e.then)):e.else&&t.push(this._runRulesSequentially(e.else))}if(e.any){for(const t of e.any)if(await this.evaluateRuleStep(t))return!0;return!1}if(0===t.length)return!1;return(await Promise.all(t)).reduce(((e,t)=>e&&t),!0)}async _runRulesParallel(e){const t=e.map((e=>this.evaluateRuleStep(e)));return(await Promise.all(t)).every((e=>!!e))}async _runRulesSequentially(e){for(const t of e){if(!await this.evaluateRuleStep(t)&&!t.optional)return!1}return!0}},O=class{constructor(e,t){this.name=e,this.config=t,this.methods=new Map,this.runContext=f,this.isCosmetic=!1,t.methods.forEach((e=>{e.action&&this.methods.set(e.name,e.action)})),this.hasSelfTest=!1}get isIntermediate(){return!1}checkRunContext(){return!0}async detectCmp(){return this.config.detectors.map((e=>o(e.presentMatcher))).some((e=>!!e))}async detectPopup(){return this.config.detectors.map((e=>o(e.showingMatcher))).some((e=>!!e))}async executeAction(e,t){return!this.methods.has(e)||c(this.methods.get(e),t)}async optOut(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),await this.executeAction("HIDE_CMP"),await this.executeAction("DO_CONSENT",[]),await this.executeAction("SAVE_CONSENT"),!0}async optIn(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),await this.executeAction("HIDE_CMP"),await this.executeAction("DO_CONSENT",["D","A","B","E","F","X"]),await this.executeAction("SAVE_CONSENT"),!0}async openCmp(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),!0}async test(){return!0}},x="#truste-show-consent",S="#truste-consent-track",I=[class extends A{constructor(e){super(e),this.name="TrustArc-top",this.prehideSelectors=[".trustarc-banner-container",`.truste_popframe,.truste_overlay,.truste_box_overlay,${S}`],this.runContext={main:!0,frame:!1},this._shortcutButton=null,this._optInDone=!1}get hasSelfTest(){return!1}get isIntermediate(){return!this._optInDone&&!this._shortcutButton}get isCosmetic(){return!1}async detectCmp(){const e=d(`${x},${S}`);return e&&(this._shortcutButton=document.querySelector("#truste-consent-required")),e}async detectPopup(){return u(`#truste-consent-content,#trustarc-banner-overlay,${S}`,"all")}openFrame(){p(x)}async optOut(){return this._shortcutButton?(this._shortcutButton.click(),!0):(s(a(),[".truste_popframe",".truste_overlay",".truste_box_overlay",S]),p(x),setTimeout((()=>{a().remove()}),1e4),!0)}async optIn(){return this._optInDone=!0,p("#truste-consent-button")}async openCmp(){return!0}async test(){return await this.mainWorldEval("EVAL_TRUSTARC_TOP")}},class extends A{constructor(){super(...arguments),this.name="TrustArc-frame",this.runContext={main:!1,frame:!0,urlPattern:"^https://consent-pref\\.trustarc\\.com/\\?"}}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return!0}async detectPopup(){return u("#defaultpreferencemanager","any")&&u(".mainContent","any")}async navigateToSettings(){return await r((async()=>d(".shp")||u(".advance","any")||d(".switch span:first-child")),10,500),d(".shp")&&p(".shp"),await m(".prefPanel",5e3),u(".advance","any")&&p(".advance"),await r((()=>u(".switch span:first-child","any")),5,1e3)}async optOut(){return await r((()=>"complete"===document.readyState),20,100),await m(".mainContent[aria-hidden=false]",5e3),!!p(".rejectAll")||(d(".prefPanel")&&await m('.prefPanel[style="visibility: visible;"]',3e3),p("#catDetails0")?(p(".submit"),waitForThenClick("#gwt-debug-close_id",5e3),!0):p(".required")?(waitForThenClick("#gwt-debug-close_id",5e3),!0):(await this.navigateToSettings(),p(".switch span:nth-child(1):not(.active)",!0),p(".submit"),waitForThenClick("#gwt-debug-close_id",3e5),!0))}async optIn(){return p(".call")||(await this.navigateToSettings(),p(".switch span:nth-child(2)",!0),p(".submit"),m("#gwt-debug-close_id",3e5).then((()=>{p("#gwt-debug-close_id")}))),!0}},class extends A{constructor(){super(...arguments),this.name="Cybotcookiebot",this.prehideSelectors=["#CybotCookiebotDialog,#dtcookie-container,#cookiebanner,#cb-cookieoverlay"]}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return d("#CybotCookiebotDialogBodyLevelButtonPreferences")}async detectPopup(){return d("#CybotCookiebotDialog,#dtcookie-container,#cookiebanner,#cb-cookiebanner")}async optOut(){return p(".cookie-alert-extended-detail-link")?(await m(".cookie-alert-configuration",2e3),p(".cookie-alert-configuration-input:checked",!0),p(".cookie-alert-extended-button-secondary"),!0):d("#dtcookie-container")?p(".h-dtcookie-decline"):(p(".cookiebot__button--settings")||p("#CybotCookiebotDialogBodyButtonDecline")||(p(".cookiebanner__link--details"),p('.CybotCookiebotDialogBodyLevelButton:checked:enabled,input[id*="CybotCookiebotDialogBodyLevelButton"]:checked:enabled',!0),p("#CybotCookiebotDialogBodyButtonDecline"),p("input[id^=CybotCookiebotDialogBodyLevelButton]:checked",!0),d("#CybotCookiebotDialogBodyButtonAcceptSelected")?p("#CybotCookiebotDialogBodyButtonAcceptSelected"):p("#CybotCookiebotDialogBodyLevelButtonAccept,#CybotCookiebotDialogBodyButtonAccept,#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowallSelection",!0),await this.mainWorldEval("EVAL_COOKIEBOT_1")&&(await this.mainWorldEval("EVAL_COOKIEBOT_2"),await b(500)),d("#cb-confirmedSettings")&&await this.mainWorldEval("EVAL_COOKIEBOT_3")),!0)}async optIn(){return d("#dtcookie-container")?p(".h-dtcookie-accept"):(p(".CybotCookiebotDialogBodyLevelButton:not(:checked):enabled",!0),p("#CybotCookiebotDialogBodyLevelButtonAccept"),p("#CybotCookiebotDialogBodyButtonAccept"),!0)}async test(){return this.mainWorldEval("EVAL_COOKIEBOT_4")}},class extends A{constructor(){super(...arguments),this.name="Sourcepoint-frame",this.prehideSelectors=["div[id^='sp_message_container_'],.message-overlay","#sp_privacy_manager_container"],this.ccpaNotice=!1,this.ccpaPopup=!1,this.runContext={main:!1,frame:!0}}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){const e=new URL(location.href);return e.searchParams.has("message_id")&&"ccpa-notice.sp-prod.net"===e.hostname?(this.ccpaNotice=!0,!0):"ccpa-pm.sp-prod.net"===e.hostname?(this.ccpaPopup=!0,!0):("/index.html"===e.pathname||"/privacy-manager/index.html"===e.pathname)&&(e.searchParams.has("message_id")||e.searchParams.has("requestUUID")||e.searchParams.has("consentUUID"))}async detectPopup(){return!!this.ccpaNotice||(this.ccpaPopup?await m(".priv-save-btn",2e3):(await m(".sp_choice_type_11,.sp_choice_type_12,.sp_choice_type_13,.sp_choice_type_ACCEPT_ALL",2e3),!d(".sp_choice_type_9")))}async optIn(){return await m(".sp_choice_type_11,.sp_choice_type_ACCEPT_ALL",2e3),!!p(".sp_choice_type_11")||!!p(".sp_choice_type_ACCEPT_ALL")}isManagerOpen(){return"/privacy-manager/index.html"===location.pathname}async optOut(){if(this.ccpaPopup){const e=document.querySelectorAll(".priv-purpose-container .sp-switch-arrow-block a.neutral.on .right");for(const t of e)t.click();const t=document.querySelectorAll(".priv-purpose-container .sp-switch-arrow-block a.switch-bg.on");for(const e of t)e.click();return p(".priv-save-btn")}if(!this.isManagerOpen()){if(!await m(".sp_choice_type_12,.sp_choice_type_13"))return!1;if(!d(".sp_choice_type_12"))return p(".sp_choice_type_13");p(".sp_choice_type_12"),await r((()=>this.isManagerOpen()),200,100)}await m(".type-modal",2e4);try{const e=".sp_choice_type_REJECT_ALL",t=".reject-toggle",o=await Promise.race([m(e,2e3).then((e=>e?0:-1)),m(t,2e3).then((e=>e?1:-1)),m(".pm-features",2e3).then((e=>e?2:-1))]);if(0===o)return await b(1e3),p(e);1===o?p(t):2===o&&(await m(".pm-features",1e4),p(".checked > span",!0),p(".chevron"))}catch(e){}return p(".sp_choice_type_SAVE_AND_EXIT")}},class extends A{constructor(){super(...arguments),this.name="consentmanager.net",this.prehideSelectors=["#cmpbox,#cmpbox2"],this.apiAvailable=!1}get hasSelfTest(){return this.apiAvailable}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.apiAvailable=await this.mainWorldEval("EVAL_CONSENTMANAGER_1"),!!this.apiAvailable||d("#cmpbox")}async detectPopup(){return this.apiAvailable?(await b(500),await this.mainWorldEval("EVAL_CONSENTMANAGER_2")):u("#cmpbox .cmpmore","any")}async optOut(){return await b(500),this.apiAvailable?await this.mainWorldEval("EVAL_CONSENTMANAGER_3"):!!p(".cmpboxbtnno")||(d(".cmpwelcomeprpsbtn")?(p(".cmpwelcomeprpsbtn > a[aria-checked=true]",!0),p(".cmpboxbtnsave"),!0):(p(".cmpboxbtncustom"),await m(".cmptblbox",2e3),p(".cmptdchoice > a[aria-checked=true]",!0),p(".cmpboxbtnyescustomchoices"),!0))}async optIn(){return this.apiAvailable?await this.mainWorldEval("EVAL_CONSENTMANAGER_4"):p(".cmpboxbtnyes")}async test(){if(this.apiAvailable)return await this.mainWorldEval("EVAL_CONSENTMANAGER_5")}},class extends A{constructor(){super(...arguments),this.name="Evidon"}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return d("#_evidon_banner")}async detectPopup(){return u("#_evidon_banner","any")}async optOut(){return p("#_evidon-decline-button")||(s(a(),["#evidon-prefdiag-overlay","#evidon-prefdiag-background"]),p("#_evidon-option-button"),await m("#evidon-prefdiag-overlay",5e3),p("#evidon-prefdiag-decline")),!0}async optIn(){return p("#_evidon-accept-button")}},class extends A{constructor(){super(...arguments),this.name="Onetrust",this.prehideSelectors=["#onetrust-banner-sdk,#onetrust-consent-sdk,.onetrust-pc-dark-filter,.js-consent-banner"],this.runContext={urlPattern:"^(?!.*https://www\\.nba\\.com/)"}}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return d("#onetrust-banner-sdk")}async detectPopup(){return u("#onetrust-banner-sdk","all")}async optOut(){return u("#onetrust-reject-all-handler,.js-reject-cookies","any")?p("#onetrust-reject-all-handler,.js-reject-cookies"):(d("#onetrust-pc-btn-handler")?p("#onetrust-pc-btn-handler"):p(".ot-sdk-show-settings,button.js-cookie-settings"),await m("#onetrust-consent-sdk",2e3),await b(1e3),p("#onetrust-consent-sdk input.category-switch-handler:checked,.js-editor-toggle-state:checked",!0),await b(1e3),await m(".save-preference-btn-handler,.js-consent-save",2e3),p(".save-preference-btn-handler,.js-consent-save"),await h("#onetrust-banner-sdk",5e3,"none"),!0)}async optIn(){return p("#onetrust-accept-btn-handler,.js-accept-cookies")}async test(){return await r((()=>this.mainWorldEval("EVAL_ONETRUST_1")),10,500)}},class extends A{constructor(){super(...arguments),this.name="Klaro",this.prehideSelectors=[".klaro"],this.settingsOpen=!1}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return d(".klaro > .cookie-modal")?(this.settingsOpen=!0,!0):d(".klaro > .cookie-notice")}async detectPopup(){return u(".klaro > .cookie-notice,.klaro > .cookie-modal","any")}async optOut(){return!!p(".klaro .cn-decline")||(this.settingsOpen||(p(".klaro .cn-learn-more"),await m(".klaro > .cookie-modal",2e3),this.settingsOpen=!0),!!p(".klaro .cn-decline")||(p(".cm-purpose:not(.cm-toggle-all) > input:not(.half-checked)",!0),p(".cm-btn-accept")))}async optIn(){return!!p(".klaro .cm-btn-accept-all")||(this.settingsOpen?(p(".cm-purpose:not(.cm-toggle-all) > input.half-checked",!0),p(".cm-btn-accept")):p(".klaro .cookie-notice .cm-btn-success"))}async test(){return await this.mainWorldEval("EVAL_KLARO_1")}},class extends A{constructor(){super(...arguments),this.name="Uniconsent"}get prehideSelectors(){return[".unic",".modal:has(.unic)"]}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return d(".unic .unic-box,.unic .unic-bar")}async detectPopup(){return u(".unic .unic-box,.unic .unic-bar","any")}async optOut(){if(await m(".unic button",1e3),document.querySelectorAll(".unic button").forEach((e=>{const t=e.textContent;(t.includes("Manage Options")||t.includes("Optionen verwalten"))&&e.click()})),await m(".unic input[type=checkbox]",1e3)){await m(".unic button",1e3),document.querySelectorAll(".unic input[type=checkbox]").forEach((e=>{e.checked&&e.click()}));for(const e of document.querySelectorAll(".unic button")){const t=e.textContent;for(const o of["Confirm Choices","Save Choices","Auswahl speichern"])if(t.includes(o))return e.click(),await b(500),!0}}return!1}async optIn(){return k(".unic #unic-agree")}async test(){await b(1e3);return!d(".unic .unic-box,.unic .unic-bar")}},class extends A{constructor(){super(...arguments),this.prehideSelectors=[".cmp-root"],this.name="Conversant"}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return d(".cmp-root .cmp-receptacle")}async detectPopup(){return u(".cmp-root .cmp-receptacle","any")}async optOut(){if(!await k(".cmp-main-button:not(.cmp-main-button--primary)"))return!1;if(!await m(".cmp-view-tab-tabs"))return!1;await k(".cmp-view-tab-tabs > :first-child"),await k(".cmp-view-tab-tabs > .cmp-view-tab--active:first-child");for(const e of Array.from(document.querySelectorAll(".cmp-accordion-item"))){e.querySelector(".cmp-accordion-item-title").click(),await r((()=>!!e.querySelector(".cmp-accordion-item-content.cmp-active")),10,50);const t=e.querySelector(".cmp-accordion-item-content.cmp-active");t.querySelectorAll(".cmp-toggle-actions .cmp-toggle-deny:not(.cmp-toggle-deny--active)").forEach((e=>e.click())),t.querySelectorAll(".cmp-toggle-actions .cmp-toggle-checkbox:not(.cmp-toggle-checkbox--active)").forEach((e=>e.click()))}return await p(".cmp-main-button:not(.cmp-main-button--primary)"),!0}async optIn(){return k(".cmp-main-button.cmp-main-button--primary")}async test(){return document.cookie.includes("cmp-data=0")}},class extends A{constructor(){super(...arguments),this.name="tiktok.com",this.runContext={urlPattern:"tiktok"}}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}getShadowRoot(){const e=document.querySelector("tiktok-cookie-banner");return e?e.shadowRoot:null}async detectCmp(){return d("tiktok-cookie-banner")}async detectPopup(){return l(this.getShadowRoot().querySelector(".tiktok-cookie-banner"))}async optOut(){const e=this.getShadowRoot().querySelector(".button-wrapper button:first-child");return!!e&&(e.click(),!0)}async optIn(){const e=this.getShadowRoot().querySelector(".button-wrapper button:last-child");return!!e&&(e.click(),!0)}async test(){const e=document.cookie.match(/cookie-consent=([^;]+)/);if(!e)return!1;const t=JSON.parse(decodeURIComponent(e[1]));return Object.values(t).every((e=>"boolean"!=typeof e||!1===e))}},class extends A{constructor(){super(...arguments),this.runContext={urlPattern:"^https://(www\\.)?airbnb\\.[^/]+/"},this.prehideSelectors=["div[data-testid=main-cookies-banner-container]",'div:has(> div:first-child):has(> div:last-child):has(> section [data-testid="strictly-necessary-cookies"])']}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return d("div[data-testid=main-cookies-banner-container]")}async detectPopup(){return u("div[data-testid=main-cookies-banner-container","any")}async optOut(){let e;for(await k("div[data-testid=main-cookies-banner-container] button._snbhip0");e=document.querySelector("[data-testid=modal-container] button[aria-checked=true]:not([disabled])");)e.click();return k("button[data-testid=save-btn]")}async optIn(){return k("div[data-testid=main-cookies-banner-container] button._148dgdpk")}async test(){return await r((()=>!!document.cookie.match("OptanonAlertBoxClosed")),20,200)}}];var P=[{name:"192.com",detectCmp:[{exists:".ont-cookies"}],detectPopup:[{visible:".ont-cookies"}],optIn:[{click:".ont-btn-main.ont-cookies-btn.js-ont-btn-ok2"}],optOut:[{click:".ont-cookes-btn-manage"},{click:".ont-btn-main.ont-cookies-btn.js-ont-btn-choose"}],test:[{eval:"EVAL_ONENINETWO_0"}]},{name:"1password-com",cosmetic:!0,prehideSelectors:['footer #footer-root [aria-label="Cookie Consent"]'],detectCmp:[{exists:'footer #footer-root [aria-label="Cookie Consent"]'}],detectPopup:[{visible:'footer #footer-root [aria-label="Cookie Consent"]'}],optIn:[{click:'footer #footer-root [aria-label="Cookie Consent"] button'}],optOut:[{hide:['footer #footer-root [aria-label="Cookie Consent"]']}]},{name:"activobank.pt",runContext:{urlPattern:"^https://(www\\.)?activobank\\.pt"},prehideSelectors:["aside#cookies,.overlay-cookies"],detectCmp:[{exists:"#cookies .cookies-btn"}],detectPopup:[{visible:"#cookies #submitCookies"}],optIn:[{waitForThenClick:"#cookies #submitCookies"}],optOut:[{waitForThenClick:"#cookies #rejectCookies"}]},{name:"Adroll",prehideSelectors:["#adroll_consent_container"],detectCmp:[{exists:"#adroll_consent_container"}],detectPopup:[{visible:"#adroll_consent_container"}],optIn:[{waitForThenClick:"#adroll_consent_accept"}],optOut:[{waitForThenClick:"#adroll_consent_reject"}],test:[{eval:"EVAL_ADROLL_0"}]},{name:"affinity.serif.com",detectCmp:[{exists:".c-cookie-banner button[data-qa='allow-all-cookies']"}],detectPopup:[{visible:".c-cookie-banner"}],optIn:[{click:'button[data-qa="allow-all-cookies"]'}],optOut:[{click:'button[data-qa="manage-cookies"]'},{waitFor:'.c-cookie-banner ~ [role="dialog"]'},{waitForThenClick:'.c-cookie-banner ~ [role="dialog"] input[type="checkbox"][value="true"]',all:!0},{click:'.c-cookie-banner ~ [role="dialog"] .c-modal__action button'}],test:[{wait:500},{eval:"EVAL_AFFINITY_SERIF_COM_0"}]},{name:"agolde.com",cosmetic:!0,prehideSelectors:["#modal-1 div[data-micromodal-close]"],detectCmp:[{exists:"#modal-1 div[aria-labelledby=modal-1-title]"}],detectPopup:[{exists:"#modal-1 div[data-micromodal-close]"}],optIn:[{click:'button[aria-label="Close modal"]'}],optOut:[{hide:["#modal-1 div[data-micromodal-close]"]}]},{name:"almacmp",prehideSelectors:["#alma-cmpv2-container"],detectCmp:[{exists:"#alma-cmpv2-container"}],detectPopup:[{visible:"#alma-cmpv2-container #almacmp-modal-layer1"}],optIn:[{waitForThenClick:"#alma-cmpv2-container #almacmp-modal-layer1 #almacmp-modalConfirmBtn"}],optOut:[{waitForThenClick:"#alma-cmpv2-container #almacmp-modal-layer1 #almacmp-modalSettingBtn"},{waitFor:"#alma-cmpv2-container #almacmp-modal-layer2"},{waitForThenClick:"#alma-cmpv2-container #almacmp-modal-layer2 #almacmp-reject-all-layer2"}],test:[{eval:"EVAL_ALMACMP_0"}]},{name:"altium.com",cosmetic:!0,prehideSelectors:[".altium-privacy-bar"],detectCmp:[{exists:".altium-privacy-bar"}],detectPopup:[{exists:".altium-privacy-bar"}],optIn:[{click:"a.altium-privacy-bar__btn"}],optOut:[{hide:[".altium-privacy-bar"]}]},{name:"amazon.com",prehideSelectors:['span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'],detectCmp:[{exists:'span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'}],detectPopup:[{visible:'span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'}],optIn:[{waitForVisible:"#sp-cc-accept"},{wait:500},{click:"#sp-cc-accept"}],optOut:[{waitForVisible:"#sp-cc-rejectall-link"},{wait:500},{click:"#sp-cc-rejectall-link"}]},{name:"aquasana.com",cosmetic:!0,prehideSelectors:["#consent-tracking"],detectCmp:[{exists:"#consent-tracking"}],detectPopup:[{exists:"#consent-tracking"}],optIn:[{click:"#accept_consent"}],optOut:[{hide:["#consent-tracking"]}]},{name:"arzt-auskunft.de",prehideSelectors:["#cookiescript_injected"],detectCmp:[{exists:"#cookiescript_injected"}],detectPopup:[{visible:"#cookiescript_injected"}],optOut:[{click:"#cookiescript_reject"}],optIn:[{click:"#cookiescript_accept"}]},{name:"athlinks-com",runContext:{urlPattern:"^https://(www\\.)?athlinks\\.com/"},cosmetic:!0,prehideSelectors:["#footer-container ~ div"],detectCmp:[{exists:"#footer-container ~ div"}],detectPopup:[{visible:"#footer-container > div"}],optIn:[{click:"#footer-container ~ div button"}],optOut:[{hide:["#footer-container ~ div"]}]},{name:"ausopen.com",cosmetic:!0,detectCmp:[{exists:".gdpr-popup__message"}],detectPopup:[{visible:".gdpr-popup__message"}],optOut:[{hide:[".gdpr-popup__message"]}],optIn:[{click:".gdpr-popup__message button"}]},{name:"automattic-cmp-optout",prehideSelectors:['form[class*="cookie-banner"][method="post"]'],detectCmp:[{exists:'form[class*="cookie-banner"][method="post"]'}],detectPopup:[{visible:'form[class*="cookie-banner"][method="post"]'}],optIn:[{click:'a[class*="accept-all-button"]'}],optOut:[{click:'form[class*="cookie-banner"] div[class*="simple-options"] a[class*="customize-button"]'},{waitForThenClick:"input[type=checkbox][checked]:not([disabled])",all:!0},{click:'a[class*="accept-selection-button"]'}]},{name:"aws.amazon.com",prehideSelectors:["#awsccc-cb-content","#awsccc-cs-container","#awsccc-cs-modalOverlay","#awsccc-cs-container-inner"],detectCmp:[{exists:"#awsccc-cb-content"}],detectPopup:[{visible:"#awsccc-cb-content"}],optIn:[{click:"button[data-id=awsccc-cb-btn-accept"}],optOut:[{click:"button[data-id=awsccc-cb-btn-customize]"},{waitFor:"input[aria-checked]"},{click:"input[aria-checked=true]",all:!0,optional:!0},{click:"button[data-id=awsccc-cs-btn-save]"}]},{name:"axeptio",prehideSelectors:[".axeptio_widget"],detectCmp:[{exists:".axeptio_widget"}],detectPopup:[{visible:".axeptio_widget"}],optIn:[{waitFor:".axeptio-widget--open"},{click:"button#axeptio_btn_acceptAll"}],optOut:[{waitFor:".axeptio-widget--open"},{click:"button#axeptio_btn_dismiss"}],test:[{eval:"EVAL_AXEPTIO_0"}]},{name:"baden-wuerttemberg.de",prehideSelectors:[".cookie-alert.t-dark"],cosmetic:!0,detectCmp:[{exists:".cookie-alert.t-dark"}],detectPopup:[{visible:".cookie-alert.t-dark"}],optIn:[{click:".cookie-alert__form input:not([disabled]):not([checked])"},{click:".cookie-alert__button button"}],optOut:[{hide:[".cookie-alert.t-dark"]}]},{name:"bbb.org",runContext:{urlPattern:"^https://www\\.bbb\\.org/"},cosmetic:!0,prehideSelectors:['div[aria-label="use of cookies on bbb.org"]'],detectCmp:[{exists:'div[aria-label="use of cookies on bbb.org"]'}],detectPopup:[{visible:'div[aria-label="use of cookies on bbb.org"]'}],optIn:[{click:'div[aria-label="use of cookies on bbb.org"] button.bds-button-unstyled span.visually-hidden'}],optOut:[{hide:['div[aria-label="use of cookies on bbb.org"]']}]},{name:"bing.com",prehideSelectors:["#bnp_container"],detectCmp:[{exists:"#bnp_cookie_banner"}],detectPopup:[{visible:"#bnp_cookie_banner"}],optIn:[{click:"#bnp_btn_accept"}],optOut:[{click:"#bnp_btn_preference"},{click:"#mcp_savesettings"}],test:[{eval:"EVAL_BING_0"}]},{name:"borlabs",detectCmp:[{exists:"._brlbs-block-content"}],detectPopup:[{visible:"._brlbs-bar-wrap,._brlbs-box-wrap"}],optIn:[{click:"a[data-cookie-accept-all]"}],optOut:[{click:"a[data-cookie-individual]"},{waitForVisible:".cookie-preference"},{click:"input[data-borlabs-cookie-checkbox]:checked",all:!0,optional:!0},{click:"#CookiePrefSave"},{wait:500}],prehideSelectors:["#BorlabsCookieBox"],test:[{eval:"EVAL_BORLABS_0"}]},{name:"bundesregierung.de",prehideSelectors:[".bpa-cookie-banner"],detectCmp:[{exists:".bpa-cookie-banner"}],detectPopup:[{visible:".bpa-cookie-banner .bpa-module-full-hero"}],optIn:[{click:".bpa-accept-all-button"}],optOut:[{wait:500,comment:"click is not immediately recognized"},{waitForThenClick:".bpa-close-button"}],test:[{eval:"EVAL_BUNDESREGIERUNG_DE_0"}]},{name:"burpee.com",cosmetic:!0,prehideSelectors:["#notice-cookie-block"],detectCmp:[{exists:"#notice-cookie-block"}],detectPopup:[{exists:"#html-body #notice-cookie-block"}],optIn:[{click:"#btn-cookie-allow"}],optOut:[{hide:["#html-body #notice-cookie-block","#notice-cookie"]}]},{name:"canva.com",prehideSelectors:['div[role="dialog"] a[data-anchor-id="cookie-policy"]'],detectCmp:[{exists:'div[role="dialog"] a[data-anchor-id="cookie-policy"]'}],detectPopup:[{exists:'div[role="dialog"] a[data-anchor-id="cookie-policy"]'}],optIn:[{click:'div[role="dialog"] button:nth-child(1)'}],optOut:[{if:{exists:'div[role="dialog"] button:nth-child(3)'},then:[{click:'div[role="dialog"] button:nth-child(2)'}],else:[{click:'div[role="dialog"] button:nth-child(2)'},{waitFor:'div[role="dialog"] a[data-anchor-id="privacy-policy"]'},{click:'div[role="dialog"] button:nth-child(2)'},{click:'div[role="dialog"] div:last-child button:only-child'}]}],test:[{eval:"EVAL_CANVA_0"}]},{name:"cc-banner-springer",prehideSelectors:[".cc-banner[data-cc-banner]"],detectCmp:[{exists:".cc-banner[data-cc-banner]"}],detectPopup:[{visible:".cc-banner[data-cc-banner]"}],optIn:[{waitForThenClick:".cc-banner[data-cc-banner] button[data-cc-action=accept]"}],optOut:[{if:{exists:".cc-banner[data-cc-banner] button[data-cc-action=reject]"},then:[{click:".cc-banner[data-cc-banner] button[data-cc-action=reject]"}],else:[{waitForThenClick:".cc-banner[data-cc-banner] button[data-cc-action=preferences]"},{waitFor:".cc-preferences[data-cc-preferences]"},{click:".cc-preferences[data-cc-preferences] input[type=radio][data-cc-action=toggle-category][value=off]",all:!0},{if:{exists:".cc-preferences[data-cc-preferences] button[data-cc-action=reject]"},then:[{click:".cc-preferences[data-cc-preferences] button[data-cc-action=reject]"}],else:[{click:".cc-preferences[data-cc-preferences] button[data-cc-action=save]"}]}]}],test:[{eval:"EVAL_CC_BANNER2_0"}]},{name:"cc_banner",cosmetic:!0,prehideSelectors:[".cc_banner-wrapper"],detectCmp:[{exists:".cc_banner-wrapper"}],detectPopup:[{visible:".cc_banner"}],optIn:[{click:".cc_btn_accept_all"}],optOut:[{hide:[".cc_banner-wrapper"]}]},{comment:"https://www.civicuk.com/cookie-control/",name:"civic-cookie-control",prehideSelectors:["#ccc-module,#ccc-overlay"],detectCmp:[{exists:"#ccc-module"}],detectPopup:[{visible:"#ccc"},{visible:"#ccc-module"}],optOut:[{click:"#ccc-reject-settings"}],optIn:[{click:"#ccc-recommended-settings"}]},{name:"click.io",prehideSelectors:["#cl-consent"],detectCmp:[{exists:"#cl-consent"}],detectPopup:[{visible:"#cl-consent"}],optIn:[{waitForThenClick:'#cl-consent [data-role="b_agree"]'}],optOut:[{waitFor:'#cl-consent [data-role="b_options"]'},{wait:500},{click:'#cl-consent [data-role="b_options"]'},{waitFor:'.cl-consent-popup.cl-consent-visible [data-role="alloff"]'},{click:'.cl-consent-popup.cl-consent-visible [data-role="alloff"]',all:!0},{click:'[data-role="b_save"]'}],test:[{eval:"EVAL_CLICKIO_0",comment:"TODO: this only checks if we interacted at all"}]},{name:"clinch",intermediate:!1,runContext:{frame:!1,main:!0},prehideSelectors:[".consent-modal[role=dialog]"],detectCmp:[{exists:".consent-modal[role=dialog]"}],detectPopup:[{visible:".consent-modal[role=dialog]"}],optIn:[{click:"#consent_agree"}],optOut:[{click:"#manage_cookie_preferences"},{click:"#cookie_consent_preferences input:checked",all:!0,optional:!0},{click:"#consent_save"}],test:[{eval:"EVAL_CLINCH_0"}]},{name:"clustrmaps.com",runContext:{urlPattern:"^https://(www\\.)?clustrmaps\\.com/"},cosmetic:!0,prehideSelectors:["#gdpr-cookie-message"],detectCmp:[{exists:"#gdpr-cookie-message"}],detectPopup:[{visible:"#gdpr-cookie-message"}],optIn:[{click:"button#gdpr-cookie-accept"}],optOut:[{hide:["#gdpr-cookie-message"]}]},{name:"coinbase",intermediate:!1,runContext:{frame:!0,main:!0,urlPattern:"^https://(www|help)\\.coinbase\\.com"},prehideSelectors:[],detectCmp:[{exists:"div[class^=CookieBannerContent__Container]"}],detectPopup:[{visible:"div[class^=CookieBannerContent__Container]"}],optIn:[{click:"div[class^=CookieBannerContent__CTA] :nth-last-child(1)"}],optOut:[{click:"button[class^=CookieBannerContent__Settings]"},{click:"div[class^=CookiePreferencesModal__CategoryContainer] input:checked",all:!0,optional:!0},{click:"div[class^=CookiePreferencesModal__ButtonContainer] > button"}],test:[{eval:"EVAL_COINBASE_0"}]},{name:"Complianz banner",prehideSelectors:["#cmplz-cookiebanner-container"],detectCmp:[{exists:"#cmplz-cookiebanner-container .cmplz-cookiebanner"}],detectPopup:[{visible:"#cmplz-cookiebanner-container .cmplz-cookiebanner",check:"any"}],optIn:[{waitForThenClick:".cmplz-cookiebanner .cmplz-accept"}],optOut:[{waitForThenClick:".cmplz-cookiebanner .cmplz-deny"}],test:[{eval:"EVAL_COMPLIANZ_BANNER_0"}]},{name:"Complianz categories",prehideSelectors:['.cc-type-categories[aria-describedby="cookieconsent:desc"]'],detectCmp:[{exists:'.cc-type-categories[aria-describedby="cookieconsent:desc"]'}],detectPopup:[{visible:'.cc-type-categories[aria-describedby="cookieconsent:desc"]'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{click:".cc-dismiss"}],test:[{eval:"EVAL_COMPLIANZ_CATEGORIES_0"}]},{name:"Complianz notice",prehideSelectors:['.cc-type-info[aria-describedby="cookieconsent:desc"]'],cosmetic:!0,detectCmp:[{exists:'.cc-type-info[aria-describedby="cookieconsent:desc"]'}],detectPopup:[{visible:'.cc-type-info[aria-describedby="cookieconsent:desc"]'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{hide:['[aria-describedby="cookieconsent:desc"]']}]},{name:"Complianz optin",prehideSelectors:['.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'],detectCmp:[{exists:'.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'}],detectPopup:[{visible:'.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{click:".cc-settings"},{waitForVisible:'[aria-label="cookies preferences popup"]'},{click:'[aria-label="cookies preferences popup"] input[type=checkbox]:not([disabled]):checked',all:!0,optional:!0},{click:'[aria-label="cookies preferences popup"] [aria-label="Accept Selected"], [aria-label="cookies preferences popup"] [aria-label="Save my choice"], .cc-btn-accept-selected, .cc-deny',optional:!0}],test:[{eval:"EVAL_COMPLIANZ_OPTIN_0"}]},{name:"cookie-law-info",prehideSelectors:["#cookie-law-info-bar"],detectCmp:[{exists:"#cookie-law-info-bar"}],detectPopup:[{visible:"#cookie-law-info-bar"}],optIn:[{click:'[data-cli_action="accept_all"]'}],optOut:[{hide:["#cookie-law-info-bar"]},{eval:"EVAL_COOKIE_LAW_INFO_0"}],test:[{eval:"EVAL_COOKIE_LAW_INFO_1"}]},{name:"cookie-manager-popup",cosmetic:!1,runContext:{main:!0,frame:!1},intermediate:!1,detectCmp:[{exists:"#notice-cookie-block #allow-functional-cookies, #notice-cookie-block #btn-cookie-settings"}],detectPopup:[{visible:"#notice-cookie-block"}],optIn:[{click:"#btn-cookie-allow"}],optOut:[{if:{exists:"#allow-functional-cookies"},then:[{click:"#allow-functional-cookies"}],else:[{waitForThenClick:"#btn-cookie-settings"},{waitForVisible:".modal-body"},{click:'.modal-body input:checked, .switch[data-switch="on"]',all:!0,optional:!0},{click:'[role="dialog"] .modal-footer button'}]}],prehideSelectors:["#btn-cookie-settings"],test:[{eval:"EVAL_COOKIE_MANAGER_POPUP_0"}]},{name:"cookie-notice",prehideSelectors:["#cookie-notice"],cosmetic:!0,detectCmp:[{visible:"#cookie-notice .cookie-notice-container"}],detectPopup:[{visible:"#cookie-notice"}],optIn:[{click:"#cn-accept-cookie"}],optOut:[{hide:["#cookie-notice"]}]},{name:"cookiealert",intermediate:!1,prehideSelectors:[],runContext:{frame:!0,main:!0},detectCmp:[{exists:".cookie-alert-extended"}],detectPopup:[{visible:".cookie-alert-extended-modal"}],optIn:[{click:"button[data-controller='cookie-alert/extended/button/accept']"},{eval:"EVAL_COOKIEALERT_0"}],optOut:[{click:"a[data-controller='cookie-alert/extended/detail-link']"},{click:".cookie-alert-configuration-input:checked",all:!0,optional:!0},{click:"button[data-controller='cookie-alert/extended/button/configuration']"},{eval:"EVAL_COOKIEALERT_0"}],test:[{eval:"EVAL_COOKIEALERT_2"}]},{name:"cookiefirst.com",prehideSelectors:["#cookiefirst-root,.cookiefirst-root,[aria-labelledby=cookie-preference-panel-title]"],detectCmp:[{exists:"#cookiefirst-root,.cookiefirst-root"}],detectPopup:[{visible:"#cookiefirst-root,.cookiefirst-root"}],optIn:[{click:"button[data-cookiefirst-action=accept]"}],optOut:[{if:{exists:"button[data-cookiefirst-action=adjust]"},then:[{click:"button[data-cookiefirst-action=adjust]"},{waitForVisible:"[data-cookiefirst-widget=modal]",timeout:1e3},{eval:"EVAL_COOKIEFIRST_1"},{wait:1e3},{click:"button[data-cookiefirst-action=save]"}],else:[{click:"button[data-cookiefirst-action=reject]"}]}],test:[{eval:"EVAL_COOKIEFIRST_0"}]},{name:"Cookie Information Banner",prehideSelectors:["#cookie-information-template-wrapper"],detectCmp:[{exists:"#cookie-information-template-wrapper"}],detectPopup:[{visible:"#cookie-information-template-wrapper"}],optIn:[{eval:"EVAL_COOKIEINFORMATION_1"}],optOut:[{hide:["#cookie-information-template-wrapper"],comment:"some templates don't hide the banner automatically"},{eval:"EVAL_COOKIEINFORMATION_0"}],test:[{eval:"EVAL_COOKIEINFORMATION_2"}]},{name:"corona-in-zahlen.de",prehideSelectors:[".cookiealert"],detectCmp:[{exists:".cookiealert"}],detectPopup:[{visible:".cookiealert"}],optOut:[{click:".configurecookies"},{click:".confirmcookies"}],optIn:[{click:".acceptcookies"}]},{name:"crossfit-com",cosmetic:!0,prehideSelectors:['body #modal > div > div[class^="_wrapper_"]'],detectCmp:[{exists:'body #modal > div > div[class^="_wrapper_"]'}],detectPopup:[{visible:'body #modal > div > div[class^="_wrapper_"]'}],optIn:[{click:'button[aria-label="accept cookie policy"]'}],optOut:[{hide:['body #modal > div > div[class^="_wrapper_"]']}]},{name:"dailymotion-us",cosmetic:!0,prehideSelectors:['div[class*="CookiePopup__desktopContainer"]:has(div[class*="CookiePopup"])'],detectCmp:[{exists:'div[class*="CookiePopup__desktopContainer"]'}],detectPopup:[{visible:'div[class*="CookiePopup__desktopContainer"]'}],optIn:[{click:'div[class*="CookiePopup__desktopContainer"] > button > span'}],optOut:[{hide:['div[class*="CookiePopup__desktopContainer"]']}]},{name:"dailymotion.com",runContext:{urlPattern:"^https://(www\\.)?dailymotion\\.com/"},prehideSelectors:['div[class*="Overlay__container"]:has(div[class*="TCF2Popup"])'],detectCmp:[{exists:'div[class*="TCF2Popup"]'}],detectPopup:[{visible:'[class*="TCF2Popup"] a[href^="https://www.dailymotion.com/legal/cookiemanagement"]'}],optIn:[{waitForThenClick:'button[class*="TCF2Popup__button"]:not([class*="TCF2Popup__personalize"])'}],optOut:[{waitForThenClick:'button[class*="TCF2ContinueWithoutAcceptingButton"]'}],test:[{eval:"EVAL_DAILYMOTION_0"}]},{name:"deepl.com",prehideSelectors:[".dl_cookieBanner_container"],detectCmp:[{exists:".dl_cookieBanner_container"}],detectPopup:[{visible:".dl_cookieBanner_container"}],optOut:[{click:".dl_cookieBanner--buttonSelected"}],optIn:[{click:".dl_cookieBanner--buttonAll"}]},{name:"delta.com",runContext:{urlPattern:"^https://www\\.delta\\.com/"},cosmetic:!0,prehideSelectors:["ngc-cookie-banner"],detectCmp:[{exists:"div.cookie-footer-container"}],detectPopup:[{visible:"div.cookie-footer-container"}],optIn:[{click:" button.cookie-close-icon"}],optOut:[{hide:["div.cookie-footer-container"]}]},{name:"dmgmedia-us",prehideSelectors:["#mol-ads-cmp-iframe, div.mol-ads-cmp > form > div"],detectCmp:[{exists:"div.mol-ads-cmp > form > div"}],detectPopup:[{waitForVisible:"div.mol-ads-cmp > form > div"}],optIn:[{waitForThenClick:"button.mol-ads-cmp--btn-primary"}],optOut:[{waitForThenClick:"div.mol-ads-ccpa--message > u > a"},{waitForVisible:".mol-ads-cmp--modal-dialog"},{waitForThenClick:"a.mol-ads-cmp-footer-privacy"},{waitForThenClick:"button.mol-ads-cmp--btn-secondary"}]},{name:"dmgmedia",prehideSelectors:['[data-project="mol-fe-cmp"]'],detectCmp:[{exists:'[data-project="mol-fe-cmp"]'}],detectPopup:[{visible:'[data-project="mol-fe-cmp"]'}],optIn:[{waitForThenClick:'[data-project="mol-fe-cmp"] button[class*=primary]'}],optOut:[{waitForThenClick:'[data-project="mol-fe-cmp"] button[class*=basic]'},{waitForVisible:'[data-project="mol-fe-cmp"] div[class*="tabContent"]'},{waitForThenClick:'[data-project="mol-fe-cmp"] div[class*="toggle"][class*="enabled"]',all:!0},{waitForThenClick:'[data-project="mol-fe-cmp"] button[class*=white]'}]},{name:"Drupal",detectCmp:[{exists:"#drupalorg-crosssite-gdpr"}],detectPopup:[{visible:"#drupalorg-crosssite-gdpr"}],optOut:[{click:".no"}],optIn:[{click:".yes"}]},{name:"WP DSGVO Tools",link:"https://wordpress.org/plugins/shapepress-dsgvo/",prehideSelectors:[".sp-dsgvo"],cosmetic:!0,detectCmp:[{exists:".sp-dsgvo.sp-dsgvo-popup-overlay"}],detectPopup:[{visible:".sp-dsgvo.sp-dsgvo-popup-overlay",check:"any"}],optIn:[{click:".sp-dsgvo-privacy-btn-accept-all",all:!0}],optOut:[{hide:[".sp-dsgvo.sp-dsgvo-popup-overlay"]}],test:[{eval:"EVAL_DSGVO_0"}]},{name:"dunelm.com",prehideSelectors:["div[data-testid=cookie-consent-modal-backdrop]"],detectCmp:[{exists:"div[data-testid=cookie-consent-message-contents]"}],detectPopup:[{visible:"div[data-testid=cookie-consent-message-contents]"}],optIn:[{click:'[data-testid="cookie-consent-allow-all"]'}],optOut:[{click:"button[data-testid=cookie-consent-adjust-settings]"},{click:"button[data-testid=cookie-consent-preferences-save]"}],test:[{eval:"EVAL_DUNELM_0"}]},{name:"etsy",prehideSelectors:["#gdpr-single-choice-overlay","#gdpr-privacy-settings"],detectCmp:[{exists:"#gdpr-single-choice-overlay"}],detectPopup:[{visible:"#gdpr-single-choice-overlay"}],optOut:[{click:"button[data-gdpr-open-full-settings]"},{waitForVisible:".gdpr-overlay-body input",timeout:3e3},{wait:1e3},{eval:"EVAL_ETSY_0"},{eval:"EVAL_ETSY_1"}],optIn:[{click:"button[data-gdpr-single-choice-accept]"}]},{name:"eu-cookie-compliance-banner",detectCmp:[{exists:".eu-cookie-compliance-banner-info"}],detectPopup:[{exists:".eu-cookie-compliance-popup-open"}],optIn:[{click:".agree-button"}],optOut:[{click:".decline-button,.eu-cookie-compliance-save-preferences-button",optional:!0},{hide:[".eu-cookie-compliance-banner-info","#sliding-popup"]}],test:[{eval:"EVAL_EU_COOKIE_COMPLIANCE_0"}]},{name:"EU Cookie Law",prehideSelectors:[".pea_cook_wrapper,.pea_cook_more_info_popover"],cosmetic:!0,detectCmp:[{exists:".pea_cook_wrapper"}],detectPopup:[{wait:500},{visible:".pea_cook_wrapper"}],optIn:[{click:"#pea_cook_btn"}],optOut:[{hide:[".pea_cook_wrapper"]}],test:[{eval:"EVAL_EU_COOKIE_LAW_0"}]},{name:"EZoic",prehideSelectors:["#ez-cookie-dialog-wrapper"],detectCmp:[{exists:"#ez-cookie-dialog-wrapper"}],detectPopup:[{visible:"#ez-cookie-dialog-wrapper"}],optIn:[{click:"#ez-accept-all",optional:!0},{eval:"EVAL_EZOIC_0",optional:!0}],optOut:[{wait:500},{click:"#ez-manage-settings"},{waitFor:"#ez-cookie-dialog input[type=checkbox]"},{click:"#ez-cookie-dialog input[type=checkbox][checked]",all:!0},{click:"#ez-save-settings"}],test:[{eval:"EVAL_EZOIC_1"}]},{name:"facebook",runContext:{urlPattern:"^https://([a-z0-9-]+\\.)?facebook\\.com/"},prehideSelectors:['div[data-testid="cookie-policy-manage-dialog"]'],detectCmp:[{exists:'div[data-testid="cookie-policy-manage-dialog"]'}],detectPopup:[{visible:'div[data-testid="cookie-policy-manage-dialog"]'}],optIn:[{waitForThenClick:'button[data-cookiebanner="accept_button"]'},{waitForVisible:'div[data-testid="cookie-policy-manage-dialog"]',check:"none"}],optOut:[{waitForThenClick:'button[data-cookiebanner="accept_only_essential_button"]'},{waitForVisible:'div[data-testid="cookie-policy-manage-dialog"]',check:"none"}]},{name:"funding-choices",prehideSelectors:[".fc-consent-root,.fc-dialog-container,.fc-dialog-overlay,.fc-dialog-content"],detectCmp:[{exists:".fc-consent-root"}],detectPopup:[{exists:".fc-dialog-container"}],optOut:[{click:".fc-cta-do-not-consent,.fc-cta-manage-options"},{click:".fc-preference-consent:checked,.fc-preference-legitimate-interest:checked",all:!0,optional:!0},{click:".fc-confirm-choices",optional:!0}],optIn:[{click:".fc-cta-consent"}]},{name:"geeks-for-geeks",runContext:{urlPattern:"^https://www\\.geeksforgeeks\\.org/"},cosmetic:!0,prehideSelectors:[".cookie-consent"],detectCmp:[{exists:".cookie-consent"}],detectPopup:[{visible:".cookie-consent"}],optIn:[{click:".cookie-consent button.consent-btn"}],optOut:[{hide:[".cookie-consent"]}]},{name:"generic-cosmetic",cosmetic:!0,prehideSelectors:["#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"],detectCmp:[{exists:"#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"}],detectPopup:[{visible:"#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"}],optIn:[],optOut:[{hide:["#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"]}]},{name:"google-consent-standalone",prehideSelectors:[],detectCmp:[{exists:'a[href^="https://policies.google.com/technologies/cookies"'},{exists:'form[action^="https://consent."][action$=".com/save"]'}],detectPopup:[{visible:'a[href^="https://policies.google.com/technologies/cookies"'}],optIn:[{waitForThenClick:'form[action^="https://consent."][action$=".com/save"]:has(input[name=set_eom][value=false]) button'}],optOut:[{waitForThenClick:'form[action^="https://consent."][action$=".com/save"]:has(input[name=set_eom][value=true]) button'}]},{name:"google.com",prehideSelectors:[".HTjtHe#xe7COe"],detectCmp:[{exists:".HTjtHe#xe7COe"},{exists:'.HTjtHe#xe7COe a[href^="https://policies.google.com/technologies/cookies"]'}],detectPopup:[{visible:".HTjtHe#xe7COe button#W0wltc"}],optIn:[{waitForThenClick:".HTjtHe#xe7COe button#L2AGLb"}],optOut:[{waitForThenClick:".HTjtHe#xe7COe button#W0wltc"}],test:[{eval:"EVAL_GOOGLE_0"}]},{name:"gov.uk",detectCmp:[{exists:"#global-cookie-message"}],detectPopup:[{exists:"#global-cookie-message"}],optIn:[{click:"button[data-accept-cookies=true]"}],optOut:[{click:"button[data-reject-cookies=true],#reject-cookies"},{click:"button[data-hide-cookie-banner=true],#hide-cookie-decision"}]},{name:"healthline-media",prehideSelectors:["#modal-host > div.no-hash > div.window-wrapper"],detectCmp:[{exists:"#modal-host > div.no-hash > div.window-wrapper, div[data-testid=qualtrics-container]"}],detectPopup:[{exists:"#modal-host > div.no-hash > div.window-wrapper, div[data-testid=qualtrics-container]"}],optIn:[{click:"#modal-host > div.no-hash > div.window-wrapper > div:last-child button"}],optOut:[{if:{exists:'#modal-host > div.no-hash > div.window-wrapper > div:last-child a[href="/privacy-settings"]'},then:[{click:'#modal-host > div.no-hash > div.window-wrapper > div:last-child a[href="/privacy-settings"]'}],else:[{waitForVisible:"div#__next"},{click:"#__next div:nth-child(1) > button:first-child"}]}]},{name:"hl.co.uk",prehideSelectors:[".cookieModalContent","#cookie-banner-overlay"],detectCmp:[{exists:"#cookie-banner-overlay"}],detectPopup:[{exists:"#cookie-banner-overlay"}],optIn:[{click:"#acceptCookieButton"}],optOut:[{click:"#manageCookie"},{hide:[".cookieSettingsModal"]},{waitFor:"#AOCookieToggle"},{click:"#AOCookieToggle[aria-pressed=true]",optional:!0},{waitFor:"#TPCookieToggle"},{click:"#TPCookieToggle[aria-pressed=true]",optional:!0},{click:"#updateCookieButton"}]},{name:"hubspot",detectCmp:[{exists:"#hs-eu-cookie-confirmation"}],detectPopup:[{visible:"#hs-eu-cookie-confirmation"}],optIn:[{click:"#hs-eu-confirmation-button"}],optOut:[{click:"#hs-eu-decline-button"}]},{name:"indeed.com",cosmetic:!0,prehideSelectors:["#CookiePrivacyNotice"],detectCmp:[{exists:"#CookiePrivacyNotice"}],detectPopup:[{visible:"#CookiePrivacyNotice"}],optIn:[{click:"#CookiePrivacyNotice button[data-gnav-element-name=CookiePrivacyNoticeOk]"}],optOut:[{hide:["#CookiePrivacyNotice"]}]},{name:"ionos.de",prehideSelectors:[".privacy-consent--backdrop",".privacy-consent--modal"],detectCmp:[{exists:".privacy-consent--modal"}],detectPopup:[{visible:".privacy-consent--modal"}],optIn:[{click:"#selectAll"}],optOut:[{click:".footer-config-link"},{click:"#confirmSelection"}]},{name:"itopvpn.com",cosmetic:!0,prehideSelectors:[".pop-cookie"],detectCmp:[{exists:".pop-cookie"}],detectPopup:[{exists:".pop-cookie"}],optIn:[{click:"#_pcookie"}],optOut:[{hide:[".pop-cookie"]}]},{name:"iubenda",prehideSelectors:["#iubenda-cs-banner"],detectCmp:[{exists:"#iubenda-cs-banner"}],detectPopup:[{visible:".iubenda-cs-accept-btn"}],optIn:[{click:".iubenda-cs-accept-btn"}],optOut:[{click:".iubenda-cs-customize-btn"},{eval:"EVAL_IUBENDA_0"},{click:"#iubFooterBtn"}],test:[{eval:"EVAL_IUBENDA_1"}]},{name:"johnlewis.com",prehideSelectors:["div[class^=pecr-cookie-banner-]"],detectCmp:[{exists:"div[class^=pecr-cookie-banner-]"}],detectPopup:[{exists:"div[class^=pecr-cookie-banner-]"}],optOut:[{click:"button[data-test^=manage-cookies]"},{wait:"500"},{click:"label[data-test^=toggle][class*=checked]:not([class*=disabled])",all:!0,optional:!0},{click:"button[data-test=save-preferences]"}],optIn:[{click:"button[data-test=allow-all]"}]},{name:"jquery.cookieBar",comment:"https://github.com/kovarp/jquery.cookieBar",prehideSelectors:[".cookie-bar"],cosmetic:!0,detectCmp:[{exists:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons"}],detectPopup:[{visible:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons",check:"any"}],optIn:[{click:".cookie-bar .cookie-bar__btn"}],optOut:[{hide:[".cookie-bar"]}],test:[{visible:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons",check:"none"},{eval:"EVAL_JQUERY_COOKIEBAR_0"}]},{name:"justwatch.com",prehideSelectors:[".consent-banner"],detectCmp:[{exists:".consent-banner .consent-banner__actions"}],detectPopup:[{visible:".consent-banner .consent-banner__actions"}],optIn:[{click:".consent-banner__actions button.basic-button.primary"}],optOut:[{click:".consent-banner__actions button.basic-button.secondary"},{waitForThenClick:".consent-modal__footer button.basic-button.secondary"},{waitForThenClick:".consent-modal ion-content > div > a:nth-child(9)"},{click:"label.consent-switch input[type=checkbox]:checked",all:!0,optional:!0},{waitForVisible:".consent-modal__footer button.basic-button.primary"},{click:".consent-modal__footer button.basic-button.primary"}]},{name:"ketch",runContext:{frame:!1,main:!0},intermediate:!1,prehideSelectors:["#lanyard_root div[role='dialog']"],detectCmp:[{exists:"#lanyard_root div[role='dialog']"}],detectPopup:[{visible:"#lanyard_root div[role='dialog']"}],optIn:[{if:{exists:"#lanyard_root button[class='confirmButton']"},then:[{waitForThenClick:"#lanyard_root div[class^='buttons'] > :nth-child(2)"},{click:"#lanyard_root button[class='confirmButton']"}],else:[{waitForThenClick:"#lanyard_root div[class^='buttons'] > :nth-child(2)"}]}],optOut:[{click:"#lanyard_root button[class^='link']",optional:!0},{if:{exists:"#lanyard_root button[class*='confirmButton']"},then:[{waitForThenClick:"#lanyard_root button[class*='rejectButton']"},{click:"#lanyard_root button[class*='confirmButton']"}],else:[{click:"#lanyard_root div[class^='buttons'] > :nth-child(1)",optional:!0},{waitForThenClick:"#lanyard_root input:checked"},{click:"#consentsTab > div:nth-child(2) > div > div[class^='actions'] > button:nth-child(1)"}]}],test:[]},{name:"kleinanzeigen-de",runContext:{urlPattern:"^https?://(www\\.)?kleinanzeigen\\.de"},prehideSelectors:["#gdpr-banner-container"],detectCmp:[{any:[{exists:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"},{exists:"#ConsentManagementPage"}]}],detectPopup:[{any:[{visible:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"},{visible:"#ConsentManagementPage"}]}],optIn:[{if:{exists:"#gdpr-banner-container #gdpr-banner"},then:[{click:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-accept]"}],else:[{click:"#ConsentManagementPage .Button-primary"}]}],optOut:[{if:{exists:"#gdpr-banner-container #gdpr-banner"},then:[{click:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"}],else:[{click:"#ConsentManagementPage .Button-secondary"}]}]},{name:"linkedin.com",prehideSelectors:[".artdeco-global-alert[type=COOKIE_CONSENT]"],detectCmp:[{exists:".artdeco-global-alert[type=COOKIE_CONSENT]"}],detectPopup:[{visible:".artdeco-global-alert[type=COOKIE_CONSENT]"}],optIn:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=ACCEPT]"},{wait:500},{waitForThenClick:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=ACCEPT]"}],optOut:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=DENY]"},{wait:500},{waitForThenClick:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=DENY]"}],test:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT]",check:"none"}]},{name:"macpaw.com",cosmetic:!0,prehideSelectors:['div[data-banner="cookies"]'],detectCmp:[{exists:'div[data-banner="cookies"]'}],detectPopup:[{exists:'div[data-banner="cookies"]'}],optIn:[{click:'button[data-banner-close="cookies"]'}],optOut:[{hide:['div[data-banner="cookies"]']}]},{name:"marksandspencer.com",cosmetic:!0,detectCmp:[{exists:".navigation-cookiebbanner"}],detectPopup:[{visible:".navigation-cookiebbanner"}],optOut:[{hide:[".navigation-cookiebbanner"]}],optIn:[{click:".navigation-cookiebbanner__submit"}]},{name:"mediamarkt.de",prehideSelectors:["div[aria-labelledby=pwa-consent-layer-title]","div[class^=StyledConsentLayerWrapper-]"],detectCmp:[{exists:"div[aria-labelledby^=pwa-consent-layer-title]"}],detectPopup:[{exists:"div[aria-labelledby^=pwa-consent-layer-title]"}],optOut:[{click:"button[data-test^=pwa-consent-layer-deny-all]"}],optIn:[{click:"button[data-test^=pwa-consent-layer-accept-all"}]},{name:"Mediavine",prehideSelectors:['[data-name="mediavine-gdpr-cmp"]'],detectCmp:[{exists:'[data-name="mediavine-gdpr-cmp"]'}],detectPopup:[{wait:500},{visible:'[data-name="mediavine-gdpr-cmp"]'}],optIn:[{waitForThenClick:'[data-name="mediavine-gdpr-cmp"] [format="primary"]'}],optOut:[{waitForThenClick:'[data-name="mediavine-gdpr-cmp"] [data-view="manageSettings"]'},{waitFor:'[data-name="mediavine-gdpr-cmp"] input[type=checkbox]'},{eval:"EVAL_MEDIAVINE_0",optional:!0},{click:'[data-name="mediavine-gdpr-cmp"] [format="secondary"]'}]},{name:"microsoft.com",prehideSelectors:["#wcpConsentBannerCtrl"],detectCmp:[{exists:"#wcpConsentBannerCtrl"}],detectPopup:[{exists:"#wcpConsentBannerCtrl"}],optOut:[{eval:"EVAL_MICROSOFT_0"}],optIn:[{eval:"EVAL_MICROSOFT_1"}],test:[{eval:"EVAL_MICROSOFT_2"}]},{name:"midway-usa",runContext:{urlPattern:"^https://www\\.midwayusa\\.com/"},cosmetic:!0,prehideSelectors:["#cookie-container"],detectCmp:[{exists:['div[aria-label="Cookie Policy Banner"]']}],detectPopup:[{visible:"#cookie-container"}],optIn:[{click:"button#cookie-btn"}],optOut:[{hide:['div[aria-label="Cookie Policy Banner"]']}]},{name:"moneysavingexpert.com",detectCmp:[{exists:"dialog[data-testid=accept-our-cookies-dialog]"}],detectPopup:[{visible:"dialog[data-testid=accept-our-cookies-dialog]"}],optIn:[{click:"#banner-accept"}],optOut:[{click:"#banner-manage"},{click:"#pc-confirm"}]},{name:"monzo.com",prehideSelectors:[".cookie-alert, cookie-alert__content"],detectCmp:[{exists:'div.cookie-alert[role="dialog"]'},{exists:'a[href*="monzo"]'}],detectPopup:[{visible:".cookie-alert__content"}],optIn:[{click:".js-accept-cookie-policy"}],optOut:[{click:".js-decline-cookie-policy"}]},{name:"Moove",prehideSelectors:["#moove_gdpr_cookie_info_bar"],detectCmp:[{exists:"#moove_gdpr_cookie_info_bar"}],detectPopup:[{visible:"#moove_gdpr_cookie_info_bar"}],optIn:[{waitForThenClick:".moove-gdpr-infobar-allow-all"}],optOut:[{if:{exists:"#moove_gdpr_cookie_info_bar .change-settings-button"},then:[{click:"#moove_gdpr_cookie_info_bar .change-settings-button"},{waitForVisible:"#moove_gdpr_cookie_modal"},{eval:"EVAL_MOOVE_0"},{click:".moove-gdpr-modal-save-settings"}],else:[{hide:["#moove_gdpr_cookie_info_bar"]}]}],test:[{visible:"#moove_gdpr_cookie_info_bar",check:"none"}]},{name:"national-lottery.co.uk",detectCmp:[{exists:".cuk_cookie_consent"}],detectPopup:[{visible:".cuk_cookie_consent",check:"any"}],optOut:[{click:".cuk_cookie_consent_manage_pref"},{click:".cuk_cookie_consent_save_pref"},{click:".cuk_cookie_consent_close"}],optIn:[{click:".cuk_cookie_consent_accept_all"}]},{name:"nba.com",runContext:{urlPattern:"^https://(www\\.)?nba.com/"},cosmetic:!0,prehideSelectors:["#onetrust-banner-sdk"],detectCmp:[{exists:"#onetrust-banner-sdk"}],detectPopup:[{visible:"#onetrust-banner-sdk"}],optIn:[{click:"#onetrust-accept-btn-handler"}],optOut:[{hide:["#onetrust-banner-sdk"]}]},{name:"netflix.de",detectCmp:[{exists:"#cookie-disclosure"}],detectPopup:[{visible:".cookie-disclosure-message",check:"any"}],optIn:[{click:".btn-accept"}],optOut:[{hide:["#cookie-disclosure"]},{click:".btn-reject"}]},{name:"nhs.uk",prehideSelectors:["#nhsuk-cookie-banner"],detectCmp:[{exists:"#nhsuk-cookie-banner"}],detectPopup:[{exists:"#nhsuk-cookie-banner"}],optOut:[{click:"#nhsuk-cookie-banner__link_accept"}],optIn:[{click:"#nhsuk-cookie-banner__link_accept_analytics"}]},{name:"notice-cookie",prehideSelectors:[".button--notice"],cosmetic:!0,detectCmp:[{exists:".notice--cookie"}],detectPopup:[{visible:".notice--cookie"}],optIn:[{click:".button--notice"}],optOut:[{hide:[".notice--cookie"]}]},{name:"nrk.no",cosmetic:!0,prehideSelectors:[".nrk-masthead__info-banner--cookie"],detectCmp:[{exists:".nrk-masthead__info-banner--cookie"}],detectPopup:[{exists:".nrk-masthead__info-banner--cookie"}],optIn:[{click:"div.nrk-masthead__info-banner--cookie button > span:has(+ svg.nrk-close)"}],optOut:[{hide:[".nrk-masthead__info-banner--cookie"]}]},{name:"obi.de",prehideSelectors:[".disc-cp--active"],detectCmp:[{exists:".disc-cp-modal__modal"}],detectPopup:[{visible:".disc-cp-modal__modal"}],optIn:[{click:".js-disc-cp-accept-all"}],optOut:[{click:".js-disc-cp-deny-all"}]},{name:"onlyFans.com",prehideSelectors:["div.b-cookies-informer"],detectCmp:[{exists:"div.b-cookies-informer"}],detectPopup:[{exists:"div.b-cookies-informer"}],optIn:[{click:"div.b-cookies-informer__nav > button:nth-child(2)"}],optOut:[{click:"div.b-cookies-informer__nav > button:nth-child(1)"},{click:'div.b-cookies-informer__switchers > div:nth-child(2) > div[at-attr="checkbox"] > span.b-input-radio__container > input[type="checkbox"]'},{click:"div.b-cookies-informer__nav > button"}]},{name:"osano",prehideSelectors:[".osano-cm-window"],cosmetic:!0,detectCmp:[{exists:".osano-cm-window"}],detectPopup:[{visible:".osano-cm-dialog"}],optIn:[{click:".osano-cm-accept-all",optional:!0}],optOut:[{hide:[".osano-cm-window"]}]},{name:"otto.de",prehideSelectors:[".cookieBanner--visibility"],detectCmp:[{exists:".cookieBanner--visibility"}],detectPopup:[{visible:".cookieBanner__wrapper"}],optIn:[{click:".js_cookieBannerPermissionButton"}],optOut:[{click:".js_cookieBannerProhibitionButton"}]},{name:"paypal-us",prehideSelectors:["#ccpaCookieContent_wrapper, article.ppvx_modal--overpanel"],detectCmp:[{exists:"#ccpaCookieBanner, .privacy-modal-content"}],detectPopup:[{exists:"#ccpaCookieBanner, .privacy-modal-content"}],optIn:[{click:"#acceptAllButton"}],optOut:[{if:{exists:"a#manageCookiesLink"},then:[{click:"a#manageCookiesLink"}],else:[{waitForVisible:".privacy-modal-content #formContent"},{click:"#formContent .cookiepref-11m2iee-checkbox_base input:checked",all:!0,optional:!0},{click:".confirmCookie #submitCookiesBtn"}]}]},{name:"paypal.com",prehideSelectors:["#gdprCookieBanner"],detectCmp:[{exists:"#gdprCookieBanner"}],detectPopup:[{visible:"#gdprCookieContent_wrapper"}],optIn:[{click:"#acceptAllButton"}],optOut:[{wait:200},{click:".gdprCookieBanner_decline-button"}],test:[{wait:500},{eval:"EVAL_PAYPAL_0"}]},{name:"pinetools.com",cosmetic:!0,prehideSelectors:["#aviso_cookies"],detectCmp:[{exists:"#aviso_cookies"}],detectPopup:[{exists:".lang_en #aviso_cookies"}],optIn:[{click:"#aviso_cookies .a_boton_cerrar"}],optOut:[{hide:["#aviso_cookies"]}]},{name:"pmc",cosmetic:!0,prehideSelectors:["#pmc-pp-tou--notice"],detectCmp:[{exists:"#pmc-pp-tou--notice"}],detectPopup:[{visible:"#pmc-pp-tou--notice"}],optIn:[{click:"span.pmc-pp-tou--notice-close-btn"}],optOut:[{hide:["#pmc-pp-tou--notice"]}]},{name:"pornhub.com",runContext:{urlPattern:"^https://(www\\.)?pornhub\\.com/"},cosmetic:!0,prehideSelectors:[".cookiesBanner"],detectCmp:[{exists:".cookiesBanner"}],detectPopup:[{visible:".cookiesBanner"}],optIn:[{click:".cookiesBanner .okButton"}],optOut:[{hide:[".cookiesBanner"]}]},{name:"pornpics.com",cosmetic:!0,prehideSelectors:["#cookie-contract"],detectCmp:[{exists:"#cookie-contract"}],detectPopup:[{visible:"#cookie-contract"}],optIn:[{click:"#cookie-contract .icon-cross"}],optOut:[{hide:["#cookie-contract"]}]},{name:"PrimeBox CookieBar",prehideSelectors:["#cookie-bar"],detectCmp:[{exists:"#cookie-bar .cb-enable,#cookie-bar .cb-disable,#cookie-bar .cb-policy"}],detectPopup:[{visible:"#cookie-bar .cb-enable,#cookie-bar .cb-disable,#cookie-bar .cb-policy",check:"any"}],optIn:[{waitForThenClick:"#cookie-bar .cb-enable"}],optOut:[{click:"#cookie-bar .cb-disable",optional:!0},{hide:["#cookie-bar"]}],test:[{eval:"EVAL_PRIMEBOX_0"}]},{name:"privacymanager.io",prehideSelectors:["#gdpr-consent-tool-wrapper",'iframe[src^="https://cmp-consent-tool.privacymanager.io"]'],runContext:{urlPattern:"^https://cmp-consent-tool\\.privacymanager\\.io/",main:!1,frame:!0},detectCmp:[{exists:"button#save"}],detectPopup:[{visible:"button#save"}],optIn:[{click:"button#save"}],optOut:[{if:{exists:"#denyAll"},then:[{click:"#denyAll"},{waitForThenClick:".okButton"}],else:[{waitForThenClick:"#manageSettings"},{waitFor:".purposes-overview-list"},{waitFor:"button#saveAndExit"},{click:"span[role=checkbox][aria-checked=true]",all:!0,optional:!0},{click:"button#saveAndExit"}]}]},{name:"pubtech",prehideSelectors:["#pubtech-cmp"],detectCmp:[{exists:"#pubtech-cmp"}],detectPopup:[{visible:"#pubtech-cmp #pt-actions"}],optIn:[{if:{exists:"#pt-accept-all"},then:[{click:"#pubtech-cmp #pt-actions #pt-accept-all"}],else:[{click:"#pubtech-cmp #pt-actions button:nth-of-type(2)"}]}],optOut:[{click:"#pubtech-cmp #pt-close"}],test:[{eval:"EVAL_PUBTECH_0"}]},{name:"quantcast",prehideSelectors:["#qc-cmp2-main,#qc-cmp2-container"],detectCmp:[{exists:"#qc-cmp2-container"}],detectPopup:[{visible:"#qc-cmp2-ui"}],optOut:[{click:'.qc-cmp2-summary-buttons > button[mode="secondary"]'},{waitFor:"#qc-cmp2-ui"},{click:'.qc-cmp2-toggle-switch > button[aria-checked="true"]',all:!0,optional:!0},{click:'.qc-cmp2-main button[aria-label="REJECT ALL"]',optional:!0},{waitForThenClick:'.qc-cmp2-main button[aria-label="SAVE & EXIT"],.qc-cmp2-buttons-desktop > button[mode="primary"]',timeout:5e3}],optIn:[{click:'.qc-cmp2-summary-buttons > button[mode="primary"]'}]},{name:"reddit.com",runContext:{urlPattern:"^https://www\\.reddit\\.com/"},prehideSelectors:['section:has(a[href^="https://www.reddit.com/policies/cookies"])'],detectCmp:[{exists:'section:has(a[href^="https://www.reddit.com/policies/cookies"])'}],detectPopup:[{visible:'section:has(a[href^="https://www.reddit.com/policies/cookies"])'}],optIn:[{waitForThenClick:"section:has(a[href^=\"https://www.reddit.com/policies/cookies\"]) section[class^='_'] > section:first-child form button"}],optOut:[{waitForThenClick:"section:has(a[href^=\"https://www.reddit.com/policies/cookies\"]) section[class^='_'] > section:last-child form button"}],test:[{eval:"EVAL_REDDIT_0"}]},{name:"samsung.com",runContext:{urlPattern:"^https://www\\.samsung\\.com/"},cosmetic:!0,prehideSelectors:["div.cookie-bar"],detectCmp:[{exists:"div.cookie-bar"}],detectPopup:[{visible:"div.cookie-bar"}],optIn:[{click:"div.cookie-bar__manage > a"}],optOut:[{hide:["div.cookie-bar"]}]},{name:"sibbo",prehideSelectors:["sibbo-cmp-layout"],detectCmp:[{exists:"sibbo-cmp-layout"}],detectPopup:[{visible:"sibbo-cmp-layout"}],optIn:[{click:"sibbo-cmp-layout [data-accept-all]"}],optOut:[{click:'.sibbo-panel__aside__buttons a[data-nav="purposes"]'},{click:'.sibbo-panel__main__header__actions a[data-focusable="reject-all"]'},{if:{exists:"[data-view=purposes] .sibbo-panel__main__footer__actions [data-save-and-exit]"},then:[],else:[{waitFor:'.sibbo-panel__main__footer__actions a[data-focusable="next"]:not(.sibbo-cmp-button--disabled)'},{click:'.sibbo-panel__main__footer__actions a[data-focusable="next"]'},{click:'.sibbo-panel__main div[data-view="purposesLegInt"] a[data-focusable="reject-all"]'}]},{waitFor:".sibbo-panel__main__footer__actions [data-save-and-exit]:not(.sibbo-cmp-button--disabled)"},{click:".sibbo-panel__main__footer__actions [data-save-and-exit]:not(.sibbo-cmp-button--disabled)"}],test:[{eval:"EVAL_SIBBO_0"}]},{name:"similarweb.com",cosmetic:!0,prehideSelectors:[".app-cookies-notification"],detectCmp:[{exists:".app-cookies-notification"}],detectPopup:[{exists:".app-layout .app-cookies-notification"}],optIn:[{click:"button.app-cookies-notification__dismiss"}],optOut:[{hide:[".app-layout .app-cookies-notification"]}]},{name:"Sirdata",prehideSelectors:["#sd-cmp"],detectCmp:[{exists:"#sd-cmp"}],detectPopup:[{visible:"#sd-cmp"}],optIn:[{waitForThenClick:"#sd-cmp .sd-cmp-3cRQ2"}],optOut:[{waitForThenClick:"#sd-cmp .sd-cmp-1pO44"}],test:[{eval:"EVAL_SIRDATA_0"}]},{name:"snigel",detectCmp:[{exists:".snigel-cmp-framework"}],detectPopup:[{visible:".snigel-cmp-framework"}],optOut:[{click:"#sn-b-custom"},{click:"#sn-b-save"}],test:[{eval:"EVAL_SNIGEL_0"}],optIn:[{click:".snigel-cmp-framework #accept-choices"}]},{name:"steampowered.com",detectCmp:[{exists:".cookiepreferences_popup"},{visible:".cookiepreferences_popup"}],detectPopup:[{visible:".cookiepreferences_popup"}],optOut:[{click:"#rejectAllButton"}],optIn:[{click:"#acceptAllButton"}],test:[{wait:1e3},{eval:"EVAL_STEAMPOWERED_0"}]},{name:"takealot.com",cosmetic:!0,prehideSelectors:['div[class^="cookies-banner-module_"]'],detectCmp:[{exists:'div[class^="cookies-banner-module_cookie-banner_"]'}],detectPopup:[{exists:'div[class^="cookies-banner-module_cookie-banner_"]'}],optIn:[{click:'button[class*="cookies-banner-module_dismiss-button_"]'}],optOut:[{hide:['div[class^="cookies-banner-module_"]']},{if:{exists:'div[class^="cookies-banner-module_small-cookie-banner_"]'},then:[{eval:"EVAL_TAKEALOT_0"}],else:[]}]},{name:"tarteaucitron.js",prehideSelectors:["#tarteaucitronRoot"],detectCmp:[{exists:"#tarteaucitronRoot"}],detectPopup:[{visible:"#tarteaucitronRoot #tarteaucitronAlertSmall,#tarteaucitronRoot #tarteaucitronAlertBig",check:"any"}],optIn:[{eval:"EVAL_TARTEAUCITRON_1"}],optOut:[{eval:"EVAL_TARTEAUCITRON_0"}],test:[{eval:"EVAL_TARTEAUCITRON_2",comment:"sometimes there are required categories, so we check that at least something is false"}]},{name:"Tealium",prehideSelectors:["#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs,#consent-layer"],detectCmp:[{visible:"#__tealiumGDPRecModal"},{eval:"EVAL_TEALIUM_0"}],detectPopup:[{visible:"#__tealiumGDPRecModal"}],optOut:[{waitForThenClick:"#cm-acceptNone,.js-accept-essential-cookies",timeout:1e3},{eval:"EVAL_TEALIUM_1"}],optIn:[{hide:["#__tealiumGDPRecModal"]},{eval:"EVAL_TEALIUM_2"}],test:[{eval:"EVAL_TEALIUM_3"}]},{name:"Termly",prehideSelectors:["#termly-code-snippet-support"],detectCmp:[{exists:"#termly-code-snippet-support"}],detectPopup:[{visible:"#termly-code-snippet-support div"}],optIn:[{waitForThenClick:'[data-tid="banner-accept"]'}],optOut:[{if:{exists:'[data-tid="banner-decline"]'},then:[{click:'[data-tid="banner-decline"]'}],else:[{click:".t-preference-button"},{wait:500},{if:{exists:".t-declineAllButton"},then:[{click:".t-declineAllButton"}],else:[{waitForThenClick:".t-preference-modal input[type=checkbox][checked]:not([disabled])",all:!0},{waitForThenClick:".t-saveButton"}]}]}]},{name:"Test page cosmetic CMP",cosmetic:!0,prehideSelectors:["#privacy-test-page-cmp-test-prehide"],detectCmp:[{exists:"#privacy-test-page-cmp-test-banner"}],detectPopup:[{visible:"#privacy-test-page-cmp-test-banner"}],optIn:[{waitFor:"#accept-all"},{click:"#accept-all"}],optOut:[{hide:["#privacy-test-page-cmp-test-banner"]}],test:[{wait:500},{eval:"EVAL_TESTCMP_COSMETIC_0"}]},{name:"Test page CMP",prehideSelectors:["#reject-all"],detectCmp:[{exists:"#privacy-test-page-cmp-test"}],detectPopup:[{visible:"#privacy-test-page-cmp-test"}],optIn:[{waitFor:"#accept-all"},{click:"#accept-all"}],optOut:[{waitFor:"#reject-all"},{click:"#reject-all"}],test:[{eval:"EVAL_TESTCMP_0"}]},{name:"thalia.de",prehideSelectors:[".consent-banner-box"],detectCmp:[{exists:"consent-banner[component=consent-banner]"}],detectPopup:[{visible:".consent-banner-box"}],optIn:[{click:".button-zustimmen"}],optOut:[{click:"button[data-consent=disagree]"}]},{name:"thefreedictionary.com",prehideSelectors:["#cmpBanner"],detectCmp:[{exists:"#cmpBanner"}],detectPopup:[{visible:"#cmpBanner"}],optIn:[{eval:"EVAL_THEFREEDICTIONARY_1"}],optOut:[{eval:"EVAL_THEFREEDICTIONARY_0"}]},{name:"theverge",runContext:{frame:!1,main:!0,urlPattern:"^https://(www)?\\.theverge\\.com"},intermediate:!1,prehideSelectors:[".duet--cta--cookie-banner"],detectCmp:[{exists:".duet--cta--cookie-banner"}],detectPopup:[{visible:".duet--cta--cookie-banner"}],optIn:[{click:".duet--cta--cookie-banner button.tracking-12",all:!1}],optOut:[{click:".duet--cta--cookie-banner button.tracking-12 > span"}],test:[{eval:"EVAL_THEVERGE_0"}]},{name:"tidbits-com",cosmetic:!0,prehideSelectors:["#eu_cookie_law_widget-2"],detectCmp:[{exists:"#eu_cookie_law_widget-2"}],detectPopup:[{visible:"#eu_cookie_law_widget-2"}],optIn:[{click:"#eu-cookie-law form > input.accept"}],optOut:[{hide:["#eu_cookie_law_widget-2"]}]},{name:"tractor-supply",runContext:{urlPattern:"^https://www\\.tractorsupply\\.com/"},cosmetic:!0,prehideSelectors:[".tsc-cookie-banner"],detectCmp:[{exists:".tsc-cookie-banner"}],detectPopup:[{visible:".tsc-cookie-banner"}],optIn:[{click:"#cookie-banner-cancel"}],optOut:[{hide:[".tsc-cookie-banner"]}]},{name:"trader-joes-com",cosmetic:!0,prehideSelectors:['div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'],detectCmp:[{exists:'div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'}],detectPopup:[{visible:'div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'}],optIn:[{click:'div[class^="CookiesAlert_cookiesAlert__container__"] button'}],optOut:[{hide:['div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]']}]},{name:"tropicfeel-com",prehideSelectors:["#shopify-section-cookies-controller"],detectCmp:[{exists:"#shopify-section-cookies-controller"}],detectPopup:[{visible:"#shopify-section-cookies-controller #cookies-controller-main-pane",check:"any"}],optIn:[{waitForThenClick:"#cookies-controller-main-pane form[data-form-allow-all] button"}],optOut:[{click:"#cookies-controller-main-pane a[data-tab-target=manage-cookies]"},{waitFor:"#manage-cookies-pane.active"},{click:"#manage-cookies-pane.active input[type=checkbox][checked]:not([disabled])",all:!0},{click:"#manage-cookies-pane.active button[type=submit]"}],test:[]},{name:"true-car",runContext:{urlPattern:"^https://www\\.truecar\\.com/"},cosmetic:!0,prehideSelectors:[['div[aria-labelledby="cookie-banner-heading"]']],detectCmp:[{exists:'div[aria-labelledby="cookie-banner-heading"]'}],detectPopup:[{visible:'div[aria-labelledby="cookie-banner-heading"]'}],optIn:[{click:'div[aria-labelledby="cookie-banner-heading"] > button[aria-label="Close"]'}],optOut:[{hide:['div[aria-labelledby="cookie-banner-heading"]']}]},{name:"truyo",prehideSelectors:["#truyo-consent-module"],detectCmp:[{exists:"#truyo-cookieBarContent"}],detectPopup:[{visible:"#truyo-consent-module"}],optIn:[{click:"button#acceptAllCookieButton"}],optOut:[{click:"button#declineAllCookieButton"}]},{name:"tumblr-com",cosmetic:!0,prehideSelectors:["#cmp-app-container"],detectCmp:[{exists:"#cmp-app-container"}],detectPopup:[{visible:"#cmp-app-container"}],optIn:[{click:"#tumblr #cmp-app-container div.components-modal__frame > iframe > html body > div > div > div.cmp__dialog-footer > div > button.components-button.white-space-normal.is-primary"}],optOut:[{hide:["#cmp-app-container"]}]},{name:"twitch.tv",runContext:{urlPattern:"^https?://(www\\.)?twitch\\.tv"},prehideSelectors:["div:has(> .consent-banner .consent-banner__content--gdpr-v2),.ReactModalPortal:has([data-a-target=consent-modal-save])"],detectCmp:[{exists:".consent-banner .consent-banner__content--gdpr-v2"}],detectPopup:[{visible:".consent-banner .consent-banner__content--gdpr-v2"}],optIn:[{click:'button[data-a-target="consent-banner-accept"]'}],optOut:[{hide:["div:has(> .consent-banner .consent-banner__content--gdpr-v2)"]},{click:'button[data-a-target="consent-banner-manage-preferences"]'},{waitFor:"input[type=checkbox][data-a-target=tw-checkbox]"},{click:"input[type=checkbox][data-a-target=tw-checkbox][checked]:not([disabled])",all:!0,optional:!0},{waitForThenClick:"[data-a-target=consent-modal-save]"},{waitForVisible:".ReactModalPortal:has([data-a-target=consent-modal-save])",check:"none"}]},{name:"twitter",runContext:{urlPattern:"^https://([a-z0-9-]+\\.)?twitter\\.com/"},prehideSelectors:['[data-testid="BottomBar"]'],detectCmp:[{exists:'[data-testid="BottomBar"] div'}],detectPopup:[{visible:'[data-testid="BottomBar"] div'}],optIn:[{waitForThenClick:'[data-testid="BottomBar"] > div:has(>div:first-child>div:last-child>span[role=button]) > div:last-child > div[role=button]:first-child'}],optOut:[{waitForThenClick:'[data-testid="BottomBar"] > div:has(>div:first-child>div:last-child>span[role=button]) > div:last-child > div[role=button]:last-child'}],TODOtest:[{eval:"EVAL_document.cookie.includes('d_prefs=MjoxLGNvbnNlbnRfdmVyc2lvbjoy')"}]},{name:"ubuntu.com",prehideSelectors:["dialog.cookie-policy"],detectCmp:[{any:[{exists:"dialog.cookie-policy header"},{exists:'xpath///*[@id="modal"]/div/header'}]}],detectPopup:[{any:[{visible:"dialog header"},{visible:'xpath///*[@id="modal"]/div/header'}]}],optIn:[{any:[{waitForThenClick:"#cookie-policy-button-accept"},{waitForThenClick:'xpath///*[@id="cookie-policy-button-accept"]'}]}],optOut:[{any:[{waitForThenClick:"button.p-button"},{waitForThenClick:'xpath///*[@id="cookie-policy-content"]/p[4]/button[2]'}]},{waitForThenClick:".p-switch__input:checked",optional:!0,all:!0},{any:[{waitForThenClick:"div > button"},{waitForThenClick:'xpath///*[@id="modal"]/div/button'}]}],test:[{eval:"EVAL_UBUNTU_COM_0"}]},{name:"UK Cookie Consent",prehideSelectors:["#catapult-cookie-bar"],cosmetic:!0,detectCmp:[{exists:"#catapult-cookie-bar"}],detectPopup:[{exists:".has-cookie-bar #catapult-cookie-bar"}],optIn:[{click:"#catapultCookie"}],optOut:[{hide:["#catapult-cookie-bar"]}],test:[{eval:"EVAL_UK_COOKIE_CONSENT_0"}]},{name:"urbanarmorgear-com",cosmetic:!0,prehideSelectors:['div[class^="Layout__CookieBannerContainer-"]'],detectCmp:[{exists:'div[class^="Layout__CookieBannerContainer-"]'}],detectPopup:[{visible:'div[class^="Layout__CookieBannerContainer-"]'}],optIn:[{click:'button[class^="CookieBanner__AcceptButton"]'}],optOut:[{hide:['div[class^="Layout__CookieBannerContainer-"]']}]},{name:"usercentrics-api",detectCmp:[{exists:"#usercentrics-root"}],detectPopup:[{eval:"EVAL_USERCENTRICS_API_0"},{exists:["#usercentrics-root","[data-testid=uc-container]"]}],optIn:[{eval:"EVAL_USERCENTRICS_API_3"},{eval:"EVAL_USERCENTRICS_API_1"},{eval:"EVAL_USERCENTRICS_API_5"}],optOut:[{eval:"EVAL_USERCENTRICS_API_1"},{eval:"EVAL_USERCENTRICS_API_2"}],test:[{eval:"EVAL_USERCENTRICS_API_6"}]},{name:"usercentrics-button",detectCmp:[{exists:"#usercentrics-button"}],detectPopup:[{visible:"#usercentrics-button #uc-btn-accept-banner"}],optIn:[{click:"#usercentrics-button #uc-btn-accept-banner"}],optOut:[{click:"#usercentrics-button #uc-btn-deny-banner"}],test:[{eval:"EVAL_USERCENTRICS_BUTTON_0"}]},{name:"uswitch.com",prehideSelectors:["#cookie-banner-wrapper"],detectCmp:[{exists:"#cookie-banner-wrapper"}],detectPopup:[{visible:"#cookie-banner-wrapper"}],optIn:[{click:"#cookie_banner_accept_mobile"}],optOut:[{click:"#cookie_banner_save"}]},{name:"vodafone.de",runContext:{urlPattern:"^https://www\\.vodafone\\.de/"},prehideSelectors:[".dip-consent,.dip-consent-container"],detectCmp:[{exists:".dip-consent-container"}],detectPopup:[{visible:".dip-consent-content"}],optOut:[{click:'.dip-consent-btn[tabindex="2"]'}],optIn:[{click:'.dip-consent-btn[tabindex="1"]'}]},{name:"waitrose.com",prehideSelectors:["div[aria-labelledby=CookieAlertModalHeading]","section[data-test=initial-waitrose-cookie-consent-banner]","section[data-test=cookie-consent-modal]"],detectCmp:[{exists:"section[data-test=initial-waitrose-cookie-consent-banner]"}],detectPopup:[{visible:"section[data-test=initial-waitrose-cookie-consent-banner]"}],optIn:[{click:"button[data-test=accept-all]"}],optOut:[{click:"button[data-test=manage-cookies]"},{wait:200},{eval:"EVAL_WAITROSE_0"},{click:"button[data-test=submit]"}],test:[{eval:"EVAL_WAITROSE_1"}]},{name:"wetransfer.com",detectCmp:[{exists:".welcome__cookie-notice"}],detectPopup:[{visible:".welcome__cookie-notice"}],optIn:[{click:".welcome__button--accept"}],optOut:[{click:".welcome__button--decline"}]},{name:"whitepages.com",runContext:{urlPattern:"^https://www\\.whitepages\\.com/"},cosmetic:!0,prehideSelectors:[".cookie-wrapper, .cookie-overlay"],detectCmp:[{exists:".cookie-wrapper"}],detectPopup:[{visible:".cookie-overlay"}],optIn:[{click:'button[aria-label="Got it"]'}],optOut:[{hide:[".cookie-wrapper"]}]},{name:"woo-commerce-com",prehideSelectors:[".wccom-comp-privacy-banner .wccom-privacy-banner"],detectCmp:[{exists:".wccom-comp-privacy-banner .wccom-privacy-banner"}],detectPopup:[{exists:".wccom-comp-privacy-banner .wccom-privacy-banner"}],optIn:[{click:".wccom-privacy-banner__content-buttons button.is-primary"}],optOut:[{click:".wccom-privacy-banner__content-buttons button.is-secondary"},{waitForThenClick:"input[type=checkbox][checked]:not([disabled])",all:!0},{click:"div.wccom-modal__footer > button"}]},{name:"WP Cookie Notice for GDPR",comment:"https://wordpress.org/plugins/gdpr-cookie-consent/",prehideSelectors:["#gdpr-cookie-consent-bar"],detectCmp:[{exists:"#gdpr-cookie-consent-bar"}],detectPopup:[{visible:"#gdpr-cookie-consent-bar"}],optIn:[{waitForThenClick:"#gdpr-cookie-consent-bar #cookie_action_accept"}],optOut:[{waitForThenClick:"#gdpr-cookie-consent-bar #cookie_action_reject"}],test:[{eval:"EVAL_WP_COOKIE_NOTICE_0"}]},{name:"wpcc",cosmetic:!0,prehideSelectors:[".wpcc-container"],detectCmp:[{exists:".wpcc-container"}],detectPopup:[{exists:".wpcc-container .wpcc-message"}],optIn:[{click:".wpcc-compliance .wpcc-btn"}],optOut:[{hide:[".wpcc-container"]}]},{name:"xhamster-eu",prehideSelectors:[".cookies-modal"],detectCmp:[{exists:".cookies-modal"}],detectPopup:[{exists:".cookies-modal"}],optIn:[{click:"button.cmd-button-accept-all"}],optOut:[{click:"button.cmd-button-reject-all"}]},{name:"xhamster-us",runContext:{urlPattern:"^https://(www\\.)?xhamster\\d?\\.com"},cosmetic:!0,prehideSelectors:[".cookie-announce"],detectCmp:[{exists:".cookie-announce"}],detectPopup:[{visible:".cookie-announce .announce-text"}],optIn:[{click:".cookie-announce button.xh-button"}],optOut:[{hide:[".cookie-announce"]}]},{name:"xing.com",detectCmp:[{exists:"div[class^=cookie-consent-CookieConsent]"}],detectPopup:[{exists:"div[class^=cookie-consent-CookieConsent]"}],optIn:[{click:"#consent-accept-button"}],optOut:[{click:"#consent-settings-button"},{click:".consent-banner-button-accept-overlay"}],test:[{eval:"EVAL_XING_0"}]},{name:"xnxx-com",cosmetic:!0,prehideSelectors:["#cookies-use-alert"],detectCmp:[{exists:"#cookies-use-alert"}],detectPopup:[{visible:"#cookies-use-alert"}],optIn:[{click:"#cookies-use-alert .close"}],optOut:[{hide:["#cookies-use-alert"]}]},{name:"youporn.com",cosmetic:!0,prehideSelectors:[".euCookieModal, #js_euCookieModal"],detectCmp:[{exists:".euCookieModal"}],detectPopup:[{exists:".euCookieModal, #js_euCookieModal"}],optIn:[{click:'button[name="user_acceptCookie"]'}],optOut:[{hide:[".euCookieModal"]}]},{name:"youtube-desktop",prehideSelectors:["tp-yt-iron-overlay-backdrop.opened","ytd-consent-bump-v2-lightbox"],detectCmp:[{exists:"ytd-consent-bump-v2-lightbox tp-yt-paper-dialog"},{exists:'ytd-consent-bump-v2-lightbox tp-yt-paper-dialog a[href^="https://consent.youtube.com/"]'}],detectPopup:[{visible:"ytd-consent-bump-v2-lightbox tp-yt-paper-dialog"}],optIn:[{waitForThenClick:"ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:last-child #button,ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:last-child button"},{wait:500}],optOut:[{waitForThenClick:"ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:first-child #button,ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:first-child button"},{wait:500}],test:[{wait:500},{eval:"EVAL_YOUTUBE_DESKTOP_0"}]},{name:"youtube-mobile",prehideSelectors:[".consent-bump-v2-lightbox"],detectCmp:[{exists:"ytm-consent-bump-v2-renderer"}],detectPopup:[{visible:"ytm-consent-bump-v2-renderer"}],optIn:[{waitForThenClick:"ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons c3-material-button:first-child button, ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons ytm-button-renderer:first-child button"},{wait:500}],optOut:[{waitForThenClick:"ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons c3-material-button:nth-child(2) button, ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons ytm-button-renderer:nth-child(2) button"},{wait:500}],test:[{wait:500},{eval:"EVAL_YOUTUBE_MOBILE_0"}]},{name:"zdf",prehideSelectors:["#zdf-cmp-banner-sdk"],detectCmp:[{exists:"#zdf-cmp-banner-sdk"}],detectPopup:[{visible:"#zdf-cmp-main.zdf-cmp-show"}],optIn:[{waitForThenClick:"#zdf-cmp-main #zdf-cmp-accept-btn"}],optOut:[{waitForThenClick:"#zdf-cmp-main #zdf-cmp-deny-btn"}],test:[]}],T={"didomi.io":{detectors:[{presentMatcher:{target:{selector:"#didomi-host, #didomi-notice"},type:"css"},showingMatcher:{target:{selector:"body.didomi-popup-open, .didomi-notice-banner"},type:"css"}}],methods:[{action:{target:{selector:".didomi-popup-notice-buttons .didomi-button:not(.didomi-button-highlight), .didomi-notice-banner .didomi-learn-more-button"},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{retries:50,target:{selector:"#didomi-purpose-cookies"},type:"waitcss",waitTime:50},{consents:[{description:"Share (everything) with others",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-share_whith_others]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-share_whith_others]:last-child"},type:"click"},type:"X"},{description:"Information storage and access",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-cookies]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-cookies]:last-child"},type:"click"},type:"D"},{description:"Content selection, offers and marketing",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-CL-T1Rgm7]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-CL-T1Rgm7]:last-child"},type:"click"},type:"E"},{description:"Analytics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-analytics]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-analytics]:last-child"},type:"click"},type:"B"},{description:"Analytics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-M9NRHJe3G]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-M9NRHJe3G]:last-child"},type:"click"},type:"B"},{description:"Ad and content selection",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-advertising_personalization]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-advertising_personalization]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection",falseAction:{parent:{childFilter:{target:{selector:"#didomi-purpose-pub-ciblee"}},selector:".didomi-consent-popup-data-processing, .didomi-components-accordion-label-container"},target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-pub-ciblee]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-pub-ciblee]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - basics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-q4zlJqdcD]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-q4zlJqdcD]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - partners and subsidiaries",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-partenaire-cAsDe8jC]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-partenaire-cAsDe8jC]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-p4em9a8m]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-p4em9a8m]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - others",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-autres-pub]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-autres-pub]:last-child"},type:"click"},type:"F"},{description:"Social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-reseauxsociaux]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-reseauxsociaux]:last-child"},type:"click"},type:"A"},{description:"Social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-social_media]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-social_media]:last-child"},type:"click"},type:"A"},{description:"Content selection",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-content_personalization]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-content_personalization]:last-child"},type:"click"},type:"E"},{description:"Ad delivery",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-ad_delivery]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-ad_delivery]:last-child"},type:"click"},type:"F"}],type:"consent"},{action:{consents:[{matcher:{childFilter:{target:{selector:":not(.didomi-components-radio__option--selected)"}},type:"css"},trueAction:{target:{selector:":nth-child(2)"},type:"click"},falseAction:{target:{selector:":first-child"},type:"click"},type:"X"}],type:"consent"},target:{selector:".didomi-components-radio"},type:"foreach"}],type:"list"},name:"DO_CONSENT"},{action:{parent:{selector:".didomi-consent-popup-footer .didomi-consent-popup-actions"},target:{selector:".didomi-components-button:first-child"},type:"click"},name:"SAVE_CONSENT"}]},oil:{detectors:[{presentMatcher:{target:{selector:".as-oil-content-overlay"},type:"css"},showingMatcher:{target:{selector:".as-oil-content-overlay"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".as-js-advanced-settings"},type:"click"},{retries:"10",target:{selector:".as-oil-cpc__purpose-container"},type:"waitcss",waitTime:"250"}],type:"list"},name:"OPEN_OPTIONS"},{action:{actions:[{consents:[{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Information storage and access","Opbevaring af og adgang til oplysninger på din enhed"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Information storage and access","Opbevaring af og adgang til oplysninger på din enhed"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"D"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personlige annoncer","Personalisation"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personlige annoncer","Personalisation"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"E"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Annoncevalg, levering og rapportering","Ad selection, delivery, reporting"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Annoncevalg, levering og rapportering","Ad selection, delivery, reporting"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"F"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personalisering af indhold","Content selection, delivery, reporting"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personalisering af indhold","Content selection, delivery, reporting"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"E"},{matcher:{parent:{childFilter:{target:{selector:".as-oil-cpc__purpose-header",textFilter:["Måling","Measurement"]}},selector:".as-oil-cpc__purpose-container"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{childFilter:{target:{selector:".as-oil-cpc__purpose-header",textFilter:["Måling","Measurement"]}},selector:".as-oil-cpc__purpose-container"},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"B"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:"Google"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:"Google"},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"F"}],type:"consent"}],type:"list"},name:"DO_CONSENT"},{action:{target:{selector:".as-oil__btn-optin"},type:"click"},name:"SAVE_CONSENT"},{action:{target:{selector:"div.as-oil"},type:"hide"},name:"HIDE_CMP"}]},optanon:{detectors:[{presentMatcher:{target:{selector:"#optanon-menu, .optanon-alert-box-wrapper"},type:"css"},showingMatcher:{target:{displayFilter:!0,selector:".optanon-alert-box-wrapper"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".optanon-alert-box-wrapper .optanon-toggle-display, a[onclick*='OneTrust.ToggleInfoDisplay()'], a[onclick*='Optanon.ToggleInfoDisplay()']"},type:"click"}],type:"list"},name:"OPEN_OPTIONS"},{action:{actions:[{target:{selector:".preference-menu-item #Your-privacy"},type:"click"},{target:{selector:"#optanon-vendor-consent-text"},type:"click"},{action:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},target:{selector:"#optanon-vendor-consent-list .vendor-item"},type:"foreach"},{target:{selector:".vendor-consent-back-link"},type:"click"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-performance"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-performance"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-functional"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-functional"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-advertising"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-advertising"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-social"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-social"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Social Media Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Social Media Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalisation"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalisation"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Site monitoring cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Site monitoring cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Third party privacy-enhanced content"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Third party privacy-enhanced content"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Performance & Advertising Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Performance & Advertising Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Information storage and access"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Information storage and access"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"D"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad selection, delivery, reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad selection, delivery, reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content selection, delivery, reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content selection, delivery, reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Measurement"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Measurement"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Recommended Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Recommended Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Unclassified Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Unclassified Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Analytical Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Analytical Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Marketing Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Marketing Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalization"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalization"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad Selection, Delivery & Reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad Selection, Delivery & Reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content Selection, Delivery & Reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content Selection, Delivery & Reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"}],type:"list"},name:"DO_CONSENT"},{action:{parent:{selector:".optanon-save-settings-button"},target:{selector:".optanon-white-button-middle"},type:"click"},name:"SAVE_CONSENT"},{action:{actions:[{target:{selector:"#optanon-popup-wrapper"},type:"hide"},{target:{selector:"#optanon-popup-bg"},type:"hide"},{target:{selector:".optanon-alert-box-wrapper"},type:"hide"}],type:"list"},name:"HIDE_CMP"}]},quantcast2:{detectors:[{presentMatcher:{target:{selector:"[data-tracking-opt-in-overlay]"},type:"css"},showingMatcher:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-learn-more]"},type:"css"}}],methods:[{action:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-learn-more]"},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{type:"wait",waitTime:500},{action:{actions:[{target:{selector:"div",textFilter:["Information storage and access"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"D"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Personalization"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"F"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Ad selection, delivery, reporting"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"F"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Content selection, delivery, reporting"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"E"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Measurement"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"B"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Other Partners"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},type:"ifcss"}],type:"list"},parent:{childFilter:{target:{selector:"input"}},selector:"[data-tracking-opt-in-overlay] > div > div"},target:{childFilter:{target:{selector:"input"}},selector:":scope > div"},type:"foreach"}],type:"list"},name:"DO_CONSENT"},{action:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-save]"},type:"click"},name:"SAVE_CONSENT"}]},springer:{detectors:[{presentMatcher:{parent:null,target:{selector:".cmp-app_gdpr"},type:"css"},showingMatcher:{parent:null,target:{displayFilter:!0,selector:".cmp-popup_popup"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".cmp-intro_rejectAll"},type:"click"},{type:"wait",waitTime:250},{target:{selector:".cmp-purposes_purposeItem:not(.cmp-purposes_selectedPurpose)"},type:"click"}],type:"list"},name:"OPEN_OPTIONS"},{action:{consents:[{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Przechowywanie informacji na urządzeniu lub dostęp do nich",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Przechowywanie informacji na urządzeniu lub dostęp do nich",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"D"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór podstawowych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór podstawowych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"F"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"F"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"E"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"E"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Stosowanie badań rynkowych w celu generowania opinii odbiorców",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Stosowanie badań rynkowych w celu generowania opinii odbiorców",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"X"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Opracowywanie i ulepszanie produktów",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Opracowywanie i ulepszanie produktów",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"X"}],type:"consent"},name:"DO_CONSENT"},{action:{target:{selector:".cmp-details_save"},type:"click"},name:"SAVE_CONSENT"}]},wordpressgdpr:{detectors:[{presentMatcher:{parent:null,target:{selector:".wpgdprc-consent-bar"},type:"css"},showingMatcher:{parent:null,target:{displayFilter:!0,selector:".wpgdprc-consent-bar"},type:"css"}}],methods:[{action:{parent:null,target:{selector:".wpgdprc-consent-bar .wpgdprc-consent-bar__settings",textFilter:null},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{target:{selector:".wpgdprc-consent-modal .wpgdprc-button",textFilter:"Eyeota"},type:"click"},{consents:[{description:"Eyeota Cookies",matcher:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Eyeota"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Eyeota"},target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},{target:{selector:".wpgdprc-consent-modal .wpgdprc-button",textFilter:"Advertising"},type:"click"},{consents:[{description:"Advertising Cookies",matcher:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Advertising"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Advertising"},target:{selector:"label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},name:"DO_CONSENT"},{action:{parent:null,target:{selector:".wpgdprc-button",textFilter:"Save my settings"},type:"click"},name:"SAVE_CONSENT"}]}},F={autoconsent:P,consentomatic:T},L=Object.freeze({__proto__:null,autoconsent:P,consentomatic:T,default:F});const N=new class{constructor(e,t=null,o=null){if(this.id=y(),this.rules=[],this.foundCmp=null,this.state={lifecycle:"loading",prehideOn:!1,findCmpAttempts:0,detectedCmps:[],detectedPopups:[],selfTest:null},C.sendContentMessage=e,this.sendContentMessage=e,this.rules=[],this.updateState({lifecycle:"loading"}),this.addDynamicRules(),t)this.initialize(t,o);else{o&&this.parseDeclarativeRules(o);e({type:"init",url:window.location.href}),this.updateState({lifecycle:"waitingForInitResponse"})}}initialize(e,t){if(this.config=e,e.enabled){if(t&&this.parseDeclarativeRules(t),this.rules=function(e,t){return e.filter((e=>(!t.disabledCmps||!t.disabledCmps.includes(e.name))&&(t.enableCosmeticRules||!e.isCosmetic)))}(this.rules,e),e.enablePrehide)if(document.documentElement)this.prehideElements();else{const e=()=>{window.removeEventListener("DOMContentLoaded",e),this.prehideElements()};window.addEventListener("DOMContentLoaded",e)}if("loading"===document.readyState){const e=()=>{window.removeEventListener("DOMContentLoaded",e),this.start()};window.addEventListener("DOMContentLoaded",e)}else this.start();this.updateState({lifecycle:"initialized"})}}addDynamicRules(){I.forEach((e=>{this.rules.push(new e(this))}))}parseDeclarativeRules(e){Object.keys(e.consentomatic).forEach((t=>{this.addConsentomaticCMP(t,e.consentomatic[t])})),e.autoconsent.forEach((e=>{this.addDeclarativeCMP(e)}))}addDeclarativeCMP(e){this.rules.push(new E(e,this))}addConsentomaticCMP(e,t){this.rules.push(new O(`com_${e}`,t))}start(){window.requestIdleCallback?window.requestIdleCallback((()=>this._start()),{timeout:500}):this._start()}async _start(){this.updateState({lifecycle:"started"});const e=await this.findCmp(this.config.detectRetries);if(this.updateState({detectedCmps:e.map((e=>e.name))}),0===e.length)return this.config.enablePrehide&&this.undoPrehide(),this.updateState({lifecycle:"nothingDetected"}),!1;this.updateState({lifecycle:"cmpDetected"});let t=await this.detectPopups(e.filter((e=>!e.isCosmetic)));if(0===t.length&&(t=await this.detectPopups(e.filter((e=>e.isCosmetic)))),0===t.length)return this.config.enablePrehide&&this.undoPrehide(),!1;if(this.updateState({lifecycle:"openPopupDetected"}),this.config.enablePrehide&&!this.state.prehideOn&&this.prehideElements(),t.length>1){const e={msg:"Found multiple CMPs, check the detection rules.",cmps:t.map((e=>e.name))};this.sendContentMessage({type:"autoconsentError",details:e})}return this.foundCmp=t[0],"optOut"===this.config.autoAction?await this.doOptOut():"optIn"!==this.config.autoAction||await this.doOptIn()}async findCmp(e){this.updateState({findCmpAttempts:this.state.findCmpAttempts+1});const t=[];for(const e of this.rules)try{if(!e.checkRunContext())continue;await e.detectCmp()&&(this.sendContentMessage({type:"cmpDetected",url:location.href,cmp:e.name}),t.push(e))}catch(e){}return 0===t.length&&e>0?(await b(500),this.findCmp(e-1)):t}async detectPopups(e){const t=[],o=e.map((e=>this.waitForPopup(e).then((o=>{o&&(this.updateState({detectedPopups:this.state.detectedPopups.concat([e.name])}),this.sendContentMessage({type:"popupFound",cmp:e.name,url:location.href}),t.push(e))})).catch((e=>null))));return await Promise.all(o),t}async doOptOut(){let e;return this.updateState({lifecycle:"runningOptOut"}),e=!!this.foundCmp&&await this.foundCmp.optOut(),this.config.enablePrehide&&this.undoPrehide(),this.sendContentMessage({type:"optOutResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:e,scheduleSelfTest:this.foundCmp&&this.foundCmp.hasSelfTest,url:location.href}),e&&!this.foundCmp.isIntermediate?(this.sendContentMessage({type:"autoconsentDone",cmp:this.foundCmp.name,isCosmetic:this.foundCmp.isCosmetic,url:location.href}),this.updateState({lifecycle:"done"})):this.updateState({lifecycle:e?"optOutSucceeded":"optOutFailed"}),e}async doOptIn(){let e;return this.updateState({lifecycle:"runningOptIn"}),e=!!this.foundCmp&&await this.foundCmp.optIn(),this.config.enablePrehide&&this.undoPrehide(),this.sendContentMessage({type:"optInResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:e,scheduleSelfTest:!1,url:location.href}),e&&!this.foundCmp.isIntermediate?(this.sendContentMessage({type:"autoconsentDone",cmp:this.foundCmp.name,isCosmetic:this.foundCmp.isCosmetic,url:location.href}),this.updateState({lifecycle:"done"})):this.updateState({lifecycle:e?"optInSucceeded":"optInFailed"}),e}async doSelfTest(){let e;return e=!!this.foundCmp&&await this.foundCmp.test(),this.sendContentMessage({type:"selfTestResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:e,url:location.href}),this.updateState({selfTest:e}),e}async waitForPopup(e,t=5,o=500){const c=await e.detectPopup().catch((e=>!1));return!c&&t>0?(await b(o),this.waitForPopup(e,t-1,o)):c}prehideElements(){const e=this.rules.reduce(((e,t)=>t.prehideSelectors?[...e,...t.prehideSelectors]:e),["#didomi-popup,.didomi-popup-container,.didomi-popup-notice,.didomi-consent-popup-preferences,#didomi-notice,.didomi-popup-backdrop,.didomi-screen-medium"]);return this.updateState({prehideOn:!0}),setTimeout((()=>{this.config.enablePrehide&&this.state.prehideOn&&!["runningOptOut","runningOptIn"].includes(this.state.lifecycle)&&this.undoPrehide()}),this.config.prehideTimeout||2e3),function(e){return s(a("autoconsent-prehide"),e,"opacity")}(e)}undoPrehide(){return this.updateState({prehideOn:!1}),function(){const e=a("autoconsent-prehide");return e&&e.remove(),!!e}()}updateState(e){Object.assign(this.state,e),this.sendContentMessage({type:"report",instanceId:this.id,url:window.location.href,mainFrame:window.top===window.self,state:this.state})}async receiveMessageCallback(e){switch(e.type){case"initResp":this.initialize(e.config,e.rules);break;case"optIn":await this.doOptIn();break;case"optOut":await this.doOptOut();break;case"selfTest":await this.doSelfTest();break;case"evalResp":!function(e,t){const o=C.pending.get(e);o?(C.pending.delete(e),o.timer&&window.clearTimeout(o.timer),o.resolve(t)):console.warn("no eval #",e)}(e.id,e.result)}}}((e=>{window.webkit.messageHandlers[e.type]&&window.webkit.messageHandlers[e.type].postMessage(e).then((e=>{N.receiveMessageCallback(e)}))}),null,L);window.autoconsentMessageCallback=e=>{N.receiveMessageCallback(e)}}(); +!function(){"use strict";var e=class e{static setBase(t){e.base=t}static findElement(t,o=null,c=!1){let i=null;return i=null!=o?Array.from(o.querySelectorAll(t.selector)):null!=e.base?Array.from(e.base.querySelectorAll(t.selector)):Array.from(document.querySelectorAll(t.selector)),null!=t.textFilter&&(i=i.filter((e=>{const o=e.textContent.toLowerCase();if(Array.isArray(t.textFilter)){let e=!1;for(const c of t.textFilter)if(-1!==o.indexOf(c.toLowerCase())){e=!0;break}return e}if(null!=t.textFilter)return-1!==o.indexOf(t.textFilter.toLowerCase())}))),null!=t.styleFilters&&(i=i.filter((e=>{const o=window.getComputedStyle(e);let c=!0;for(const e of t.styleFilters){const t=o[e.option];c=e.negated?c&&t!==e.value:c&&t===e.value}return c}))),null!=t.displayFilter&&(i=i.filter((e=>t.displayFilter?0!==e.offsetHeight:0===e.offsetHeight))),null!=t.iframeFilter&&(i=i.filter((()=>t.iframeFilter?window.location!==window.parent.location:window.location===window.parent.location))),null!=t.childFilter&&(i=i.filter((o=>{const c=e.base;e.setBase(o);const i=e.find(t.childFilter);return e.setBase(c),null!=i.target}))),c?i:(i.length>1&&console.warn("Multiple possible targets: ",i,t,o),i[0])}static find(t,o=!1){const c=[];if(null!=t.parent){const i=e.findElement(t.parent,null,o);if(null!=i){if(i instanceof Array)return i.forEach((i=>{const n=e.findElement(t.target,i,o);n instanceof Array?n.forEach((e=>{c.push({parent:i,target:e})})):c.push({parent:i,target:n})})),c;{const n=e.findElement(t.target,i,o);n instanceof Array?n.forEach((e=>{c.push({parent:i,target:e})})):c.push({parent:i,target:n})}}}else{const i=e.findElement(t.target,null,o);i instanceof Array?i.forEach((e=>{c.push({parent:null,target:e})})):c.push({parent:null,target:i})}return 0===c.length&&c.push({parent:null,target:null}),o?c:(1!==c.length&&console.warn("Multiple results found, even though multiple false",c),c[0])}};e.base=null;var t=e;function o(e){const o=t.find(e);return"css"===e.type?!!o.target:"checkbox"===e.type?!!o.target&&o.target.checked:void 0}async function c(e,a){switch(e.type){case"click":return async function(e){const o=t.find(e);null!=o.target&&o.target.click();return n(i)}(e);case"list":return async function(e,t){for(const o of e.actions)await c(o,t)}(e,a);case"consent":return async function(e,t){for(const i of e.consents){const e=-1!==t.indexOf(i.type);if(i.matcher&&i.toggleAction){o(i.matcher)!==e&&await c(i.toggleAction)}else e?await c(i.trueAction):await c(i.falseAction)}}(e,a);case"ifcss":return async function(e,o){const i=t.find(e);i.target?e.falseAction&&await c(e.falseAction,o):e.trueAction&&await c(e.trueAction,o)}(e,a);case"waitcss":return async function(e){await new Promise((o=>{let c=e.retries||10;const i=e.waitTime||250,n=()=>{const a=t.find(e);(e.negated&&a.target||!e.negated&&!a.target)&&c>0?(c-=1,setTimeout(n,i)):o()};n()}))}(e);case"foreach":return async function(e,o){const i=t.find(e,!0),n=t.base;for(const n of i)n.target&&(t.setBase(n.target),await c(e.action,o));t.setBase(n)}(e,a);case"hide":return async function(e){const o=t.find(e);o.target&&o.target.classList.add("Autoconsent-Hidden")}(e);case"slide":return async function(e){const o=t.find(e),c=t.find(e.dragTarget);if(o.target){const e=o.target.getBoundingClientRect(),t=c.target.getBoundingClientRect();let i=t.top-e.top,n=t.left-e.left;"y"===this.config.axis.toLowerCase()&&(n=0),"x"===this.config.axis.toLowerCase()&&(i=0);const a=window.screenX+e.left+e.width/2,s=window.screenY+e.top+e.height/2,r=e.left+e.width/2,l=e.top+e.height/2,p=document.createEvent("MouseEvents");p.initMouseEvent("mousedown",!0,!0,window,0,a,s,r,l,!1,!1,!1,!1,0,o.target);const d=document.createEvent("MouseEvents");d.initMouseEvent("mousemove",!0,!0,window,0,a+n,s+i,r+n,l+i,!1,!1,!1,!1,0,o.target);const u=document.createEvent("MouseEvents");u.initMouseEvent("mouseup",!0,!0,window,0,a+n,s+i,r+n,l+i,!1,!1,!1,!1,0,o.target),o.target.dispatchEvent(p),await this.waitTimeout(10),o.target.dispatchEvent(d),await this.waitTimeout(10),o.target.dispatchEvent(u)}}(e);case"close":return async function(){window.close()}();case"wait":return async function(e){await n(e.waitTime)}(e);case"eval":return async function(e){return console.log("eval!",e.code),new Promise((t=>{try{e.async?(window.eval(e.code),setTimeout((()=>{t(window.eval("window.__consentCheckResult"))}),e.timeout||250)):t(window.eval(e.code))}catch(o){console.warn("eval error",o,e.code),t(!1)}}))}(e);default:throw"Unknown action type: "+e.type}}var i=0;function n(e){return new Promise((t=>{setTimeout((()=>{t()}),e)}))}function a(){return crypto&&void 0!==crypto.randomUUID?crypto.randomUUID():Math.random().toString()}var s=class{constructor(e,t=1e3){this.id=e,this.promise=new Promise(((e,t)=>{this.resolve=e,this.reject=t})),this.timer=window.setTimeout((()=>{this.reject(new Error("timeout"))}),t)}},r={pending:new Map,sendContentMessage:null};var l={EVAL_0:()=>console.log(1),EVAL_CONSENTMANAGER_1:()=>window.__cmp&&"object"==typeof __cmp("getCMPData"),EVAL_CONSENTMANAGER_2:()=>!__cmp("consentStatus").userChoiceExists,EVAL_CONSENTMANAGER_3:()=>__cmp("setConsent",0),EVAL_CONSENTMANAGER_4:()=>__cmp("setConsent",1),EVAL_CONSENTMANAGER_5:()=>__cmp("consentStatus").userChoiceExists,EVAL_COOKIEBOT_1:()=>!!window.Cookiebot,EVAL_COOKIEBOT_2:()=>!window.Cookiebot.hasResponse&&!0===window.Cookiebot.dialog?.visible,EVAL_COOKIEBOT_3:()=>window.Cookiebot.withdraw()||!0,EVAL_COOKIEBOT_4:()=>window.Cookiebot.hide()||!0,EVAL_COOKIEBOT_5:()=>!0===window.Cookiebot.declined,EVAL_KLARO_1:()=>{const e=globalThis.klaroConfig||globalThis.klaro?.getManager&&globalThis.klaro.getManager().config;if(!e)return!0;const t=(e.services||e.apps).filter((e=>!e.required)).map((e=>e.name));if(klaro&&klaro.getManager){const e=klaro.getManager();return t.every((t=>!e.consents[t]))}if(klaroConfig&&"cookie"===klaroConfig.storageMethod){const e=klaroConfig.cookieName||klaroConfig.storageName,o=JSON.parse(decodeURIComponent(document.cookie.split(";").find((t=>t.trim().startsWith(e))).split("=")[1]));return Object.keys(o).filter((e=>t.includes(e))).every((e=>!1===o[e]))}},EVAL_ONETRUST_1:()=>window.OnetrustActiveGroups.split(",").filter((e=>e.length>0)).length<=1,EVAL_TRUSTARC_TOP:()=>window&&window.truste&&"0"===window.truste.eu.bindMap.prefCookie,EVAL_ADROLL_0:()=>!document.cookie.includes("__adroll_fpc"),EVAL_ALMACMP_0:()=>document.cookie.includes('"name":"Google","consent":false'),EVAL_AFFINITY_SERIF_COM_0:()=>document.cookie.includes("serif_manage_cookies_viewed")&&!document.cookie.includes("serif_allow_analytics"),EVAL_AXEPTIO_0:()=>document.cookie.includes("axeptio_authorized_vendors=%2C%2C"),EVAL_BING_0:()=>document.cookie.includes("AL=0")&&document.cookie.includes("AD=0")&&document.cookie.includes("SM=0"),EVAL_BLOCKSY_0:()=>document.cookie.includes("blocksy_cookies_consent_accepted=no"),EVAL_BORLABS_0:()=>!JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>-1!==e.indexOf("borlabs-cookie"))).split("=",2)[1])).consents.statistics,EVAL_BUNDESREGIERUNG_DE_0:()=>document.cookie.match("cookie-allow-tracking=0"),EVAL_CANVA_0:()=>!document.cookie.includes("gtm_fpc_engagement_event"),EVAL_CC_BANNER2_0:()=>!!document.cookie.match(/sncc=[^;]+D%3Dtrue/),EVAL_CLICKIO_0:()=>document.cookie.includes("__lxG__consent__v2_daisybit="),EVAL_CLINCH_0:()=>document.cookie.includes("ctc_rejected=1"),EVAL_COINBASE_0:()=>JSON.parse(decodeURIComponent(document.cookie.match(/cm_(eu|default)_preferences=([0-9a-zA-Z\\{\\}\\[\\]%:]*);?/)[2])).consent.length<=1,EVAL_COMPLIANZ_BANNER_0:()=>document.cookie.includes("cmplz_banner-status=dismissed"),EVAL_COMPLIANZ_CATEGORIES_0:()=>!!document.cookie.match(/cmplz_[^=]+=deny/),EVAL_COMPLIANZ_OPTIN_0:()=>!!document.cookie.match(/cookieconsent_preferences_disabled=[^;]+/),EVAL_COOKIE_LAW_INFO_0:()=>CLI.disableAllCookies()||CLI.reject_close()||!0,EVAL_COOKIE_LAW_INFO_1:()=>-1===document.cookie.indexOf("cookielawinfo-checkbox-non-necessary=yes"),EVAL_COOKIE_MANAGER_POPUP_0:()=>!1===JSON.parse(document.cookie.split(";").find((e=>e.trim().startsWith("CookieLevel"))).split("=")[1]).social,EVAL_COOKIEALERT_0:()=>document.querySelector("body").removeAttribute("style")||!0,EVAL_COOKIEALERT_1:()=>document.querySelector("body").removeAttribute("style")||!0,EVAL_COOKIEALERT_2:()=>!0===window.CookieConsent.declined,EVAL_COOKIEFIRST_0:()=>{return!1===(e=JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>-1!==e.indexOf("cookiefirst"))).trim()).split("=")[1])).performance&&!1===e.functional&&!1===e.advertising;var e},EVAL_COOKIEFIRST_1:()=>document.querySelectorAll("button[data-cookiefirst-accent-color=true][role=checkbox]:not([disabled])").forEach((e=>"true"==e.getAttribute("aria-checked")&&e.click()))||!0,EVAL_COOKIEINFORMATION_0:()=>CookieInformation.declineAllCategories()||!0,EVAL_COOKIEINFORMATION_1:()=>CookieInformation.submitAllCategories()||!0,EVAL_COOKIEINFORMATION_2:()=>document.cookie.includes("CookieInformationConsent="),EVAL_COOKIEYES_0:()=>document.cookie.includes("advertisement:no"),EVAL_DAILYMOTION_0:()=>!!document.cookie.match("dm-euconsent-v2"),EVAL_DSGVO_0:()=>!document.cookie.includes("sp_dsgvo_cookie_settings"),EVAL_DUNELM_0:()=>document.cookie.includes("cc_functional=0")&&document.cookie.includes("cc_targeting=0"),EVAL_ETSY_0:()=>document.querySelectorAll(".gdpr-overlay-body input").forEach((e=>{e.checked=!1}))||!0,EVAL_ETSY_1:()=>document.querySelector(".gdpr-overlay-view button[data-wt-overlay-close]").click()||!0,EVAL_EU_COOKIE_COMPLIANCE_0:()=>-1===document.cookie.indexOf("cookie-agreed=2"),EVAL_EU_COOKIE_LAW_0:()=>!document.cookie.includes("euCookie"),EVAL_EZOIC_0:()=>ezCMP.handleAcceptAllClick(),EVAL_EZOIC_1:()=>!!document.cookie.match(/ezCMPCookieConsent=[^;]+\|2=0\|3=0\|4=0/),EVAL_GOOGLE_0:()=>!!document.cookie.match(/SOCS=CAE/),EVAL_HEMA_TEST_0:()=>document.cookie.includes("cookies_rejected=1"),EVAL_IUBENDA_0:()=>document.querySelectorAll(".purposes-item input[type=checkbox]:not([disabled])").forEach((e=>{e.checked&&e.click()}))||!0,EVAL_IUBENDA_1:()=>!!document.cookie.match(/_iub_cs-\d+=/),EVAL_IWINK_TEST:()=>document.cookie.includes("cookie_permission_granted=no"),EVAL_JQUERY_COOKIEBAR_0:()=>!document.cookie.includes("cookies-state=accepted"),EVAL_MEDIAVINE_0:()=>document.querySelectorAll('[data-name="mediavine-gdpr-cmp"] input[type=checkbox]').forEach((e=>e.checked&&e.click()))||!0,EVAL_MICROSOFT_0:()=>Array.from(document.querySelectorAll("div > button")).filter((e=>e.innerText.match("Reject|Ablehnen")))[0].click()||!0,EVAL_MICROSOFT_1:()=>Array.from(document.querySelectorAll("div > button")).filter((e=>e.innerText.match("Accept|Annehmen")))[0].click()||!0,EVAL_MICROSOFT_2:()=>!!document.cookie.match("MSCC|GHCC"),EVAL_MOOVE_0:()=>document.querySelectorAll("#moove_gdpr_cookie_modal input").forEach((e=>{e.disabled||"moove_gdpr_strict_cookies"===e.name||(e.checked=!1)}))||!0,EVAL_ONENINETWO_0:()=>document.cookie.includes("CC_ADVERTISING=NO")&&document.cookie.includes("CC_ANALYTICS=NO"),EVAL_OPERA_0:()=>document.cookie.includes("cookie_consent_essential=true")&&!document.cookie.includes("cookie_consent_marketing=true"),EVAL_PAYPAL_0:()=>!0===document.cookie.includes("cookie_prefs"),EVAL_PRIMEBOX_0:()=>!document.cookie.includes("cb-enabled=accepted"),EVAL_PUBTECH_0:()=>document.cookie.includes("euconsent-v2")&&(document.cookie.match(/.YAAAAAAAAAAA/)||document.cookie.match(/.aAAAAAAAAAAA/)||document.cookie.match(/.YAAACFgAAAAA/)),EVAL_REDDIT_0:()=>document.cookie.includes("eu_cookie={%22opted%22:true%2C%22nonessential%22:false}"),EVAL_SIBBO_0:()=>!!window.localStorage.getItem("euconsent-v2"),EVAL_SIRDATA_0:()=>document.cookie.includes("euconsent-v2"),EVAL_SNIGEL_0:()=>!!document.cookie.match("snconsent"),EVAL_STEAMPOWERED_0:()=>2===JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>e.trim().startsWith("cookieSettings"))).split("=")[1])).preference_state,EVAL_TAKEALOT_0:()=>document.body.classList.remove("freeze")||(document.body.style="")||!0,EVAL_TARTEAUCITRON_0:()=>tarteaucitron.userInterface.respondAll(!1)||!0,EVAL_TARTEAUCITRON_1:()=>tarteaucitron.userInterface.respondAll(!0)||!0,EVAL_TARTEAUCITRON_2:()=>document.cookie.match(/tarteaucitron=[^;]*/)[0].includes("false"),EVAL_TEALIUM_0:()=>void 0!==window.utag&&"object"==typeof utag.gdpr,EVAL_TEALIUM_1:()=>utag.gdpr.setConsentValue(!1)||!0,EVAL_TEALIUM_DONOTSELL:()=>utag.gdpr.dns?.setDnsState(!1)||!0,EVAL_TEALIUM_2:()=>utag.gdpr.setConsentValue(!0)||!0,EVAL_TEALIUM_3:()=>1!==utag.gdpr.getConsentState(),EVAL_TEALIUM_DONOTSELL_CHECK:()=>1!==utag.gdpr.dns?.getDnsState(),EVAL_TESTCMP_0:()=>"button_clicked"===window.results.results[0],EVAL_TESTCMP_COSMETIC_0:()=>"banner_hidden"===window.results.results[0],EVAL_THEFREEDICTIONARY_0:()=>cmpUi.showPurposes()||cmpUi.rejectAll()||!0,EVAL_THEFREEDICTIONARY_1:()=>cmpUi.allowAll()||!0,EVAL_THEVERGE_0:()=>document.cookie.includes("_duet_gdpr_acknowledged=1"),EVAL_UBUNTU_COM_0:()=>"_cookies_accepted=essential"===document.cookie,EVAL_UK_COOKIE_CONSENT_0:()=>!document.cookie.includes("catAccCookies"),EVAL_USERCENTRICS_API_0:()=>"object"==typeof UC_UI,EVAL_USERCENTRICS_API_1:()=>!!UC_UI.closeCMP(),EVAL_USERCENTRICS_API_2:()=>!!UC_UI.denyAllConsents(),EVAL_USERCENTRICS_API_3:()=>!!UC_UI.acceptAllConsents(),EVAL_USERCENTRICS_API_4:()=>!!UC_UI.closeCMP(),EVAL_USERCENTRICS_API_5:()=>!0===UC_UI.areAllConsentsAccepted(),EVAL_USERCENTRICS_API_6:()=>!1===UC_UI.areAllConsentsAccepted(),EVAL_USERCENTRICS_BUTTON_0:()=>JSON.parse(localStorage.getItem("usercentrics")).consents.every((e=>e.isEssential||!e.consentStatus)),EVAL_WAITROSE_0:()=>Array.from(document.querySelectorAll("label[id$=cookies-deny-label]")).forEach((e=>e.click()))||!0,EVAL_WAITROSE_1:()=>document.cookie.includes("wtr_cookies_advertising=0")&&document.cookie.includes("wtr_cookies_analytics=0"),EVAL_WP_COOKIE_NOTICE_0:()=>document.cookie.includes("wpl_viewed_cookie=no"),EVAL_XING_0:()=>document.cookie.includes("userConsent=%7B%22marketing%22%3Afalse"),EVAL_YOUTUBE_DESKTOP_0:()=>!!document.cookie.match(/SOCS=CAE/),EVAL_YOUTUBE_MOBILE_0:()=>!!document.cookie.match(/SOCS=CAE/)};var p={main:!0,frame:!1,urlPattern:""},d=class{constructor(e){this.runContext=p,this.autoconsent=e}get hasSelfTest(){throw new Error("Not Implemented")}get isIntermediate(){throw new Error("Not Implemented")}get isCosmetic(){throw new Error("Not Implemented")}mainWorldEval(e){const t=l[e];if(!t)return console.warn("Snippet not found",e),Promise.resolve(!1);const o=this.autoconsent.config.logs;if(this.autoconsent.config.isMainWorld){o.evals&&console.log("inline eval:",e,t);let c=!1;try{c=!!t.call(globalThis)}catch(t){o.evals&&console.error("error evaluating rule",e,t)}return Promise.resolve(c)}const c=`(${t.toString()})()`;return o.evals&&console.log("async eval:",e,c),function(e,t){const o=a();r.sendContentMessage({type:"eval",id:o,code:e,snippetId:t});const c=new s(o);return r.pending.set(c.id,c),c.promise}(c,e).catch((t=>(o.evals&&console.error("error evaluating rule",e,t),!1)))}checkRunContext(){const e={...p,...this.runContext},t=window.top===window;return!(t&&!e.main)&&(!(!t&&!e.frame)&&!(e.urlPattern&&!window.location.href.match(e.urlPattern)))}detectCmp(){throw new Error("Not Implemented")}async detectPopup(){return!1}optOut(){throw new Error("Not Implemented")}optIn(){throw new Error("Not Implemented")}openCmp(){throw new Error("Not Implemented")}async test(){return Promise.resolve(!0)}click(e,t=!1){return this.autoconsent.domActions.click(e,t)}elementExists(e){return this.autoconsent.domActions.elementExists(e)}elementVisible(e,t){return this.autoconsent.domActions.elementVisible(e,t)}waitForElement(e,t){return this.autoconsent.domActions.waitForElement(e,t)}waitForVisible(e,t,o){return this.autoconsent.domActions.waitForVisible(e,t,o)}waitForThenClick(e,t,o){return this.autoconsent.domActions.waitForThenClick(e,t,o)}wait(e){return this.autoconsent.domActions.wait(e)}hide(e,t){return this.autoconsent.domActions.hide(e,t)}prehide(e){return this.autoconsent.domActions.prehide(e)}undoPrehide(){return this.autoconsent.domActions.undoPrehide()}querySingleReplySelector(e,t){return this.autoconsent.domActions.querySingleReplySelector(e,t)}querySelectorChain(e){return this.autoconsent.domActions.querySelectorChain(e)}elementSelector(e){return this.autoconsent.domActions.elementSelector(e)}},u=class extends d{constructor(e,t){super(t),this.rule=e,this.name=e.name,this.runContext=e.runContext||p}get hasSelfTest(){return!!this.rule.test}get isIntermediate(){return!!this.rule.intermediate}get isCosmetic(){return!!this.rule.cosmetic}get prehideSelectors(){return this.rule.prehideSelectors}async detectCmp(){return!!this.rule.detectCmp&&this._runRulesParallel(this.rule.detectCmp)}async detectPopup(){return!!this.rule.detectPopup&&this._runRulesSequentially(this.rule.detectPopup)}async optOut(){const e=this.autoconsent.config.logs;return!!this.rule.optOut&&(e.lifecycle&&console.log("Initiated optOut()",this.rule.optOut),this._runRulesSequentially(this.rule.optOut))}async optIn(){const e=this.autoconsent.config.logs;return!!this.rule.optIn&&(e.lifecycle&&console.log("Initiated optIn()",this.rule.optIn),this._runRulesSequentially(this.rule.optIn))}async openCmp(){return!!this.rule.openCmp&&this._runRulesSequentially(this.rule.openCmp)}async test(){return this.hasSelfTest?this._runRulesSequentially(this.rule.test):super.test()}async evaluateRuleStep(e){const t=[],o=this.autoconsent.config.logs;if(e.exists&&t.push(this.elementExists(e.exists)),e.visible&&t.push(this.elementVisible(e.visible,e.check)),e.eval){const o=this.mainWorldEval(e.eval);t.push(o)}if(e.waitFor&&t.push(this.waitForElement(e.waitFor,e.timeout)),e.waitForVisible&&t.push(this.waitForVisible(e.waitForVisible,e.timeout,e.check)),e.click&&t.push(this.click(e.click,e.all)),e.waitForThenClick&&t.push(this.waitForThenClick(e.waitForThenClick,e.timeout,e.all)),e.wait&&t.push(this.wait(e.wait)),e.hide&&t.push(this.hide(e.hide,e.method)),e.if){if(!e.if.exists&&!e.if.visible)return console.error("invalid conditional rule",e.if),!1;const c=await this.evaluateRuleStep(e.if);o.rulesteps&&console.log("Condition is",c),c?t.push(this._runRulesSequentially(e.then)):e.else&&t.push(this._runRulesSequentially(e.else))}if(e.any){for(const t of e.any)if(await this.evaluateRuleStep(t))return!0;return!1}if(0===t.length)return o.errors&&console.warn("Unrecognized rule",e),!1;return(await Promise.all(t)).reduce(((e,t)=>e&&t),!0)}async _runRulesParallel(e){const t=e.map((e=>this.evaluateRuleStep(e)));return(await Promise.all(t)).every((e=>!!e))}async _runRulesSequentially(e){const t=this.autoconsent.config.logs;for(const o of e){t.rulesteps&&console.log("Running rule...",o);const e=await this.evaluateRuleStep(o);if(t.rulesteps&&console.log("...rule result",e),!e&&!o.optional)return!1}return!0}},m=class{constructor(e,t){this.name=e,this.config=t,this.methods=new Map,this.runContext=p,this.isCosmetic=!1,t.methods.forEach((e=>{e.action&&this.methods.set(e.name,e.action)})),this.hasSelfTest=!1}get isIntermediate(){return!1}checkRunContext(){return!0}async detectCmp(){return this.config.detectors.map((e=>o(e.presentMatcher))).some((e=>!!e))}async detectPopup(){return this.config.detectors.map((e=>o(e.showingMatcher))).some((e=>!!e))}async executeAction(e,t){return!this.methods.has(e)||c(this.methods.get(e),t)}async optOut(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),await this.executeAction("HIDE_CMP"),await this.executeAction("DO_CONSENT",[]),await this.executeAction("SAVE_CONSENT"),!0}async optIn(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),await this.executeAction("HIDE_CMP"),await this.executeAction("DO_CONSENT",["D","A","B","E","F","X"]),await this.executeAction("SAVE_CONSENT"),!0}async openCmp(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),!0}async test(){return!0}};function h(e="autoconsent-css-rules"){const t=`style#${e}`,o=document.querySelector(t);if(o&&o instanceof HTMLStyleElement)return o;{const t=document.head||document.getElementsByTagName("head")[0]||document.documentElement,o=document.createElement("style");return o.id=e,t.appendChild(o),o}}function k(e,t,o="display"){const c=`${t} { ${"opacity"===o?"opacity: 0":"display: none"} !important; z-index: -1 !important; pointer-events: none !important; } `;return e instanceof HTMLStyleElement&&(e.innerText+=c,t.length>0)}async function b(e,t,o){const c=await e();return!c&&t>0?new Promise((c=>{setTimeout((async()=>{c(b(e,t-1,o))}),o)})):Promise.resolve(c)}function _(e){if(!e)return!1;if(null!==e.offsetParent)return!0;{const t=window.getComputedStyle(e);if("fixed"===t.position&&"none"!==t.display)return!0}return!1}var g="#truste-show-consent",y="#truste-consent-track",w=[class extends d{constructor(e){super(e),this.name="TrustArc-top",this.prehideSelectors=[".trustarc-banner-container",`.truste_popframe,.truste_overlay,.truste_box_overlay,${y}`],this.runContext={main:!0,frame:!1},this._shortcutButton=null,this._optInDone=!1}get hasSelfTest(){return!1}get isIntermediate(){return!this._optInDone&&!this._shortcutButton}get isCosmetic(){return!1}async detectCmp(){const e=this.elementExists(`${g},${y}`);return e&&(this._shortcutButton=document.querySelector("#truste-consent-required")),e}async detectPopup(){return this.elementVisible(`#truste-consent-content,#trustarc-banner-overlay,${y}`,"all")}openFrame(){this.click(g)}async optOut(){return this._shortcutButton?(this._shortcutButton.click(),!0):(k(h(),`.truste_popframe, .truste_overlay, .truste_box_overlay, ${y}`),this.click(g),setTimeout((()=>{h().remove()}),1e4),!0)}async optIn(){return this._optInDone=!0,this.click("#truste-consent-button")}async openCmp(){return!0}async test(){return await this.mainWorldEval("EVAL_TRUSTARC_TOP")}},class extends d{constructor(){super(...arguments),this.name="TrustArc-frame",this.runContext={main:!1,frame:!0,urlPattern:"^https://consent-pref\\.trustarc\\.com/\\?"}}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return!0}async detectPopup(){return this.elementVisible("#defaultpreferencemanager","any")&&this.elementVisible(".mainContent","any")}async navigateToSettings(){return await b((async()=>this.elementExists(".shp")||this.elementVisible(".advance","any")||this.elementExists(".switch span:first-child")),10,500),this.elementExists(".shp")&&this.click(".shp"),await this.waitForElement(".prefPanel",5e3),this.elementVisible(".advance","any")&&this.click(".advance"),await b((()=>this.elementVisible(".switch span:first-child","any")),5,1e3)}async optOut(){return await b((()=>"complete"===document.readyState),20,100),await this.waitForElement(".mainContent[aria-hidden=false]",5e3),!!this.click(".rejectAll")||(this.elementExists(".prefPanel")&&await this.waitForElement('.prefPanel[style="visibility: visible;"]',3e3),this.click("#catDetails0")?(this.click(".submit"),this.waitForThenClick("#gwt-debug-close_id",5e3),!0):this.click(".required")?(this.waitForThenClick("#gwt-debug-close_id",5e3),!0):(await this.navigateToSettings(),this.click(".switch span:nth-child(1):not(.active)",!0),this.click(".submit"),this.waitForThenClick("#gwt-debug-close_id",3e5),!0))}async optIn(){return this.click(".call")||(await this.navigateToSettings(),this.click(".switch span:nth-child(2)",!0),this.click(".submit"),this.waitForElement("#gwt-debug-close_id",3e5).then((()=>{this.click("#gwt-debug-close_id")}))),!0}},class extends d{constructor(){super(...arguments),this.name="Cybotcookiebot",this.prehideSelectors=["#CybotCookiebotDialog,#CybotCookiebotDialogBodyUnderlay,#dtcookie-container,#cookiebanner,#cb-cookieoverlay,.modal--cookie-banner,#cookiebanner_outer,#CookieBanner"]}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return await this.mainWorldEval("EVAL_COOKIEBOT_1")}async detectPopup(){return this.mainWorldEval("EVAL_COOKIEBOT_2")}async optOut(){await this.wait(500);let e=await this.mainWorldEval("EVAL_COOKIEBOT_3");return await this.wait(500),e=e&&await this.mainWorldEval("EVAL_COOKIEBOT_4"),e}async optIn(){return this.elementExists("#dtcookie-container")?this.click(".h-dtcookie-accept"):(this.click(".CybotCookiebotDialogBodyLevelButton:not(:checked):enabled",!0),this.click("#CybotCookiebotDialogBodyLevelButtonAccept"),this.click("#CybotCookiebotDialogBodyButtonAccept"),!0)}async test(){return await this.wait(500),await this.mainWorldEval("EVAL_COOKIEBOT_5")}},class extends d{constructor(){super(...arguments),this.name="Sourcepoint-frame",this.prehideSelectors=["div[id^='sp_message_container_'],.message-overlay","#sp_privacy_manager_container"],this.ccpaNotice=!1,this.ccpaPopup=!1,this.runContext={main:!1,frame:!0}}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){const e=new URL(location.href);return e.searchParams.has("message_id")&&"ccpa-notice.sp-prod.net"===e.hostname?(this.ccpaNotice=!0,!0):"ccpa-pm.sp-prod.net"===e.hostname?(this.ccpaPopup=!0,!0):("/index.html"===e.pathname||"/privacy-manager/index.html"===e.pathname||"/ccpa_pm/index.html"===e.pathname)&&(e.searchParams.has("message_id")||e.searchParams.has("requestUUID")||e.searchParams.has("consentUUID"))}async detectPopup(){return!!this.ccpaNotice||(this.ccpaPopup?await this.waitForElement(".priv-save-btn",2e3):(await this.waitForElement(".sp_choice_type_11,.sp_choice_type_12,.sp_choice_type_13,.sp_choice_type_ACCEPT_ALL,.sp_choice_type_SAVE_AND_EXIT",2e3),!this.elementExists(".sp_choice_type_9")))}async optIn(){return await this.waitForElement(".sp_choice_type_11,.sp_choice_type_ACCEPT_ALL",2e3),!!this.click(".sp_choice_type_11")||!!this.click(".sp_choice_type_ACCEPT_ALL")}isManagerOpen(){return"/privacy-manager/index.html"===location.pathname||"/ccpa_pm/index.html"===location.pathname}async optOut(){const e=this.autoconsent.config.logs;if(this.ccpaPopup){const e=document.querySelectorAll(".priv-purpose-container .sp-switch-arrow-block a.neutral.on .right");for(const t of e)t.click();const t=document.querySelectorAll(".priv-purpose-container .sp-switch-arrow-block a.switch-bg.on");for(const e of t)e.click();return this.click(".priv-save-btn")}if(!this.isManagerOpen()){if(!await this.waitForElement(".sp_choice_type_12,.sp_choice_type_13"))return!1;if(!this.elementExists(".sp_choice_type_12"))return this.click(".sp_choice_type_13");this.click(".sp_choice_type_12"),await b((()=>this.isManagerOpen()),200,100)}await this.waitForElement(".type-modal",2e4),this.waitForThenClick(".ccpa-stack .pm-switch[aria-checked=true] .slider",500,!0);try{const e=".sp_choice_type_REJECT_ALL",t=".reject-toggle",o=await Promise.race([this.waitForElement(e,2e3).then((e=>e?0:-1)),this.waitForElement(t,2e3).then((e=>e?1:-1)),this.waitForElement(".pm-features",2e3).then((e=>e?2:-1))]);if(0===o)return await this.wait(1e3),this.click(e);1===o?this.click(t):2===o&&(await this.waitForElement(".pm-features",1e4),this.click(".checked > span",!0),this.click(".chevron"))}catch(t){e.errors&&console.warn(t)}return this.click(".sp_choice_type_SAVE_AND_EXIT")}},class extends d{constructor(){super(...arguments),this.name="consentmanager.net",this.prehideSelectors=["#cmpbox,#cmpbox2"],this.apiAvailable=!1}get hasSelfTest(){return this.apiAvailable}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.apiAvailable=await this.mainWorldEval("EVAL_CONSENTMANAGER_1"),!!this.apiAvailable||this.elementExists("#cmpbox")}async detectPopup(){return this.apiAvailable?(await this.wait(500),await this.mainWorldEval("EVAL_CONSENTMANAGER_2")):this.elementVisible("#cmpbox .cmpmore","any")}async optOut(){return await this.wait(500),this.apiAvailable?await this.mainWorldEval("EVAL_CONSENTMANAGER_3"):!!this.click(".cmpboxbtnno")||(this.elementExists(".cmpwelcomeprpsbtn")?(this.click(".cmpwelcomeprpsbtn > a[aria-checked=true]",!0),this.click(".cmpboxbtnsave"),!0):(this.click(".cmpboxbtncustom"),await this.waitForElement(".cmptblbox",2e3),this.click(".cmptdchoice > a[aria-checked=true]",!0),this.click(".cmpboxbtnyescustomchoices"),!0))}async optIn(){return this.apiAvailable?await this.mainWorldEval("EVAL_CONSENTMANAGER_4"):this.click(".cmpboxbtnyes")}async test(){if(this.apiAvailable)return await this.mainWorldEval("EVAL_CONSENTMANAGER_5")}},class extends d{constructor(){super(...arguments),this.name="Evidon"}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists("#_evidon_banner")}async detectPopup(){return this.elementVisible("#_evidon_banner","any")}async optOut(){return this.click("#_evidon-decline-button")||(k(h(),"#evidon-prefdiag-overlay,#evidon-prefdiag-background"),this.click("#_evidon-option-button"),await this.waitForElement("#evidon-prefdiag-overlay",5e3),this.click("#evidon-prefdiag-decline")),!0}async optIn(){return this.click("#_evidon-accept-button")}},class extends d{constructor(){super(...arguments),this.name="Onetrust",this.prehideSelectors=["#onetrust-banner-sdk,#onetrust-consent-sdk,.onetrust-pc-dark-filter,.js-consent-banner"],this.runContext={urlPattern:"^(?!.*https://www\\.nba\\.com/)"}}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists("#onetrust-banner-sdk")}async detectPopup(){return this.elementVisible("#onetrust-banner-sdk","all")}async optOut(){return this.elementVisible("#onetrust-reject-all-handler,.js-reject-cookies","any")?this.click("#onetrust-reject-all-handler,.js-reject-cookies"):(this.elementExists("#onetrust-pc-btn-handler")?this.click("#onetrust-pc-btn-handler"):this.click(".ot-sdk-show-settings,button.js-cookie-settings"),await this.waitForElement("#onetrust-consent-sdk",2e3),await this.wait(1e3),this.click("#onetrust-consent-sdk input.category-switch-handler:checked,.js-editor-toggle-state:checked",!0),await this.wait(1e3),await this.waitForElement(".save-preference-btn-handler,.js-consent-save",2e3),this.click(".save-preference-btn-handler,.js-consent-save"),await this.waitForVisible("#onetrust-banner-sdk",5e3,"none"),!0)}async optIn(){return this.click("#onetrust-accept-btn-handler,.js-accept-cookies")}async test(){return await b((()=>this.mainWorldEval("EVAL_ONETRUST_1")),10,500)}},class extends d{constructor(){super(...arguments),this.name="Klaro",this.prehideSelectors=[".klaro"],this.settingsOpen=!1}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists(".klaro > .cookie-modal")?(this.settingsOpen=!0,!0):this.elementExists(".klaro > .cookie-notice")}async detectPopup(){return this.elementVisible(".klaro > .cookie-notice,.klaro > .cookie-modal","any")}async optOut(){return!!this.click(".klaro .cn-decline")||(this.settingsOpen||(this.click(".klaro .cn-learn-more,.klaro .cm-button-manage"),await this.waitForElement(".klaro > .cookie-modal",2e3),this.settingsOpen=!0),!!this.click(".klaro .cn-decline")||(this.click(".cm-purpose:not(.cm-toggle-all) > input:not(.half-checked,.required,.only-required),.cm-purpose:not(.cm-toggle-all) > div > input:not(.half-checked,.required,.only-required)",!0),this.click(".cm-btn-accept,.cm-button")))}async optIn(){return!!this.click(".klaro .cm-btn-accept-all")||(this.settingsOpen?(this.click(".cm-purpose:not(.cm-toggle-all) > input.half-checked",!0),this.click(".cm-btn-accept")):this.click(".klaro .cookie-notice .cm-btn-success"))}async test(){return await this.mainWorldEval("EVAL_KLARO_1")}},class extends d{constructor(){super(...arguments),this.name="Uniconsent"}get prehideSelectors(){return[".unic",".modal:has(.unic)"]}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists(".unic .unic-box,.unic .unic-bar")}async detectPopup(){return this.elementVisible(".unic .unic-box,.unic .unic-bar","any")}async optOut(){if(await this.waitForElement(".unic button",1e3),document.querySelectorAll(".unic button").forEach((e=>{const t=e.textContent;(t.includes("Manage Options")||t.includes("Optionen verwalten"))&&e.click()})),await this.waitForElement(".unic input[type=checkbox]",1e3)){await this.waitForElement(".unic button",1e3),document.querySelectorAll(".unic input[type=checkbox]").forEach((e=>{e.checked&&e.click()}));for(const e of document.querySelectorAll(".unic button")){const t=e.textContent;for(const o of["Confirm Choices","Save Choices","Auswahl speichern"])if(t.includes(o))return e.click(),await this.wait(500),!0}}return!1}async optIn(){return this.waitForThenClick(".unic #unic-agree")}async test(){await this.wait(1e3);return!this.elementExists(".unic .unic-box,.unic .unic-bar")}},class extends d{constructor(){super(...arguments),this.prehideSelectors=[".cmp-root"],this.name="Conversant"}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists(".cmp-root .cmp-receptacle")}async detectPopup(){return this.elementVisible(".cmp-root .cmp-receptacle","any")}async optOut(){if(!await this.waitForThenClick(".cmp-main-button:not(.cmp-main-button--primary)"))return!1;if(!await this.waitForElement(".cmp-view-tab-tabs"))return!1;await this.waitForThenClick(".cmp-view-tab-tabs > :first-child"),await this.waitForThenClick(".cmp-view-tab-tabs > .cmp-view-tab--active:first-child");for(const e of Array.from(document.querySelectorAll(".cmp-accordion-item"))){e.querySelector(".cmp-accordion-item-title").click(),await b((()=>!!e.querySelector(".cmp-accordion-item-content.cmp-active")),10,50);const t=e.querySelector(".cmp-accordion-item-content.cmp-active");t.querySelectorAll(".cmp-toggle-actions .cmp-toggle-deny:not(.cmp-toggle-deny--active)").forEach((e=>e.click())),t.querySelectorAll(".cmp-toggle-actions .cmp-toggle-checkbox:not(.cmp-toggle-checkbox--active)").forEach((e=>e.click()))}return await this.click(".cmp-main-button:not(.cmp-main-button--primary)"),!0}async optIn(){return this.waitForThenClick(".cmp-main-button.cmp-main-button--primary")}async test(){return document.cookie.includes("cmp-data=0")}},class extends d{constructor(){super(...arguments),this.name="tiktok.com",this.runContext={urlPattern:"tiktok"}}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}getShadowRoot(){const e=document.querySelector("tiktok-cookie-banner");return e?e.shadowRoot:null}async detectCmp(){return this.elementExists("tiktok-cookie-banner")}async detectPopup(){return _(this.getShadowRoot().querySelector(".tiktok-cookie-banner"))}async optOut(){const e=this.autoconsent.config.logs,t=this.getShadowRoot().querySelector(".button-wrapper button:first-child");return t?(e.rulesteps&&console.log("[clicking]",t),t.click(),!0):(e.errors&&console.log("no decline button found"),!1)}async optIn(){const e=this.autoconsent.config.logs,t=this.getShadowRoot().querySelector(".button-wrapper button:last-child");return t?(e.rulesteps&&console.log("[clicking]",t),t.click(),!0):(e.errors&&console.log("no accept button found"),!1)}async test(){const e=document.cookie.match(/cookie-consent=([^;]+)/);if(!e)return!1;const t=JSON.parse(decodeURIComponent(e[1]));return Object.values(t).every((e=>"boolean"!=typeof e||!1===e))}},class extends d{constructor(){super(...arguments),this.runContext={urlPattern:"^https://(www\\.)?airbnb\\.[^/]+/"},this.prehideSelectors=["div[data-testid=main-cookies-banner-container]",'div:has(> div:first-child):has(> div:last-child):has(> section [data-testid="strictly-necessary-cookies"])']}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists("div[data-testid=main-cookies-banner-container]")}async detectPopup(){return this.elementVisible("div[data-testid=main-cookies-banner-container","any")}async optOut(){let e;for(await this.waitForThenClick("div[data-testid=main-cookies-banner-container] button._snbhip0");e=document.querySelector("[data-testid=modal-container] button[aria-checked=true]:not([disabled])");)e.click();return this.waitForThenClick("button[data-testid=save-btn]")}async optIn(){return this.waitForThenClick("div[data-testid=main-cookies-banner-container] button._148dgdpk")}async test(){return await b((()=>!!document.cookie.match("OptanonAlertBoxClosed")),20,200)}}],C=class{constructor(e){this.autoconsentInstance=e}click(e,t=!1){const o=this.elementSelector(e);return this.autoconsentInstance.config.logs.rulesteps&&console.log("[click]",e,t,o),o.length>0&&(t?o.forEach((e=>e.click())):o[0].click()),o.length>0}elementExists(e){return this.elementSelector(e).length>0}elementVisible(e,t){const o=this.elementSelector(e),c=new Array(o.length);return o.forEach(((e,t)=>{c[t]=_(e)})),"none"===t?c.every((e=>!e)):0!==c.length&&("any"===t?c.some((e=>e)):c.every((e=>e)))}waitForElement(e,t=1e4){const o=Math.ceil(t/200);return this.autoconsentInstance.config.logs.rulesteps&&console.log("[waitForElement]",e),b((()=>this.elementSelector(e).length>0),o,200)}waitForVisible(e,t=1e4,o="any"){return b((()=>this.elementVisible(e,o)),Math.ceil(t/200),200)}async waitForThenClick(e,t=1e4,o=!1){return await this.waitForElement(e,t),this.click(e,o)}wait(e){return new Promise((t=>{setTimeout((()=>{t(!0)}),e)}))}hide(e,t){return k(h(),e,t)}prehide(e){const t=h("autoconsent-prehide");return this.autoconsentInstance.config.logs.lifecycle&&console.log("[prehide]",t,location.href),k(t,e,"opacity")}undoPrehide(){const e=h("autoconsent-prehide");return this.autoconsentInstance.config.logs.lifecycle&&console.log("[undoprehide]",e,location.href),e&&e.remove(),!!e}querySingleReplySelector(e,t=document){if(e.startsWith("aria/"))return[];if(e.startsWith("xpath/")){const o=e.slice(6),c=document.evaluate(o,t,null,XPathResult.ANY_TYPE,null);let i=null;const n=[];for(;i=c.iterateNext();)n.push(i);return n}return e.startsWith("text/")||e.startsWith("pierce/")?[]:t.shadowRoot?Array.from(t.shadowRoot.querySelectorAll(e)):Array.from(t.querySelectorAll(e))}querySelectorChain(e){let t,o=document;for(const c of e){if(t=this.querySingleReplySelector(c,o),0===t.length)return[];o=t[0]}return t}elementSelector(e){return"string"==typeof e?this.querySingleReplySelector(e):this.querySelectorChain(e)}};var v=[{name:"192.com",detectCmp:[{exists:".ont-cookies"}],detectPopup:[{visible:".ont-cookies"}],optIn:[{click:".ont-btn-main.ont-cookies-btn.js-ont-btn-ok2"}],optOut:[{click:".ont-cookes-btn-manage"},{click:".ont-btn-main.ont-cookies-btn.js-ont-btn-choose"}],test:[{eval:"EVAL_ONENINETWO_0"}]},{name:"1password-com",cosmetic:!0,prehideSelectors:['footer #footer-root [aria-label="Cookie Consent"]'],detectCmp:[{exists:'footer #footer-root [aria-label="Cookie Consent"]'}],detectPopup:[{visible:'footer #footer-root [aria-label="Cookie Consent"]'}],optIn:[{click:'footer #footer-root [aria-label="Cookie Consent"] button'}],optOut:[{hide:'footer #footer-root [aria-label="Cookie Consent"]'}]},{name:"activobank.pt",runContext:{urlPattern:"^https://(www\\.)?activobank\\.pt"},prehideSelectors:["aside#cookies,.overlay-cookies"],detectCmp:[{exists:"#cookies .cookies-btn"}],detectPopup:[{visible:"#cookies #submitCookies"}],optIn:[{waitForThenClick:"#cookies #submitCookies"}],optOut:[{waitForThenClick:"#cookies #rejectCookies"}]},{name:"Adroll",prehideSelectors:["#adroll_consent_container"],detectCmp:[{exists:"#adroll_consent_container"}],detectPopup:[{visible:"#adroll_consent_container"}],optIn:[{waitForThenClick:"#adroll_consent_accept"}],optOut:[{waitForThenClick:"#adroll_consent_reject"}],test:[{eval:"EVAL_ADROLL_0"}]},{name:"affinity.serif.com",detectCmp:[{exists:".c-cookie-banner button[data-qa='allow-all-cookies']"}],detectPopup:[{visible:".c-cookie-banner"}],optIn:[{click:'button[data-qa="allow-all-cookies"]'}],optOut:[{click:'button[data-qa="manage-cookies"]'},{waitFor:'.c-cookie-banner ~ [role="dialog"]'},{waitForThenClick:'.c-cookie-banner ~ [role="dialog"] input[type="checkbox"][value="true"]',all:!0},{click:'.c-cookie-banner ~ [role="dialog"] .c-modal__action button'}],test:[{wait:500},{eval:"EVAL_AFFINITY_SERIF_COM_0"}]},{name:"agolde.com",cosmetic:!0,prehideSelectors:["#modal-1 div[data-micromodal-close]"],detectCmp:[{exists:"#modal-1 div[aria-labelledby=modal-1-title]"}],detectPopup:[{exists:"#modal-1 div[data-micromodal-close]"}],optIn:[{click:'button[aria-label="Close modal"]'}],optOut:[{hide:"#modal-1 div[data-micromodal-close]"}]},{name:"almacmp",prehideSelectors:["#alma-cmpv2-container"],detectCmp:[{exists:"#alma-cmpv2-container"}],detectPopup:[{visible:"#alma-cmpv2-container #almacmp-modal-layer1"}],optIn:[{waitForThenClick:"#alma-cmpv2-container #almacmp-modal-layer1 #almacmp-modalConfirmBtn"}],optOut:[{waitForThenClick:"#alma-cmpv2-container #almacmp-modal-layer1 #almacmp-modalSettingBtn"},{waitFor:"#alma-cmpv2-container #almacmp-modal-layer2"},{waitForThenClick:"#alma-cmpv2-container #almacmp-modal-layer2 #almacmp-reject-all-layer2"}],test:[{eval:"EVAL_ALMACMP_0"}]},{name:"altium.com",cosmetic:!0,prehideSelectors:[".altium-privacy-bar"],detectCmp:[{exists:".altium-privacy-bar"}],detectPopup:[{exists:".altium-privacy-bar"}],optIn:[{click:"a.altium-privacy-bar__btn"}],optOut:[{hide:".altium-privacy-bar"}]},{name:"amazon.com",prehideSelectors:['span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'],detectCmp:[{exists:'span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'}],detectPopup:[{visible:'span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'}],optIn:[{waitForVisible:"#sp-cc-accept"},{wait:500},{click:"#sp-cc-accept"}],optOut:[{waitForVisible:"#sp-cc-rejectall-link"},{wait:500},{click:"#sp-cc-rejectall-link"}]},{name:"aquasana.com",cosmetic:!0,prehideSelectors:["#consent-tracking"],detectCmp:[{exists:"#consent-tracking"}],detectPopup:[{exists:"#consent-tracking"}],optIn:[{click:"#accept_consent"}],optOut:[{hide:"#consent-tracking"}]},{name:"athlinks-com",runContext:{urlPattern:"^https://(www\\.)?athlinks\\.com/"},cosmetic:!0,prehideSelectors:["#footer-container ~ div"],detectCmp:[{exists:"#footer-container ~ div"}],detectPopup:[{visible:"#footer-container > div"}],optIn:[{click:"#footer-container ~ div button"}],optOut:[{hide:"#footer-container ~ div"}]},{name:"ausopen.com",cosmetic:!0,detectCmp:[{exists:".gdpr-popup__message"}],detectPopup:[{visible:".gdpr-popup__message"}],optOut:[{hide:".gdpr-popup__message"}],optIn:[{click:".gdpr-popup__message button"}]},{name:"automattic-cmp-optout",prehideSelectors:['form[class*="cookie-banner"][method="post"]'],detectCmp:[{exists:'form[class*="cookie-banner"][method="post"]'}],detectPopup:[{visible:'form[class*="cookie-banner"][method="post"]'}],optIn:[{click:'a[class*="accept-all-button"]'}],optOut:[{click:'form[class*="cookie-banner"] div[class*="simple-options"] a[class*="customize-button"]'},{waitForThenClick:"input[type=checkbox][checked]:not([disabled])",all:!0},{click:'a[class*="accept-selection-button"]'}]},{name:"aws.amazon.com",prehideSelectors:["#awsccc-cb-content","#awsccc-cs-container","#awsccc-cs-modalOverlay","#awsccc-cs-container-inner"],detectCmp:[{exists:"#awsccc-cb-content"}],detectPopup:[{visible:"#awsccc-cb-content"}],optIn:[{click:"button[data-id=awsccc-cb-btn-accept"}],optOut:[{click:"button[data-id=awsccc-cb-btn-customize]"},{waitFor:"input[aria-checked]"},{click:"input[aria-checked=true]",all:!0,optional:!0},{click:"button[data-id=awsccc-cs-btn-save]"}]},{name:"axeptio",prehideSelectors:[".axeptio_widget"],detectCmp:[{exists:".axeptio_widget"}],detectPopup:[{visible:".axeptio_widget"}],optIn:[{waitFor:".axeptio-widget--open"},{click:"button#axeptio_btn_acceptAll"}],optOut:[{waitFor:".axeptio-widget--open"},{click:"button#axeptio_btn_dismiss"}],test:[{eval:"EVAL_AXEPTIO_0"}]},{name:"baden-wuerttemberg.de",prehideSelectors:[".cookie-alert.t-dark"],cosmetic:!0,detectCmp:[{exists:".cookie-alert.t-dark"}],detectPopup:[{visible:".cookie-alert.t-dark"}],optIn:[{click:".cookie-alert__form input:not([disabled]):not([checked])"},{click:".cookie-alert__button button"}],optOut:[{hide:".cookie-alert.t-dark"}]},{name:"bbb.org",runContext:{urlPattern:"^https://www\\.bbb\\.org/"},cosmetic:!0,prehideSelectors:['div[aria-label="use of cookies on bbb.org"]'],detectCmp:[{exists:'div[aria-label="use of cookies on bbb.org"]'}],detectPopup:[{visible:'div[aria-label="use of cookies on bbb.org"]'}],optIn:[{click:'div[aria-label="use of cookies on bbb.org"] button.bds-button-unstyled span.visually-hidden'}],optOut:[{hide:'div[aria-label="use of cookies on bbb.org"]'}]},{name:"bing.com",prehideSelectors:["#bnp_container"],detectCmp:[{exists:"#bnp_cookie_banner"}],detectPopup:[{visible:"#bnp_cookie_banner"}],optIn:[{click:"#bnp_btn_accept"}],optOut:[{click:"#bnp_btn_preference"},{click:"#mcp_savesettings"}],test:[{eval:"EVAL_BING_0"}]},{name:"blocksy",vendorUrl:"https://creativethemes.com/blocksy/docs/extensions/cookies-consent/",cosmetic:!1,runContext:{main:!0,frame:!1},intermediate:!1,prehideSelectors:[".cookie-notification"],detectCmp:[{exists:"#blocksy-ext-cookies-consent-styles-css"}],detectPopup:[{visible:".cookie-notification"}],optIn:[{click:".cookie-notification .ct-cookies-decline-button"}],optOut:[{waitForThenClick:".cookie-notification .ct-cookies-decline-button"}],test:[{eval:"EVAL_BLOCKSY_0"}]},{name:"borlabs",detectCmp:[{exists:"._brlbs-block-content"}],detectPopup:[{visible:"._brlbs-bar-wrap,._brlbs-box-wrap"}],optIn:[{click:"a[data-cookie-accept-all]"}],optOut:[{click:"a[data-cookie-individual]"},{waitForVisible:".cookie-preference"},{click:"input[data-borlabs-cookie-checkbox]:checked",all:!0,optional:!0},{click:"#CookiePrefSave"},{wait:500}],prehideSelectors:["#BorlabsCookieBox"],test:[{eval:"EVAL_BORLABS_0"}]},{name:"bundesregierung.de",prehideSelectors:[".bpa-cookie-banner"],detectCmp:[{exists:".bpa-cookie-banner"}],detectPopup:[{visible:".bpa-cookie-banner .bpa-module-full-hero"}],optIn:[{click:".bpa-accept-all-button"}],optOut:[{wait:500,comment:"click is not immediately recognized"},{waitForThenClick:".bpa-close-button"}],test:[{eval:"EVAL_BUNDESREGIERUNG_DE_0"}]},{name:"burpee.com",cosmetic:!0,prehideSelectors:["#notice-cookie-block"],detectCmp:[{exists:"#notice-cookie-block"}],detectPopup:[{exists:"#html-body #notice-cookie-block"}],optIn:[{click:"#btn-cookie-allow"}],optOut:[{hide:"#html-body #notice-cookie-block, #notice-cookie"}]},{name:"canva.com",prehideSelectors:['div[role="dialog"] a[data-anchor-id="cookie-policy"]'],detectCmp:[{exists:'div[role="dialog"] a[data-anchor-id="cookie-policy"]'}],detectPopup:[{exists:'div[role="dialog"] a[data-anchor-id="cookie-policy"]'}],optIn:[{click:'div[role="dialog"] button:nth-child(1)'}],optOut:[{if:{exists:'div[role="dialog"] button:nth-child(3)'},then:[{click:'div[role="dialog"] button:nth-child(2)'}],else:[{click:'div[role="dialog"] button:nth-child(2)'},{waitFor:'div[role="dialog"] a[data-anchor-id="privacy-policy"]'},{click:'div[role="dialog"] button:nth-child(2)'},{click:'div[role="dialog"] div:last-child button:only-child'}]}],test:[{eval:"EVAL_CANVA_0"}]},{name:"cc-banner-springer",prehideSelectors:[".cc-banner[data-cc-banner]"],detectCmp:[{exists:".cc-banner[data-cc-banner]"}],detectPopup:[{visible:".cc-banner[data-cc-banner]"}],optIn:[{waitForThenClick:".cc-banner[data-cc-banner] button[data-cc-action=accept]"}],optOut:[{if:{exists:".cc-banner[data-cc-banner] button[data-cc-action=reject]"},then:[{click:".cc-banner[data-cc-banner] button[data-cc-action=reject]"}],else:[{waitForThenClick:".cc-banner[data-cc-banner] button[data-cc-action=preferences]"},{waitFor:".cc-preferences[data-cc-preferences]"},{click:".cc-preferences[data-cc-preferences] input[type=radio][data-cc-action=toggle-category][value=off]",all:!0},{if:{exists:".cc-preferences[data-cc-preferences] button[data-cc-action=reject]"},then:[{click:".cc-preferences[data-cc-preferences] button[data-cc-action=reject]"}],else:[{click:".cc-preferences[data-cc-preferences] button[data-cc-action=save]"}]}]}],test:[{eval:"EVAL_CC_BANNER2_0"}]},{name:"cc_banner",cosmetic:!0,prehideSelectors:[".cc_banner-wrapper"],detectCmp:[{exists:".cc_banner-wrapper"}],detectPopup:[{visible:".cc_banner"}],optIn:[{click:".cc_btn_accept_all"}],optOut:[{hide:".cc_banner-wrapper"}]},{name:"ciaopeople.it",prehideSelectors:["#cp-gdpr-choices"],detectCmp:[{exists:"#cp-gdpr-choices"}],detectPopup:[{visible:"#cp-gdpr-choices"}],optIn:[{waitForThenClick:".gdpr-btm__right > button:nth-child(2)"}],optOut:[{waitForThenClick:".gdpr-top-content > button"},{waitFor:".gdpr-top-back"},{waitForThenClick:".gdpr-btm__right > button:nth-child(1)"}],test:[{visible:"#cp-gdpr-choices",check:"none"}]},{vendorUrl:"https://www.civicuk.com/cookie-control/",name:"civic-cookie-control",prehideSelectors:["#ccc-module,#ccc-overlay"],detectCmp:[{exists:"#ccc-module"}],detectPopup:[{visible:"#ccc"},{visible:"#ccc-module"}],optOut:[{click:"#ccc-reject-settings"}],optIn:[{click:"#ccc-recommended-settings"}]},{name:"click.io",prehideSelectors:["#cl-consent"],detectCmp:[{exists:"#cl-consent"}],detectPopup:[{visible:"#cl-consent"}],optIn:[{waitForThenClick:'#cl-consent [data-role="b_agree"]'}],optOut:[{waitFor:'#cl-consent [data-role="b_options"]'},{wait:500},{click:'#cl-consent [data-role="b_options"]'},{waitFor:'.cl-consent-popup.cl-consent-visible [data-role="alloff"]'},{click:'.cl-consent-popup.cl-consent-visible [data-role="alloff"]',all:!0},{click:'[data-role="b_save"]'}],test:[{eval:"EVAL_CLICKIO_0",comment:"TODO: this only checks if we interacted at all"}]},{name:"clinch",intermediate:!1,runContext:{frame:!1,main:!0},prehideSelectors:[".consent-modal[role=dialog]"],detectCmp:[{exists:".consent-modal[role=dialog]"}],detectPopup:[{visible:".consent-modal[role=dialog]"}],optIn:[{click:"#consent_agree"}],optOut:[{click:"#manage_cookie_preferences"},{click:"#cookie_consent_preferences input:checked",all:!0,optional:!0},{click:"#consent_save"}],test:[{eval:"EVAL_CLINCH_0"}]},{name:"clustrmaps.com",runContext:{urlPattern:"^https://(www\\.)?clustrmaps\\.com/"},cosmetic:!0,prehideSelectors:["#gdpr-cookie-message"],detectCmp:[{exists:"#gdpr-cookie-message"}],detectPopup:[{visible:"#gdpr-cookie-message"}],optIn:[{click:"button#gdpr-cookie-accept"}],optOut:[{hide:"#gdpr-cookie-message"}]},{name:"coinbase",intermediate:!1,runContext:{frame:!0,main:!0,urlPattern:"^https://(www|help)\\.coinbase\\.com"},prehideSelectors:[],detectCmp:[{exists:"div[class^=CookieBannerContent__Container]"}],detectPopup:[{visible:"div[class^=CookieBannerContent__Container]"}],optIn:[{click:"div[class^=CookieBannerContent__CTA] :nth-last-child(1)"}],optOut:[{click:"button[class^=CookieBannerContent__Settings]"},{click:"div[class^=CookiePreferencesModal__CategoryContainer] input:checked",all:!0,optional:!0},{click:"div[class^=CookiePreferencesModal__ButtonContainer] > button"}],test:[{eval:"EVAL_COINBASE_0"}]},{name:"Complianz banner",prehideSelectors:["#cmplz-cookiebanner-container"],detectCmp:[{exists:"#cmplz-cookiebanner-container .cmplz-cookiebanner"}],detectPopup:[{visible:"#cmplz-cookiebanner-container .cmplz-cookiebanner",check:"any"}],optIn:[{waitForThenClick:".cmplz-cookiebanner .cmplz-accept"}],optOut:[{waitForThenClick:".cmplz-cookiebanner .cmplz-deny"}],test:[{eval:"EVAL_COMPLIANZ_BANNER_0"}]},{name:"Complianz categories",prehideSelectors:['.cc-type-categories[aria-describedby="cookieconsent:desc"]'],detectCmp:[{exists:'.cc-type-categories[aria-describedby="cookieconsent:desc"]'}],detectPopup:[{visible:'.cc-type-categories[aria-describedby="cookieconsent:desc"]'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{click:".cc-dismiss"}],test:[{eval:"EVAL_COMPLIANZ_CATEGORIES_0"}]},{name:"Complianz notice",prehideSelectors:['.cc-type-info[aria-describedby="cookieconsent:desc"]'],cosmetic:!0,detectCmp:[{exists:'.cc-type-info[aria-describedby="cookieconsent:desc"]'}],detectPopup:[{visible:'.cc-type-info[aria-describedby="cookieconsent:desc"]'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{hide:'[aria-describedby="cookieconsent:desc"]'}]},{name:"Complianz optin",prehideSelectors:['.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'],detectCmp:[{exists:'.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'}],detectPopup:[{visible:'.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{click:".cc-settings"},{waitForVisible:'[aria-label="cookies preferences popup"]'},{click:'[aria-label="cookies preferences popup"] input[type=checkbox]:not([disabled]):checked',all:!0,optional:!0},{click:'[aria-label="cookies preferences popup"] [aria-label="Accept Selected"], [aria-label="cookies preferences popup"] [aria-label="Save my choice"], .cc-btn-accept-selected, .cc-deny',optional:!0}],test:[{eval:"EVAL_COMPLIANZ_OPTIN_0"}]},{name:"cookie-law-info",prehideSelectors:["#cookie-law-info-bar"],detectCmp:[{exists:"#cookie-law-info-bar"}],detectPopup:[{visible:"#cookie-law-info-bar"}],optIn:[{click:'[data-cli_action="accept_all"]'}],optOut:[{hide:"#cookie-law-info-bar"},{eval:"EVAL_COOKIE_LAW_INFO_0"}],test:[{eval:"EVAL_COOKIE_LAW_INFO_1"}]},{name:"cookie-manager-popup",cosmetic:!1,runContext:{main:!0,frame:!1},intermediate:!1,detectCmp:[{exists:"#notice-cookie-block #allow-functional-cookies, #notice-cookie-block #btn-cookie-settings"}],detectPopup:[{visible:"#notice-cookie-block"}],optIn:[{click:"#btn-cookie-allow"}],optOut:[{if:{exists:"#allow-functional-cookies"},then:[{click:"#allow-functional-cookies"}],else:[{waitForThenClick:"#btn-cookie-settings"},{waitForVisible:".modal-body"},{click:'.modal-body input:checked, .switch[data-switch="on"]',all:!0,optional:!0},{click:'[role="dialog"] .modal-footer button'}]}],prehideSelectors:["#btn-cookie-settings"],test:[{eval:"EVAL_COOKIE_MANAGER_POPUP_0"}]},{name:"cookie-notice",prehideSelectors:["#cookie-notice"],cosmetic:!0,detectCmp:[{visible:"#cookie-notice .cookie-notice-container"}],detectPopup:[{visible:"#cookie-notice"}],optIn:[{click:"#cn-accept-cookie"}],optOut:[{hide:"#cookie-notice"}]},{name:"cookie-script",vendorUrl:"https://cookie-script.com/",prehideSelectors:["#cookiescript_injected"],detectCmp:[{exists:"#cookiescript_injected"}],detectPopup:[{visible:"#cookiescript_injected"}],optOut:[{click:"#cookiescript_reject"}],optIn:[{click:"#cookiescript_accept"}]},{name:"cookiealert",intermediate:!1,prehideSelectors:[],runContext:{frame:!0,main:!0},detectCmp:[{exists:".cookie-alert-extended"}],detectPopup:[{visible:".cookie-alert-extended-modal"}],optIn:[{click:"button[data-controller='cookie-alert/extended/button/accept']"},{eval:"EVAL_COOKIEALERT_0"}],optOut:[{click:"a[data-controller='cookie-alert/extended/detail-link']"},{click:".cookie-alert-configuration-input:checked",all:!0,optional:!0},{click:"button[data-controller='cookie-alert/extended/button/configuration']"},{eval:"EVAL_COOKIEALERT_0"}],test:[{eval:"EVAL_COOKIEALERT_2"}]},{name:"cookiefirst.com",prehideSelectors:["#cookiefirst-root,.cookiefirst-root,[aria-labelledby=cookie-preference-panel-title]"],detectCmp:[{exists:"#cookiefirst-root,.cookiefirst-root"}],detectPopup:[{visible:"#cookiefirst-root,.cookiefirst-root"}],optIn:[{click:"button[data-cookiefirst-action=accept]"}],optOut:[{if:{exists:"button[data-cookiefirst-action=adjust]"},then:[{click:"button[data-cookiefirst-action=adjust]"},{waitForVisible:"[data-cookiefirst-widget=modal]",timeout:1e3},{eval:"EVAL_COOKIEFIRST_1"},{wait:1e3},{click:"button[data-cookiefirst-action=save]"}],else:[{click:"button[data-cookiefirst-action=reject]"}]}],test:[{eval:"EVAL_COOKIEFIRST_0"}]},{name:"Cookie Information Banner",prehideSelectors:["#cookie-information-template-wrapper"],detectCmp:[{exists:"#cookie-information-template-wrapper"}],detectPopup:[{visible:"#cookie-information-template-wrapper"}],optIn:[{eval:"EVAL_COOKIEINFORMATION_1"}],optOut:[{hide:"#cookie-information-template-wrapper",comment:"some templates don't hide the banner automatically"},{eval:"EVAL_COOKIEINFORMATION_0"}],test:[{eval:"EVAL_COOKIEINFORMATION_2"}]},{name:"cookieyes",prehideSelectors:[".cky-overlay,.cky-consent-container"],detectCmp:[{exists:".cky-consent-container"}],detectPopup:[{visible:".cky-consent-container"}],optIn:[{waitForThenClick:".cky-consent-container [data-cky-tag=accept-button]"}],optOut:[{if:{exists:".cky-consent-container [data-cky-tag=reject-button]"},then:[{waitForThenClick:".cky-consent-container [data-cky-tag=reject-button]"}],else:[{if:{exists:".cky-consent-container [data-cky-tag=settings-button]"},then:[{click:".cky-consent-container [data-cky-tag=settings-button]"},{waitFor:".cky-modal-open input[type=checkbox]"},{click:".cky-modal-open input[type=checkbox]:checked",all:!0,optional:!0},{waitForThenClick:".cky-modal [data-cky-tag=detail-save-button]"}],else:[{hide:".cky-consent-container,.cky-overlay"}]}]}],test:[{eval:"EVAL_COOKIEYES_0"}]},{name:"corona-in-zahlen.de",prehideSelectors:[".cookiealert"],detectCmp:[{exists:".cookiealert"}],detectPopup:[{visible:".cookiealert"}],optOut:[{click:".configurecookies"},{click:".confirmcookies"}],optIn:[{click:".acceptcookies"}]},{name:"crossfit-com",cosmetic:!0,prehideSelectors:['body #modal > div > div[class^="_wrapper_"]'],detectCmp:[{exists:'body #modal > div > div[class^="_wrapper_"]'}],detectPopup:[{visible:'body #modal > div > div[class^="_wrapper_"]'}],optIn:[{click:'button[aria-label="accept cookie policy"]'}],optOut:[{hide:'body #modal > div > div[class^="_wrapper_"]'}]},{name:"csu-landtag-de",runContext:{urlPattern:"^https://(www|)?\\.csu-landtag\\.de"},prehideSelectors:["#cookie-disclaimer"],detectCmp:[{exists:"#cookie-disclaimer"}],detectPopup:[{visible:"#cookie-disclaimer"}],optIn:[{click:"#cookieall"}],optOut:[{click:"#cookiesel"}]},{name:"dailymotion-us",cosmetic:!0,prehideSelectors:['div[class*="CookiePopup__desktopContainer"]:has(div[class*="CookiePopup"])'],detectCmp:[{exists:'div[class*="CookiePopup__desktopContainer"]'}],detectPopup:[{visible:'div[class*="CookiePopup__desktopContainer"]'}],optIn:[{click:'div[class*="CookiePopup__desktopContainer"] > button > span'}],optOut:[{hide:'div[class*="CookiePopup__desktopContainer"]'}]},{name:"dailymotion.com",runContext:{urlPattern:"^https://(www\\.)?dailymotion\\.com/"},prehideSelectors:['div[class*="Overlay__container"]:has(div[class*="TCF2Popup"])'],detectCmp:[{exists:'div[class*="TCF2Popup"]'}],detectPopup:[{visible:'[class*="TCF2Popup"] a[href^="https://www.dailymotion.com/legal/cookiemanagement"]'}],optIn:[{waitForThenClick:'button[class*="TCF2Popup__button"]:not([class*="TCF2Popup__personalize"])'}],optOut:[{waitForThenClick:'button[class*="TCF2ContinueWithoutAcceptingButton"]'}],test:[{eval:"EVAL_DAILYMOTION_0"}]},{name:"deepl.com",prehideSelectors:[".dl_cookieBanner_container"],detectCmp:[{exists:".dl_cookieBanner_container"}],detectPopup:[{visible:".dl_cookieBanner_container"}],optOut:[{click:".dl_cookieBanner--buttonSelected"}],optIn:[{click:".dl_cookieBanner--buttonAll"}]},{name:"delta.com",runContext:{urlPattern:"^https://www\\.delta\\.com/"},cosmetic:!0,prehideSelectors:["ngc-cookie-banner"],detectCmp:[{exists:"div.cookie-footer-container"}],detectPopup:[{visible:"div.cookie-footer-container"}],optIn:[{click:" button.cookie-close-icon"}],optOut:[{hide:"div.cookie-footer-container"}]},{name:"dmgmedia-us",prehideSelectors:["#mol-ads-cmp-iframe, div.mol-ads-cmp > form > div"],detectCmp:[{exists:"div.mol-ads-cmp > form > div"}],detectPopup:[{waitForVisible:"div.mol-ads-cmp > form > div"}],optIn:[{waitForThenClick:"button.mol-ads-cmp--btn-primary"}],optOut:[{waitForThenClick:"div.mol-ads-ccpa--message > u > a"},{waitForVisible:".mol-ads-cmp--modal-dialog"},{waitForThenClick:"a.mol-ads-cmp-footer-privacy"},{waitForThenClick:"button.mol-ads-cmp--btn-secondary"}]},{name:"dmgmedia",prehideSelectors:['[data-project="mol-fe-cmp"]'],detectCmp:[{exists:'[data-project="mol-fe-cmp"]'}],detectPopup:[{visible:'[data-project="mol-fe-cmp"]'}],optIn:[{waitForThenClick:'[data-project="mol-fe-cmp"] button[class*=primary]'}],optOut:[{waitForThenClick:'[data-project="mol-fe-cmp"] button[class*=basic]'},{waitForVisible:'[data-project="mol-fe-cmp"] div[class*="tabContent"]'},{waitForThenClick:'[data-project="mol-fe-cmp"] div[class*="toggle"][class*="enabled"]',all:!0},{waitForThenClick:'[data-project="mol-fe-cmp"] button[class*=white]'}]},{name:"Drupal",detectCmp:[{exists:"#drupalorg-crosssite-gdpr"}],detectPopup:[{visible:"#drupalorg-crosssite-gdpr"}],optOut:[{click:".no"}],optIn:[{click:".yes"}]},{name:"WP DSGVO Tools",link:"https://wordpress.org/plugins/shapepress-dsgvo/",prehideSelectors:[".sp-dsgvo"],cosmetic:!0,detectCmp:[{exists:".sp-dsgvo.sp-dsgvo-popup-overlay"}],detectPopup:[{visible:".sp-dsgvo.sp-dsgvo-popup-overlay",check:"any"}],optIn:[{click:".sp-dsgvo-privacy-btn-accept-all",all:!0}],optOut:[{hide:".sp-dsgvo.sp-dsgvo-popup-overlay"}],test:[{eval:"EVAL_DSGVO_0"}]},{name:"dunelm.com",prehideSelectors:["div[data-testid=cookie-consent-modal-backdrop]"],detectCmp:[{exists:"div[data-testid=cookie-consent-message-contents]"}],detectPopup:[{visible:"div[data-testid=cookie-consent-message-contents]"}],optIn:[{click:'[data-testid="cookie-consent-allow-all"]'}],optOut:[{click:"button[data-testid=cookie-consent-adjust-settings]"},{click:"button[data-testid=cookie-consent-preferences-save]"}],test:[{eval:"EVAL_DUNELM_0"}]},{name:"etsy",prehideSelectors:["#gdpr-single-choice-overlay","#gdpr-privacy-settings"],detectCmp:[{exists:"#gdpr-single-choice-overlay"}],detectPopup:[{visible:"#gdpr-single-choice-overlay"}],optOut:[{click:"button[data-gdpr-open-full-settings]"},{waitForVisible:".gdpr-overlay-body input",timeout:3e3},{wait:1e3},{eval:"EVAL_ETSY_0"},{eval:"EVAL_ETSY_1"}],optIn:[{click:"button[data-gdpr-single-choice-accept]"}]},{name:"eu-cookie-compliance-banner",detectCmp:[{exists:".eu-cookie-compliance-banner-info"}],detectPopup:[{exists:".eu-cookie-compliance-popup-open"}],optIn:[{click:".agree-button"}],optOut:[{click:".decline-button,.eu-cookie-compliance-save-preferences-button",optional:!0},{hide:".eu-cookie-compliance-banner-info, #sliding-popup"}],test:[{eval:"EVAL_EU_COOKIE_COMPLIANCE_0"}]},{name:"EU Cookie Law",prehideSelectors:[".pea_cook_wrapper,.pea_cook_more_info_popover"],cosmetic:!0,detectCmp:[{exists:".pea_cook_wrapper"}],detectPopup:[{wait:500},{visible:".pea_cook_wrapper"}],optIn:[{click:"#pea_cook_btn"}],optOut:[{hide:".pea_cook_wrapper"}],test:[{eval:"EVAL_EU_COOKIE_LAW_0"}]},{name:"EZoic",prehideSelectors:["#ez-cookie-dialog-wrapper"],detectCmp:[{exists:"#ez-cookie-dialog-wrapper"}],detectPopup:[{visible:"#ez-cookie-dialog-wrapper"}],optIn:[{click:"#ez-accept-all",optional:!0},{eval:"EVAL_EZOIC_0",optional:!0}],optOut:[{wait:500},{click:"#ez-manage-settings"},{waitFor:"#ez-cookie-dialog input[type=checkbox]"},{click:"#ez-cookie-dialog input[type=checkbox][checked]",all:!0},{click:"#ez-save-settings"}],test:[{eval:"EVAL_EZOIC_1"}]},{name:"facebook",runContext:{urlPattern:"^https://([a-z0-9-]+\\.)?facebook\\.com/"},prehideSelectors:['div[data-testid="cookie-policy-manage-dialog"]'],detectCmp:[{exists:'div[data-testid="cookie-policy-manage-dialog"]'}],detectPopup:[{visible:'div[data-testid="cookie-policy-manage-dialog"]'}],optIn:[{waitForThenClick:'button[data-cookiebanner="accept_button"]'},{waitForVisible:'div[data-testid="cookie-policy-manage-dialog"]',check:"none"}],optOut:[{waitForThenClick:'button[data-cookiebanner="accept_only_essential_button"]'},{waitForVisible:'div[data-testid="cookie-policy-manage-dialog"]',check:"none"}]},{name:"funding-choices",prehideSelectors:[".fc-consent-root,.fc-dialog-container,.fc-dialog-overlay,.fc-dialog-content"],detectCmp:[{exists:".fc-consent-root"}],detectPopup:[{exists:".fc-dialog-container"}],optOut:[{click:".fc-cta-do-not-consent,.fc-cta-manage-options"},{click:".fc-preference-consent:checked,.fc-preference-legitimate-interest:checked",all:!0,optional:!0},{click:".fc-confirm-choices",optional:!0}],optIn:[{click:".fc-cta-consent"}]},{name:"geeks-for-geeks",runContext:{urlPattern:"^https://www\\.geeksforgeeks\\.org/"},cosmetic:!0,prehideSelectors:[".cookie-consent"],detectCmp:[{exists:".cookie-consent"}],detectPopup:[{visible:".cookie-consent"}],optIn:[{click:".cookie-consent button.consent-btn"}],optOut:[{hide:".cookie-consent"}]},{name:"generic-cosmetic",cosmetic:!0,prehideSelectors:["#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"],detectCmp:[{exists:"#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"}],detectPopup:[{visible:"#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"}],optIn:[],optOut:[{hide:"#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"}]},{name:"google-consent-standalone",prehideSelectors:[],detectCmp:[{exists:'a[href^="https://policies.google.com/technologies/cookies"'},{exists:'form[action^="https://consent.google."][action$=".com/save"]'}],detectPopup:[{visible:'a[href^="https://policies.google.com/technologies/cookies"'}],optIn:[{waitForThenClick:'form[action^="https://consent.google."][action$=".com/save"]:has(input[name=set_eom][value=false]) button'}],optOut:[{waitForThenClick:'form[action^="https://consent.google."][action$=".com/save"]:has(input[name=set_eom][value=true]) button'}]},{name:"google.com",prehideSelectors:[".HTjtHe#xe7COe"],detectCmp:[{exists:".HTjtHe#xe7COe"},{exists:'.HTjtHe#xe7COe a[href^="https://policies.google.com/technologies/cookies"]'}],detectPopup:[{visible:".HTjtHe#xe7COe button#W0wltc"}],optIn:[{waitForThenClick:".HTjtHe#xe7COe button#L2AGLb"}],optOut:[{waitForThenClick:".HTjtHe#xe7COe button#W0wltc"}],test:[{eval:"EVAL_GOOGLE_0"}]},{name:"gov.uk",detectCmp:[{exists:"#global-cookie-message"}],detectPopup:[{exists:"#global-cookie-message"}],optIn:[{click:"button[data-accept-cookies=true]"}],optOut:[{click:"button[data-reject-cookies=true],#reject-cookies"},{click:"button[data-hide-cookie-banner=true],#hide-cookie-decision"}]},{name:"healthline-media",prehideSelectors:["#modal-host > div.no-hash > div.window-wrapper"],detectCmp:[{exists:"#modal-host > div.no-hash > div.window-wrapper, div[data-testid=qualtrics-container]"}],detectPopup:[{exists:"#modal-host > div.no-hash > div.window-wrapper, div[data-testid=qualtrics-container]"}],optIn:[{click:"#modal-host > div.no-hash > div.window-wrapper > div:last-child button"}],optOut:[{if:{exists:'#modal-host > div.no-hash > div.window-wrapper > div:last-child a[href="/privacy-settings"]'},then:[{click:'#modal-host > div.no-hash > div.window-wrapper > div:last-child a[href="/privacy-settings"]'}],else:[{waitForVisible:"div#__next"},{click:"#__next div:nth-child(1) > button:first-child"}]}]},{name:"hema",prehideSelectors:[".cookie-modal"],detectCmp:[{visible:".cookie-modal .cookie-accept-btn"}],detectPopup:[{visible:".cookie-modal .cookie-accept-btn"}],optIn:[{waitForThenClick:".cookie-modal .cookie-accept-btn"}],optOut:[{waitForThenClick:".cookie-modal .js-cookie-reject-btn"}],test:[{eval:"EVAL_HEMA_TEST_0"}]},{name:"hl.co.uk",prehideSelectors:[".cookieModalContent","#cookie-banner-overlay"],detectCmp:[{exists:"#cookie-banner-overlay"}],detectPopup:[{exists:"#cookie-banner-overlay"}],optIn:[{click:"#acceptCookieButton"}],optOut:[{click:"#manageCookie"},{hide:".cookieSettingsModal"},{waitFor:"#AOCookieToggle"},{click:"#AOCookieToggle[aria-pressed=true]",optional:!0},{waitFor:"#TPCookieToggle"},{click:"#TPCookieToggle[aria-pressed=true]",optional:!0},{click:"#updateCookieButton"}]},{name:"hubspot",detectCmp:[{exists:"#hs-eu-cookie-confirmation"}],detectPopup:[{visible:"#hs-eu-cookie-confirmation"}],optIn:[{click:"#hs-eu-confirmation-button"}],optOut:[{click:"#hs-eu-decline-button"}]},{name:"indeed.com",cosmetic:!0,prehideSelectors:["#CookiePrivacyNotice"],detectCmp:[{exists:"#CookiePrivacyNotice"}],detectPopup:[{visible:"#CookiePrivacyNotice"}],optIn:[{click:"#CookiePrivacyNotice button[data-gnav-element-name=CookiePrivacyNoticeOk]"}],optOut:[{hide:"#CookiePrivacyNotice"}]},{name:"ing.de",runContext:{urlPattern:"^https://www\\.ing\\.de/"},cosmetic:!0,prehideSelectors:['div[slot="backdrop"]'],detectCmp:[{exists:'[data-tag-name="ing-cc-dialog-frame"]'}],detectPopup:[{visible:'[data-tag-name="ing-cc-dialog-frame"]'}],optIn:[{click:['[data-tag-name="ing-cc-dialog-level0"]','[data-tag-name="ing-cc-button"][class*="accept"]']}],optOut:[{click:['[data-tag-name="ing-cc-dialog-level0"]','[data-tag-name="ing-cc-button"][class*="more"]']}]},{name:"ionos.de",prehideSelectors:[".privacy-consent--backdrop",".privacy-consent--modal"],detectCmp:[{exists:".privacy-consent--modal"}],detectPopup:[{visible:".privacy-consent--modal"}],optIn:[{click:"#selectAll"}],optOut:[{click:".footer-config-link"},{click:"#confirmSelection"}]},{name:"itopvpn.com",cosmetic:!0,prehideSelectors:[".pop-cookie"],detectCmp:[{exists:".pop-cookie"}],detectPopup:[{exists:".pop-cookie"}],optIn:[{click:"#_pcookie"}],optOut:[{hide:".pop-cookie"}]},{name:"iubenda",prehideSelectors:["#iubenda-cs-banner"],detectCmp:[{exists:"#iubenda-cs-banner"}],detectPopup:[{visible:".iubenda-cs-accept-btn"}],optIn:[{click:".iubenda-cs-accept-btn"}],optOut:[{click:".iubenda-cs-customize-btn"},{eval:"EVAL_IUBENDA_0"},{click:"#iubFooterBtn"}],test:[{eval:"EVAL_IUBENDA_1"}]},{name:"iWink",prehideSelectors:["body.cookies-request #cookie-bar"],detectCmp:[{exists:"body.cookies-request #cookie-bar"}],detectPopup:[{visible:"body.cookies-request #cookie-bar"}],optIn:[{waitForThenClick:"body.cookies-request #cookie-bar .allow-cookies"}],optOut:[{waitForThenClick:"body.cookies-request #cookie-bar .disallow-cookies"}],test:[{eval:"EVAL_IWINK_TEST"}]},{name:"johnlewis.com",prehideSelectors:["div[class^=pecr-cookie-banner-]"],detectCmp:[{exists:"div[class^=pecr-cookie-banner-]"}],detectPopup:[{exists:"div[class^=pecr-cookie-banner-]"}],optOut:[{click:"button[data-test^=manage-cookies]"},{wait:"500"},{click:"label[data-test^=toggle][class*=checked]:not([class*=disabled])",all:!0,optional:!0},{click:"button[data-test=save-preferences]"}],optIn:[{click:"button[data-test=allow-all]"}]},{name:"jquery.cookieBar",vendorUrl:"https://github.com/kovarp/jquery.cookieBar",prehideSelectors:[".cookie-bar"],cosmetic:!0,detectCmp:[{exists:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons"}],detectPopup:[{visible:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons",check:"any"}],optIn:[{click:".cookie-bar .cookie-bar__btn"}],optOut:[{hide:".cookie-bar"}],test:[{visible:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons",check:"none"},{eval:"EVAL_JQUERY_COOKIEBAR_0"}]},{name:"justwatch.com",prehideSelectors:[".consent-banner"],detectCmp:[{exists:".consent-banner .consent-banner__actions"}],detectPopup:[{visible:".consent-banner .consent-banner__actions"}],optIn:[{click:".consent-banner__actions button.basic-button.primary"}],optOut:[{click:".consent-banner__actions button.basic-button.secondary"},{waitForThenClick:".consent-modal__footer button.basic-button.secondary"},{waitForThenClick:".consent-modal ion-content > div > a:nth-child(9)"},{click:"label.consent-switch input[type=checkbox]:checked",all:!0,optional:!0},{waitForVisible:".consent-modal__footer button.basic-button.primary"},{click:".consent-modal__footer button.basic-button.primary"}]},{name:"ketch",runContext:{frame:!1,main:!0},intermediate:!1,prehideSelectors:["#lanyard_root div[role='dialog']"],detectCmp:[{exists:"#lanyard_root div[role='dialog']"}],detectPopup:[{visible:"#lanyard_root div[role='dialog']"}],optIn:[{if:{exists:"#lanyard_root button[class='confirmButton']"},then:[{waitForThenClick:"#lanyard_root div[class^='buttons'] > :nth-child(2)"},{click:"#lanyard_root button[class='confirmButton']"}],else:[{waitForThenClick:"#lanyard_root div[class^='buttons'] > :nth-child(2)"}]}],optOut:[{click:"#lanyard_root button[class^='link']",optional:!0},{if:{exists:"#lanyard_root button[class*='confirmButton']"},then:[{waitForThenClick:"#lanyard_root button[class*='rejectButton']"},{click:"#lanyard_root button[class*='confirmButton']"}],else:[{click:"#lanyard_root div[class^='buttons'] > :nth-child(1)",optional:!0},{waitForThenClick:"#lanyard_root input:checked"},{click:"#consentsTab > div:nth-child(2) > div > div[class^='actions'] > button:nth-child(1)"}]}],test:[]},{name:"kleinanzeigen-de",runContext:{urlPattern:"^https?://(www\\.)?kleinanzeigen\\.de"},prehideSelectors:["#gdpr-banner-container"],detectCmp:[{any:[{exists:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"},{exists:"#ConsentManagementPage"}]}],detectPopup:[{any:[{visible:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"},{visible:"#ConsentManagementPage"}]}],optIn:[{if:{exists:"#gdpr-banner-container #gdpr-banner"},then:[{click:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-accept]"}],else:[{click:"#ConsentManagementPage .Button-primary"}]}],optOut:[{if:{exists:"#gdpr-banner-container #gdpr-banner"},then:[{click:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"}],else:[{click:"#ConsentManagementPage .Button-secondary"}]}]},{name:"lightbox",prehideSelectors:[".darken-layer.open,.lightbox.lightbox--cookie-consent"],detectCmp:[{exists:"body.cookie-consent-is-active div.lightbox--cookie-consent > div.lightbox__content > div.cookie-consent[data-jsb]"}],detectPopup:[{visible:"body.cookie-consent-is-active div.lightbox--cookie-consent > div.lightbox__content > div.cookie-consent[data-jsb]"}],optOut:[{click:".cookie-consent__footer > button[type='submit']:not([data-button='selectAll'])"}],optIn:[{click:".cookie-consent__footer > button[type='submit'][data-button='selectAll']"}]},{name:"linkedin.com",prehideSelectors:[".artdeco-global-alert[type=COOKIE_CONSENT]"],detectCmp:[{exists:".artdeco-global-alert[type=COOKIE_CONSENT]"}],detectPopup:[{visible:".artdeco-global-alert[type=COOKIE_CONSENT]"}],optIn:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=ACCEPT]"},{wait:500},{waitForThenClick:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=ACCEPT]"}],optOut:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=DENY]"},{wait:500},{waitForThenClick:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=DENY]"}],test:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT]",check:"none"}]},{name:"macpaw.com",cosmetic:!0,prehideSelectors:['div[data-banner="cookies"]'],detectCmp:[{exists:'div[data-banner="cookies"]'}],detectPopup:[{exists:'div[data-banner="cookies"]'}],optIn:[{click:'button[data-banner-close="cookies"]'}],optOut:[{hide:'div[data-banner="cookies"]'}]},{name:"marksandspencer.com",cosmetic:!0,detectCmp:[{exists:".navigation-cookiebbanner"}],detectPopup:[{visible:".navigation-cookiebbanner"}],optOut:[{hide:".navigation-cookiebbanner"}],optIn:[{click:".navigation-cookiebbanner__submit"}]},{name:"mediamarkt.de",prehideSelectors:["div[aria-labelledby=pwa-consent-layer-title]","div[class^=StyledConsentLayerWrapper-]"],detectCmp:[{exists:"div[aria-labelledby^=pwa-consent-layer-title]"}],detectPopup:[{exists:"div[aria-labelledby^=pwa-consent-layer-title]"}],optOut:[{click:"button[data-test^=pwa-consent-layer-deny-all]"}],optIn:[{click:"button[data-test^=pwa-consent-layer-accept-all"}]},{name:"Mediavine",prehideSelectors:['[data-name="mediavine-gdpr-cmp"]'],detectCmp:[{exists:'[data-name="mediavine-gdpr-cmp"]'}],detectPopup:[{wait:500},{visible:'[data-name="mediavine-gdpr-cmp"]'}],optIn:[{waitForThenClick:'[data-name="mediavine-gdpr-cmp"] [format="primary"]'}],optOut:[{waitForThenClick:'[data-name="mediavine-gdpr-cmp"] [data-view="manageSettings"]'},{waitFor:'[data-name="mediavine-gdpr-cmp"] input[type=checkbox]'},{eval:"EVAL_MEDIAVINE_0",optional:!0},{click:'[data-name="mediavine-gdpr-cmp"] [format="secondary"]'}]},{name:"microsoft.com",prehideSelectors:["#wcpConsentBannerCtrl"],detectCmp:[{exists:"#wcpConsentBannerCtrl"}],detectPopup:[{exists:"#wcpConsentBannerCtrl"}],optOut:[{eval:"EVAL_MICROSOFT_0"}],optIn:[{eval:"EVAL_MICROSOFT_1"}],test:[{eval:"EVAL_MICROSOFT_2"}]},{name:"midway-usa",runContext:{urlPattern:"^https://www\\.midwayusa\\.com/"},cosmetic:!0,prehideSelectors:["#cookie-container"],detectCmp:[{exists:['div[aria-label="Cookie Policy Banner"]']}],detectPopup:[{visible:"#cookie-container"}],optIn:[{click:"button#cookie-btn"}],optOut:[{hide:'div[aria-label="Cookie Policy Banner"]'}]},{name:"moneysavingexpert.com",detectCmp:[{exists:"dialog[data-testid=accept-our-cookies-dialog]"}],detectPopup:[{visible:"dialog[data-testid=accept-our-cookies-dialog]"}],optIn:[{click:"#banner-accept"}],optOut:[{click:"#banner-manage"},{click:"#pc-confirm"}]},{name:"monzo.com",prehideSelectors:[".cookie-alert, cookie-alert__content"],detectCmp:[{exists:'div.cookie-alert[role="dialog"]'},{exists:'a[href*="monzo"]'}],detectPopup:[{visible:".cookie-alert__content"}],optIn:[{click:".js-accept-cookie-policy"}],optOut:[{click:".js-decline-cookie-policy"}]},{name:"Moove",prehideSelectors:["#moove_gdpr_cookie_info_bar"],detectCmp:[{exists:"#moove_gdpr_cookie_info_bar"}],detectPopup:[{visible:"#moove_gdpr_cookie_info_bar"}],optIn:[{waitForThenClick:".moove-gdpr-infobar-allow-all"}],optOut:[{if:{exists:"#moove_gdpr_cookie_info_bar .change-settings-button"},then:[{click:"#moove_gdpr_cookie_info_bar .change-settings-button"},{waitForVisible:"#moove_gdpr_cookie_modal"},{eval:"EVAL_MOOVE_0"},{click:".moove-gdpr-modal-save-settings"}],else:[{hide:"#moove_gdpr_cookie_info_bar"}]}],test:[{visible:"#moove_gdpr_cookie_info_bar",check:"none"}]},{name:"national-lottery.co.uk",detectCmp:[{exists:".cuk_cookie_consent"}],detectPopup:[{visible:".cuk_cookie_consent",check:"any"}],optOut:[{click:".cuk_cookie_consent_manage_pref"},{click:".cuk_cookie_consent_save_pref"},{click:".cuk_cookie_consent_close"}],optIn:[{click:".cuk_cookie_consent_accept_all"}]},{name:"nba.com",runContext:{urlPattern:"^https://(www\\.)?nba.com/"},cosmetic:!0,prehideSelectors:["#onetrust-banner-sdk"],detectCmp:[{exists:"#onetrust-banner-sdk"}],detectPopup:[{visible:"#onetrust-banner-sdk"}],optIn:[{click:"#onetrust-accept-btn-handler"}],optOut:[{hide:"#onetrust-banner-sdk"}]},{name:"netflix.de",detectCmp:[{exists:"#cookie-disclosure"}],detectPopup:[{visible:".cookie-disclosure-message",check:"any"}],optIn:[{click:".btn-accept"}],optOut:[{hide:"#cookie-disclosure"},{click:".btn-reject"}]},{name:"nhs.uk",prehideSelectors:["#nhsuk-cookie-banner"],detectCmp:[{exists:"#nhsuk-cookie-banner"}],detectPopup:[{exists:"#nhsuk-cookie-banner"}],optOut:[{click:"#nhsuk-cookie-banner__link_accept"}],optIn:[{click:"#nhsuk-cookie-banner__link_accept_analytics"}]},{name:"notice-cookie",prehideSelectors:[".button--notice"],cosmetic:!0,detectCmp:[{exists:".notice--cookie"}],detectPopup:[{visible:".notice--cookie"}],optIn:[{click:".button--notice"}],optOut:[{hide:".notice--cookie"}]},{name:"nrk.no",cosmetic:!0,prehideSelectors:[".nrk-masthead__info-banner--cookie"],detectCmp:[{exists:".nrk-masthead__info-banner--cookie"}],detectPopup:[{exists:".nrk-masthead__info-banner--cookie"}],optIn:[{click:"div.nrk-masthead__info-banner--cookie button > span:has(+ svg.nrk-close)"}],optOut:[{hide:".nrk-masthead__info-banner--cookie"}]},{name:"obi.de",prehideSelectors:[".disc-cp--active"],detectCmp:[{exists:".disc-cp-modal__modal"}],detectPopup:[{visible:".disc-cp-modal__modal"}],optIn:[{click:".js-disc-cp-accept-all"}],optOut:[{click:".js-disc-cp-deny-all"}]},{name:"onlyFans.com",prehideSelectors:["div.b-cookies-informer"],detectCmp:[{exists:"div.b-cookies-informer"}],detectPopup:[{exists:"div.b-cookies-informer"}],optIn:[{click:"div.b-cookies-informer__nav > button:nth-child(2)"}],optOut:[{click:"div.b-cookies-informer__nav > button:nth-child(1)"},{click:'div.b-cookies-informer__switchers > div:nth-child(2) > div[at-attr="checkbox"] > span.b-input-radio__container > input[type="checkbox"]'},{click:"div.b-cookies-informer__nav > button"}]},{name:"opera.com",vendorUrl:"https://unknown",cosmetic:!1,runContext:{main:!0,frame:!1},intermediate:!1,prehideSelectors:[],detectCmp:[{exists:"#cookie-consent .manage-cookies__btn"}],detectPopup:[{visible:"#cookie-consent .cookie-basic-consent__btn"}],optIn:[{waitForThenClick:"#cookie-consent .cookie-basic-consent__btn"}],optOut:[{waitForThenClick:"#cookie-consent .manage-cookies__btn"},{waitForThenClick:"#cookie-consent .active.marketing_option_switch.cookie-consent__switch",all:!0},{waitForThenClick:"#cookie-consent .cookie-selection__btn"}],test:[{eval:"EVAL_OPERA_0"}]},{name:"osano",prehideSelectors:[".osano-cm-window"],cosmetic:!0,detectCmp:[{exists:".osano-cm-window"}],detectPopup:[{visible:".osano-cm-dialog"}],optIn:[{click:".osano-cm-accept-all",optional:!0}],optOut:[{hide:".osano-cm-window"}]},{name:"otto.de",prehideSelectors:[".cookieBanner--visibility"],detectCmp:[{exists:".cookieBanner--visibility"}],detectPopup:[{visible:".cookieBanner__wrapper"}],optIn:[{click:".js_cookieBannerPermissionButton"}],optOut:[{click:".js_cookieBannerProhibitionButton"}]},{name:"paypal-us",prehideSelectors:["#ccpaCookieContent_wrapper, article.ppvx_modal--overpanel"],detectCmp:[{exists:"#ccpaCookieBanner, .privacy-modal-content"}],detectPopup:[{exists:"#ccpaCookieBanner, .privacy-modal-content"}],optIn:[{click:"#acceptAllButton"}],optOut:[{if:{exists:"a#manageCookiesLink"},then:[{click:"a#manageCookiesLink"}],else:[{waitForVisible:".privacy-modal-content #formContent"},{click:"#formContent .cookiepref-11m2iee-checkbox_base input:checked",all:!0,optional:!0},{click:".confirmCookie #submitCookiesBtn"}]}]},{name:"paypal.com",prehideSelectors:["#gdprCookieBanner"],detectCmp:[{exists:"#gdprCookieBanner"}],detectPopup:[{visible:"#gdprCookieContent_wrapper"}],optIn:[{click:"#acceptAllButton"}],optOut:[{wait:200},{click:".gdprCookieBanner_decline-button"}],test:[{wait:500},{eval:"EVAL_PAYPAL_0"}]},{name:"pinetools.com",cosmetic:!0,prehideSelectors:["#aviso_cookies"],detectCmp:[{exists:"#aviso_cookies"}],detectPopup:[{exists:".lang_en #aviso_cookies"}],optIn:[{click:"#aviso_cookies .a_boton_cerrar"}],optOut:[{hide:"#aviso_cookies"}]},{name:"pmc",cosmetic:!0,prehideSelectors:["#pmc-pp-tou--notice"],detectCmp:[{exists:"#pmc-pp-tou--notice"}],detectPopup:[{visible:"#pmc-pp-tou--notice"}],optIn:[{click:"span.pmc-pp-tou--notice-close-btn"}],optOut:[{hide:"#pmc-pp-tou--notice"}]},{name:"pornhub.com",runContext:{urlPattern:"^https://(www\\.)?pornhub\\.com/"},cosmetic:!0,prehideSelectors:[".cookiesBanner"],detectCmp:[{exists:".cookiesBanner"}],detectPopup:[{visible:".cookiesBanner"}],optIn:[{click:".cookiesBanner .okButton"}],optOut:[{hide:".cookiesBanner"}]},{name:"pornpics.com",cosmetic:!0,prehideSelectors:["#cookie-contract"],detectCmp:[{exists:"#cookie-contract"}],detectPopup:[{visible:"#cookie-contract"}],optIn:[{click:"#cookie-contract .icon-cross"}],optOut:[{hide:"#cookie-contract"}]},{name:"PrimeBox CookieBar",prehideSelectors:["#cookie-bar"],detectCmp:[{exists:"#cookie-bar .cb-enable,#cookie-bar .cb-disable,#cookie-bar .cb-policy"}],detectPopup:[{visible:"#cookie-bar .cb-enable,#cookie-bar .cb-disable,#cookie-bar .cb-policy",check:"any"}],optIn:[{waitForThenClick:"#cookie-bar .cb-enable"}],optOut:[{click:"#cookie-bar .cb-disable",optional:!0},{hide:"#cookie-bar"}],test:[{eval:"EVAL_PRIMEBOX_0"}]},{name:"privacymanager.io",prehideSelectors:["#gdpr-consent-tool-wrapper",'iframe[src^="https://cmp-consent-tool.privacymanager.io"]'],runContext:{urlPattern:"^https://cmp-consent-tool\\.privacymanager\\.io/",main:!1,frame:!0},detectCmp:[{exists:"button#save"}],detectPopup:[{visible:"button#save"}],optIn:[{click:"button#save"}],optOut:[{if:{exists:"#denyAll"},then:[{click:"#denyAll"},{waitForThenClick:".okButton"}],else:[{waitForThenClick:"#manageSettings"},{waitFor:".purposes-overview-list"},{waitFor:"button#saveAndExit"},{click:"span[role=checkbox][aria-checked=true]",all:!0,optional:!0},{click:"button#saveAndExit"}]}]},{name:"pubtech",prehideSelectors:["#pubtech-cmp"],detectCmp:[{exists:"#pubtech-cmp"}],detectPopup:[{visible:"#pubtech-cmp #pt-actions"}],optIn:[{if:{exists:"#pt-accept-all"},then:[{click:"#pubtech-cmp #pt-actions #pt-accept-all"}],else:[{click:"#pubtech-cmp #pt-actions button:nth-of-type(2)"}]}],optOut:[{click:"#pubtech-cmp #pt-close"}],test:[{eval:"EVAL_PUBTECH_0"}]},{name:"quantcast",prehideSelectors:["#qc-cmp2-main,#qc-cmp2-container"],detectCmp:[{exists:"#qc-cmp2-container"}],detectPopup:[{visible:"#qc-cmp2-ui"}],optOut:[{click:'.qc-cmp2-summary-buttons > button[mode="secondary"]'},{waitFor:"#qc-cmp2-ui"},{click:'.qc-cmp2-toggle-switch > button[aria-checked="true"]',all:!0,optional:!0},{click:'.qc-cmp2-main button[aria-label="REJECT ALL"]',optional:!0},{waitForThenClick:'.qc-cmp2-main button[aria-label="SAVE & EXIT"],.qc-cmp2-buttons-desktop > button[mode="primary"]',timeout:5e3}],optIn:[{click:'.qc-cmp2-summary-buttons > button[mode="primary"]'}]},{name:"reddit.com",runContext:{urlPattern:"^https://www\\.reddit\\.com/"},prehideSelectors:['section:has(a[href^="https://www.reddit.com/policies/cookies"])'],detectCmp:[{exists:'section:has(a[href^="https://www.reddit.com/policies/cookies"])'}],detectPopup:[{visible:'section:has(a[href^="https://www.reddit.com/policies/cookies"])'}],optIn:[{waitForThenClick:"section:has(a[href^=\"https://www.reddit.com/policies/cookies\"]) section[class^='_'] > section:first-child form button"}],optOut:[{waitForThenClick:"section:has(a[href^=\"https://www.reddit.com/policies/cookies\"]) section[class^='_'] > section:last-child form button"}],test:[{eval:"EVAL_REDDIT_0"}]},{name:"roofingmegastore.co.uk",runContext:{urlPattern:"^https://(www\\.)?roofingmegastore\\.co\\.uk"},prehideSelectors:["#m-cookienotice"],detectCmp:[{exists:"#m-cookienotice"}],detectPopup:[{visible:"#m-cookienotice"}],optIn:[{click:"#accept-cookies"}],optOut:[{click:"#manage-cookies"},{waitForThenClick:"#accept-selected"}]},{name:"samsung.com",runContext:{urlPattern:"^https://www\\.samsung\\.com/"},cosmetic:!0,prehideSelectors:["div.cookie-bar"],detectCmp:[{exists:"div.cookie-bar"}],detectPopup:[{visible:"div.cookie-bar"}],optIn:[{click:"div.cookie-bar__manage > a"}],optOut:[{hide:"div.cookie-bar"}]},{name:"sibbo",prehideSelectors:["sibbo-cmp-layout"],detectCmp:[{exists:"sibbo-cmp-layout"}],detectPopup:[{visible:"sibbo-cmp-layout"}],optIn:[{click:"sibbo-cmp-layout [data-accept-all]"}],optOut:[{click:'.sibbo-panel__aside__buttons a[data-nav="purposes"]'},{click:'.sibbo-panel__main__header__actions a[data-focusable="reject-all"]'},{if:{exists:"[data-view=purposes] .sibbo-panel__main__footer__actions [data-save-and-exit]"},then:[],else:[{waitFor:'.sibbo-panel__main__footer__actions a[data-focusable="next"]:not(.sibbo-cmp-button--disabled)'},{click:'.sibbo-panel__main__footer__actions a[data-focusable="next"]'},{click:'.sibbo-panel__main div[data-view="purposesLegInt"] a[data-focusable="reject-all"]'}]},{waitFor:".sibbo-panel__main__footer__actions [data-save-and-exit]:not(.sibbo-cmp-button--disabled)"},{click:".sibbo-panel__main__footer__actions [data-save-and-exit]:not(.sibbo-cmp-button--disabled)"}],test:[{eval:"EVAL_SIBBO_0"}]},{name:"similarweb.com",cosmetic:!0,prehideSelectors:[".app-cookies-notification"],detectCmp:[{exists:".app-cookies-notification"}],detectPopup:[{exists:".app-layout .app-cookies-notification"}],optIn:[{click:"button.app-cookies-notification__dismiss"}],optOut:[{hide:".app-layout .app-cookies-notification"}]},{name:"Sirdata",prehideSelectors:["#sd-cmp"],detectCmp:[{exists:"#sd-cmp"}],detectPopup:[{visible:"#sd-cmp"}],optIn:[{waitForThenClick:"#sd-cmp .sd-cmp-3cRQ2"}],optOut:[{waitForThenClick:"#sd-cmp .sd-cmp-1pO44"}],test:[{eval:"EVAL_SIRDATA_0"}]},{name:"snigel",detectCmp:[{exists:".snigel-cmp-framework"}],detectPopup:[{visible:".snigel-cmp-framework"}],optOut:[{click:"#sn-b-custom"},{click:"#sn-b-save"}],test:[{eval:"EVAL_SNIGEL_0"}],optIn:[{click:".snigel-cmp-framework #accept-choices"}]},{name:"steampowered.com",detectCmp:[{exists:".cookiepreferences_popup"},{visible:".cookiepreferences_popup"}],detectPopup:[{visible:".cookiepreferences_popup"}],optOut:[{click:"#rejectAllButton"}],optIn:[{click:"#acceptAllButton"}],test:[{wait:1e3},{eval:"EVAL_STEAMPOWERED_0"}]},{name:"takealot.com",cosmetic:!0,prehideSelectors:['div[class^="cookies-banner-module_"]'],detectCmp:[{exists:'div[class^="cookies-banner-module_cookie-banner_"]'}],detectPopup:[{exists:'div[class^="cookies-banner-module_cookie-banner_"]'}],optIn:[{click:'button[class*="cookies-banner-module_dismiss-button_"]'}],optOut:[{hide:'div[class^="cookies-banner-module_"]'},{if:{exists:'div[class^="cookies-banner-module_small-cookie-banner_"]'},then:[{eval:"EVAL_TAKEALOT_0"}],else:[]}]},{name:"tarteaucitron.js",prehideSelectors:["#tarteaucitronRoot"],detectCmp:[{exists:"#tarteaucitronRoot"}],detectPopup:[{visible:"#tarteaucitronRoot #tarteaucitronAlertSmall,#tarteaucitronRoot #tarteaucitronAlertBig",check:"any"}],optIn:[{eval:"EVAL_TARTEAUCITRON_1"}],optOut:[{eval:"EVAL_TARTEAUCITRON_0"}],test:[{eval:"EVAL_TARTEAUCITRON_2",comment:"sometimes there are required categories, so we check that at least something is false"}]},{name:"Tealium",prehideSelectors:["#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs,#consent-layer"],detectCmp:[{exists:"#__tealiumGDPRecModal *,#__tealiumGDPRcpPrefs *"},{eval:"EVAL_TEALIUM_0"}],detectPopup:[{visible:"#__tealiumGDPRecModal *,#__tealiumGDPRcpPrefs *",check:"any"}],optOut:[{eval:"EVAL_TEALIUM_1"},{eval:"EVAL_TEALIUM_DONOTSELL"},{hide:"#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs"},{waitForThenClick:"#cm-acceptNone,.js-accept-essential-cookies",timeout:1e3,optional:!0}],optIn:[{hide:"#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs"},{eval:"EVAL_TEALIUM_2"}],test:[{eval:"EVAL_TEALIUM_3"},{eval:"EVAL_TEALIUM_DONOTSELL_CHECK"},{visible:"#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs",check:"none"}]},{name:"Termly",prehideSelectors:["#termly-code-snippet-support"],detectCmp:[{exists:"#termly-code-snippet-support"}],detectPopup:[{visible:"#termly-code-snippet-support div"}],optIn:[{waitForThenClick:'[data-tid="banner-accept"]'}],optOut:[{if:{exists:'[data-tid="banner-decline"]'},then:[{click:'[data-tid="banner-decline"]'}],else:[{click:".t-preference-button"},{wait:500},{if:{exists:".t-declineAllButton"},then:[{click:".t-declineAllButton"}],else:[{waitForThenClick:".t-preference-modal input[type=checkbox][checked]:not([disabled])",all:!0},{waitForThenClick:".t-saveButton"}]}]}]},{name:"Test page cosmetic CMP",cosmetic:!0,prehideSelectors:["#privacy-test-page-cmp-test-prehide"],detectCmp:[{exists:"#privacy-test-page-cmp-test-banner"}],detectPopup:[{visible:"#privacy-test-page-cmp-test-banner"}],optIn:[{waitFor:"#accept-all"},{click:"#accept-all"}],optOut:[{hide:"#privacy-test-page-cmp-test-banner"}],test:[{wait:500},{eval:"EVAL_TESTCMP_COSMETIC_0"}]},{name:"Test page CMP",prehideSelectors:["#reject-all"],detectCmp:[{exists:"#privacy-test-page-cmp-test"}],detectPopup:[{visible:"#privacy-test-page-cmp-test"}],optIn:[{waitFor:"#accept-all"},{click:"#accept-all"}],optOut:[{waitFor:"#reject-all"},{click:"#reject-all"}],test:[{eval:"EVAL_TESTCMP_0"}]},{name:"thalia.de",prehideSelectors:[".consent-banner-box"],detectCmp:[{exists:"consent-banner[component=consent-banner]"}],detectPopup:[{visible:".consent-banner-box"}],optIn:[{click:".button-zustimmen"}],optOut:[{click:"button[data-consent=disagree]"}]},{name:"thefreedictionary.com",prehideSelectors:["#cmpBanner"],detectCmp:[{exists:"#cmpBanner"}],detectPopup:[{visible:"#cmpBanner"}],optIn:[{eval:"EVAL_THEFREEDICTIONARY_1"}],optOut:[{eval:"EVAL_THEFREEDICTIONARY_0"}]},{name:"theverge",runContext:{frame:!1,main:!0,urlPattern:"^https://(www)?\\.theverge\\.com"},intermediate:!1,prehideSelectors:[".duet--cta--cookie-banner"],detectCmp:[{exists:".duet--cta--cookie-banner"}],detectPopup:[{visible:".duet--cta--cookie-banner"}],optIn:[{click:".duet--cta--cookie-banner button.tracking-12",all:!1}],optOut:[{click:".duet--cta--cookie-banner button.tracking-12 > span"}],test:[{eval:"EVAL_THEVERGE_0"}]},{name:"tidbits-com",cosmetic:!0,prehideSelectors:["#eu_cookie_law_widget-2"],detectCmp:[{exists:"#eu_cookie_law_widget-2"}],detectPopup:[{visible:"#eu_cookie_law_widget-2"}],optIn:[{click:"#eu-cookie-law form > input.accept"}],optOut:[{hide:"#eu_cookie_law_widget-2"}]},{name:"tractor-supply",runContext:{urlPattern:"^https://www\\.tractorsupply\\.com/"},cosmetic:!0,prehideSelectors:[".tsc-cookie-banner"],detectCmp:[{exists:".tsc-cookie-banner"}],detectPopup:[{visible:".tsc-cookie-banner"}],optIn:[{click:"#cookie-banner-cancel"}],optOut:[{hide:".tsc-cookie-banner"}]},{name:"trader-joes-com",cosmetic:!0,prehideSelectors:['div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'],detectCmp:[{exists:'div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'}],detectPopup:[{visible:'div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'}],optIn:[{click:'div[class^="CookiesAlert_cookiesAlert__container__"] button'}],optOut:[{hide:'div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'}]},{name:"tropicfeel-com",prehideSelectors:["#shopify-section-cookies-controller"],detectCmp:[{exists:"#shopify-section-cookies-controller"}],detectPopup:[{visible:"#shopify-section-cookies-controller #cookies-controller-main-pane",check:"any"}],optIn:[{waitForThenClick:"#cookies-controller-main-pane form[data-form-allow-all] button"}],optOut:[{click:"#cookies-controller-main-pane a[data-tab-target=manage-cookies]"},{waitFor:"#manage-cookies-pane.active"},{click:"#manage-cookies-pane.active input[type=checkbox][checked]:not([disabled])",all:!0},{click:"#manage-cookies-pane.active button[type=submit]"}],test:[]},{name:"true-car",runContext:{urlPattern:"^https://www\\.truecar\\.com/"},cosmetic:!0,prehideSelectors:[['div[aria-labelledby="cookie-banner-heading"]']],detectCmp:[{exists:'div[aria-labelledby="cookie-banner-heading"]'}],detectPopup:[{visible:'div[aria-labelledby="cookie-banner-heading"]'}],optIn:[{click:'div[aria-labelledby="cookie-banner-heading"] > button[aria-label="Close"]'}],optOut:[{hide:'div[aria-labelledby="cookie-banner-heading"]'}]},{name:"truyo",prehideSelectors:["#truyo-consent-module"],detectCmp:[{exists:"#truyo-cookieBarContent"}],detectPopup:[{visible:"#truyo-consent-module"}],optIn:[{click:"button#acceptAllCookieButton"}],optOut:[{click:"button#declineAllCookieButton"}]},{name:"tumblr-com",cosmetic:!0,prehideSelectors:["#cmp-app-container"],detectCmp:[{exists:"#cmp-app-container"}],detectPopup:[{visible:"#cmp-app-container"}],optIn:[{click:"#tumblr #cmp-app-container div.components-modal__frame > iframe > html body > div > div > div.cmp__dialog-footer > div > button.components-button.white-space-normal.is-primary"}],optOut:[{hide:"#cmp-app-container"}]},{name:"twitch.tv",runContext:{urlPattern:"^https?://(www\\.)?twitch\\.tv"},prehideSelectors:["div:has(> .consent-banner .consent-banner__content--gdpr-v2),.ReactModalPortal:has([data-a-target=consent-modal-save])"],detectCmp:[{exists:".consent-banner .consent-banner__content--gdpr-v2"}],detectPopup:[{visible:".consent-banner .consent-banner__content--gdpr-v2"}],optIn:[{click:'button[data-a-target="consent-banner-accept"]'}],optOut:[{hide:"div:has(> .consent-banner .consent-banner__content--gdpr-v2)"},{click:'button[data-a-target="consent-banner-manage-preferences"]'},{waitFor:"input[type=checkbox][data-a-target=tw-checkbox]"},{click:"input[type=checkbox][data-a-target=tw-checkbox][checked]:not([disabled])",all:!0,optional:!0},{waitForThenClick:"[data-a-target=consent-modal-save]"},{waitForVisible:".ReactModalPortal:has([data-a-target=consent-modal-save])",check:"none"}]},{name:"twitter",runContext:{urlPattern:"^https://([a-z0-9-]+\\.)?twitter\\.com/"},prehideSelectors:['[data-testid="BottomBar"]'],detectCmp:[{exists:'[data-testid="BottomBar"] div'}],detectPopup:[{visible:'[data-testid="BottomBar"] div'}],optIn:[{waitForThenClick:'[data-testid="BottomBar"] > div:has(>div:first-child>div:last-child>span[role=button]) > div:last-child > div[role=button]:first-child'}],optOut:[{waitForThenClick:'[data-testid="BottomBar"] > div:has(>div:first-child>div:last-child>span[role=button]) > div:last-child > div[role=button]:last-child'}],TODOtest:[{eval:"EVAL_document.cookie.includes('d_prefs=MjoxLGNvbnNlbnRfdmVyc2lvbjoy')"}]},{name:"ubuntu.com",prehideSelectors:["dialog.cookie-policy"],detectCmp:[{any:[{exists:"dialog.cookie-policy header"},{exists:'xpath///*[@id="modal"]/div/header'}]}],detectPopup:[{any:[{visible:"dialog header"},{visible:'xpath///*[@id="modal"]/div/header'}]}],optIn:[{any:[{waitForThenClick:"#cookie-policy-button-accept"},{waitForThenClick:'xpath///*[@id="cookie-policy-button-accept"]'}]}],optOut:[{any:[{waitForThenClick:"button.p-button"},{waitForThenClick:'xpath///*[@id="cookie-policy-content"]/p[4]/button[2]'}]},{waitForThenClick:".p-switch__input:checked",optional:!0,all:!0},{any:[{waitForThenClick:"div > button"},{waitForThenClick:'xpath///*[@id="modal"]/div/button'}]}],test:[{eval:"EVAL_UBUNTU_COM_0"}]},{name:"UK Cookie Consent",prehideSelectors:["#catapult-cookie-bar"],cosmetic:!0,detectCmp:[{exists:"#catapult-cookie-bar"}],detectPopup:[{exists:".has-cookie-bar #catapult-cookie-bar"}],optIn:[{click:"#catapultCookie"}],optOut:[{hide:"#catapult-cookie-bar"}],test:[{eval:"EVAL_UK_COOKIE_CONSENT_0"}]},{name:"urbanarmorgear-com",cosmetic:!0,prehideSelectors:['div[class^="Layout__CookieBannerContainer-"]'],detectCmp:[{exists:'div[class^="Layout__CookieBannerContainer-"]'}],detectPopup:[{visible:'div[class^="Layout__CookieBannerContainer-"]'}],optIn:[{click:'button[class^="CookieBanner__AcceptButton"]'}],optOut:[{hide:'div[class^="Layout__CookieBannerContainer-"]'}]},{name:"usercentrics-api",detectCmp:[{exists:"#usercentrics-root"}],detectPopup:[{eval:"EVAL_USERCENTRICS_API_0"},{exists:["#usercentrics-root","[data-testid=uc-container]"]},{waitForVisible:"#usercentrics-root",timeout:2e3}],optIn:[{eval:"EVAL_USERCENTRICS_API_3"},{eval:"EVAL_USERCENTRICS_API_1"},{eval:"EVAL_USERCENTRICS_API_5"}],optOut:[{eval:"EVAL_USERCENTRICS_API_1"},{eval:"EVAL_USERCENTRICS_API_2"}],test:[{eval:"EVAL_USERCENTRICS_API_6"}]},{name:"usercentrics-button",detectCmp:[{exists:"#usercentrics-button"}],detectPopup:[{visible:"#usercentrics-button #uc-btn-accept-banner"}],optIn:[{click:"#usercentrics-button #uc-btn-accept-banner"}],optOut:[{click:"#usercentrics-button #uc-btn-deny-banner"}],test:[{eval:"EVAL_USERCENTRICS_BUTTON_0"}]},{name:"uswitch.com",prehideSelectors:["#cookie-banner-wrapper"],detectCmp:[{exists:"#cookie-banner-wrapper"}],detectPopup:[{visible:"#cookie-banner-wrapper"}],optIn:[{click:"#cookie_banner_accept_mobile"}],optOut:[{click:"#cookie_banner_save"}]},{name:"vodafone.de",runContext:{urlPattern:"^https://www\\.vodafone\\.de/"},prehideSelectors:[".dip-consent,.dip-consent-container"],detectCmp:[{exists:".dip-consent-container"}],detectPopup:[{visible:".dip-consent-content"}],optOut:[{click:'.dip-consent-btn[tabindex="2"]'}],optIn:[{click:'.dip-consent-btn[tabindex="1"]'}]},{name:"waitrose.com",prehideSelectors:["div[aria-labelledby=CookieAlertModalHeading]","section[data-test=initial-waitrose-cookie-consent-banner]","section[data-test=cookie-consent-modal]"],detectCmp:[{exists:"section[data-test=initial-waitrose-cookie-consent-banner]"}],detectPopup:[{visible:"section[data-test=initial-waitrose-cookie-consent-banner]"}],optIn:[{click:"button[data-test=accept-all]"}],optOut:[{click:"button[data-test=manage-cookies]"},{wait:200},{eval:"EVAL_WAITROSE_0"},{click:"button[data-test=submit]"}],test:[{eval:"EVAL_WAITROSE_1"}]},{name:"wetransfer.com",detectCmp:[{exists:".welcome__cookie-notice"}],detectPopup:[{visible:".welcome__cookie-notice"}],optIn:[{click:".welcome__button--accept"}],optOut:[{click:".welcome__button--decline"}]},{name:"whitepages.com",runContext:{urlPattern:"^https://www\\.whitepages\\.com/"},cosmetic:!0,prehideSelectors:[".cookie-wrapper, .cookie-overlay"],detectCmp:[{exists:".cookie-wrapper"}],detectPopup:[{visible:".cookie-overlay"}],optIn:[{click:'button[aria-label="Got it"]'}],optOut:[{hide:".cookie-wrapper"}]},{name:"woo-commerce-com",prehideSelectors:[".wccom-comp-privacy-banner .wccom-privacy-banner"],detectCmp:[{exists:".wccom-comp-privacy-banner .wccom-privacy-banner"}],detectPopup:[{exists:".wccom-comp-privacy-banner .wccom-privacy-banner"}],optIn:[{click:".wccom-privacy-banner__content-buttons button.is-primary"}],optOut:[{click:".wccom-privacy-banner__content-buttons button.is-secondary"},{waitForThenClick:"input[type=checkbox][checked]:not([disabled])",all:!0},{click:"div.wccom-modal__footer > button"}]},{name:"WP Cookie Notice for GDPR",vendorUrl:"https://wordpress.org/plugins/gdpr-cookie-consent/",prehideSelectors:["#gdpr-cookie-consent-bar"],detectCmp:[{exists:"#gdpr-cookie-consent-bar"}],detectPopup:[{visible:"#gdpr-cookie-consent-bar"}],optIn:[{waitForThenClick:"#gdpr-cookie-consent-bar #cookie_action_accept"}],optOut:[{waitForThenClick:"#gdpr-cookie-consent-bar #cookie_action_reject"}],test:[{eval:"EVAL_WP_COOKIE_NOTICE_0"}]},{name:"wpcc",cosmetic:!0,prehideSelectors:[".wpcc-container"],detectCmp:[{exists:".wpcc-container"}],detectPopup:[{exists:".wpcc-container .wpcc-message"}],optIn:[{click:".wpcc-compliance .wpcc-btn"}],optOut:[{hide:".wpcc-container"}]},{name:"xhamster-eu",prehideSelectors:[".cookies-modal"],detectCmp:[{exists:".cookies-modal"}],detectPopup:[{exists:".cookies-modal"}],optIn:[{click:"button.cmd-button-accept-all"}],optOut:[{click:"button.cmd-button-reject-all"}]},{name:"xhamster-us",runContext:{urlPattern:"^https://(www\\.)?xhamster\\d?\\.com"},cosmetic:!0,prehideSelectors:[".cookie-announce"],detectCmp:[{exists:".cookie-announce"}],detectPopup:[{visible:".cookie-announce .announce-text"}],optIn:[{click:".cookie-announce button.xh-button"}],optOut:[{hide:".cookie-announce"}]},{name:"xing.com",detectCmp:[{exists:"div[class^=cookie-consent-CookieConsent]"}],detectPopup:[{exists:"div[class^=cookie-consent-CookieConsent]"}],optIn:[{click:"#consent-accept-button"}],optOut:[{click:"#consent-settings-button"},{click:".consent-banner-button-accept-overlay"}],test:[{eval:"EVAL_XING_0"}]},{name:"xnxx-com",cosmetic:!0,prehideSelectors:["#cookies-use-alert"],detectCmp:[{exists:"#cookies-use-alert"}],detectPopup:[{visible:"#cookies-use-alert"}],optIn:[{click:"#cookies-use-alert .close"}],optOut:[{hide:"#cookies-use-alert"}]},{name:"Yahoo",runContext:{urlPattern:"^https://consent\\.yahoo\\.com/v2/"},prehideSelectors:["#reject-all"],detectCmp:[{exists:"#consent-page"}],detectPopup:[{visible:"#consent-page"}],optIn:[{waitForThenClick:"#consent-page button[value=agree]"}],optOut:[{waitForThenClick:"#consent-page button[value=reject]"}]},{name:"youporn.com",cosmetic:!0,prehideSelectors:[".euCookieModal, #js_euCookieModal"],detectCmp:[{exists:".euCookieModal"}],detectPopup:[{exists:".euCookieModal, #js_euCookieModal"}],optIn:[{click:'button[name="user_acceptCookie"]'}],optOut:[{hide:".euCookieModal"}]},{name:"youtube-desktop",prehideSelectors:["tp-yt-iron-overlay-backdrop.opened","ytd-consent-bump-v2-lightbox"],detectCmp:[{exists:"ytd-consent-bump-v2-lightbox tp-yt-paper-dialog"},{exists:'ytd-consent-bump-v2-lightbox tp-yt-paper-dialog a[href^="https://consent.youtube.com/"]'}],detectPopup:[{visible:"ytd-consent-bump-v2-lightbox tp-yt-paper-dialog"}],optIn:[{waitForThenClick:"ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:last-child #button,ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:last-child button"},{wait:500}],optOut:[{waitForThenClick:"ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:first-child #button,ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:first-child button"},{wait:500}],test:[{wait:500},{eval:"EVAL_YOUTUBE_DESKTOP_0"}]},{name:"youtube-mobile",prehideSelectors:[".consent-bump-v2-lightbox"],detectCmp:[{exists:"ytm-consent-bump-v2-renderer"}],detectPopup:[{visible:"ytm-consent-bump-v2-renderer"}],optIn:[{waitForThenClick:"ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons c3-material-button:first-child button, ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons ytm-button-renderer:first-child button"},{wait:500}],optOut:[{waitForThenClick:"ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons c3-material-button:nth-child(2) button, ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons ytm-button-renderer:nth-child(2) button"},{wait:500}],test:[{wait:500},{eval:"EVAL_YOUTUBE_MOBILE_0"}]},{name:"zdf",prehideSelectors:["#zdf-cmp-banner-sdk"],detectCmp:[{exists:"#zdf-cmp-banner-sdk"}],detectPopup:[{visible:"#zdf-cmp-main.zdf-cmp-show"}],optIn:[{waitForThenClick:"#zdf-cmp-main #zdf-cmp-accept-btn"}],optOut:[{waitForThenClick:"#zdf-cmp-main #zdf-cmp-deny-btn"}],test:[]}],f={"didomi.io":{detectors:[{presentMatcher:{target:{selector:"#didomi-host, #didomi-notice"},type:"css"},showingMatcher:{target:{selector:"body.didomi-popup-open, .didomi-notice-banner"},type:"css"}}],methods:[{action:{target:{selector:".didomi-popup-notice-buttons .didomi-button:not(.didomi-button-highlight), .didomi-notice-banner .didomi-learn-more-button"},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{retries:50,target:{selector:"#didomi-purpose-cookies"},type:"waitcss",waitTime:50},{consents:[{description:"Share (everything) with others",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-share_whith_others]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-share_whith_others]:last-child"},type:"click"},type:"X"},{description:"Information storage and access",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-cookies]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-cookies]:last-child"},type:"click"},type:"D"},{description:"Content selection, offers and marketing",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-CL-T1Rgm7]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-CL-T1Rgm7]:last-child"},type:"click"},type:"E"},{description:"Analytics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-analytics]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-analytics]:last-child"},type:"click"},type:"B"},{description:"Analytics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-M9NRHJe3G]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-M9NRHJe3G]:last-child"},type:"click"},type:"B"},{description:"Ad and content selection",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-advertising_personalization]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-advertising_personalization]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection",falseAction:{parent:{childFilter:{target:{selector:"#didomi-purpose-pub-ciblee"}},selector:".didomi-consent-popup-data-processing, .didomi-components-accordion-label-container"},target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-pub-ciblee]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-pub-ciblee]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - basics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-q4zlJqdcD]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-q4zlJqdcD]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - partners and subsidiaries",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-partenaire-cAsDe8jC]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-partenaire-cAsDe8jC]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-p4em9a8m]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-p4em9a8m]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - others",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-autres-pub]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-autres-pub]:last-child"},type:"click"},type:"F"},{description:"Social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-reseauxsociaux]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-reseauxsociaux]:last-child"},type:"click"},type:"A"},{description:"Social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-social_media]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-social_media]:last-child"},type:"click"},type:"A"},{description:"Content selection",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-content_personalization]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-content_personalization]:last-child"},type:"click"},type:"E"},{description:"Ad delivery",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-ad_delivery]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-ad_delivery]:last-child"},type:"click"},type:"F"}],type:"consent"},{action:{consents:[{matcher:{childFilter:{target:{selector:":not(.didomi-components-radio__option--selected)"}},type:"css"},trueAction:{target:{selector:":nth-child(2)"},type:"click"},falseAction:{target:{selector:":first-child"},type:"click"},type:"X"}],type:"consent"},target:{selector:".didomi-components-radio"},type:"foreach"}],type:"list"},name:"DO_CONSENT"},{action:{parent:{selector:".didomi-consent-popup-footer .didomi-consent-popup-actions"},target:{selector:".didomi-components-button:first-child"},type:"click"},name:"SAVE_CONSENT"}]},oil:{detectors:[{presentMatcher:{target:{selector:".as-oil-content-overlay"},type:"css"},showingMatcher:{target:{selector:".as-oil-content-overlay"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".as-js-advanced-settings"},type:"click"},{retries:"10",target:{selector:".as-oil-cpc__purpose-container"},type:"waitcss",waitTime:"250"}],type:"list"},name:"OPEN_OPTIONS"},{action:{actions:[{consents:[{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Information storage and access","Opbevaring af og adgang til oplysninger på din enhed"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Information storage and access","Opbevaring af og adgang til oplysninger på din enhed"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"D"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personlige annoncer","Personalisation"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personlige annoncer","Personalisation"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"E"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Annoncevalg, levering og rapportering","Ad selection, delivery, reporting"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Annoncevalg, levering og rapportering","Ad selection, delivery, reporting"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"F"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personalisering af indhold","Content selection, delivery, reporting"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personalisering af indhold","Content selection, delivery, reporting"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"E"},{matcher:{parent:{childFilter:{target:{selector:".as-oil-cpc__purpose-header",textFilter:["Måling","Measurement"]}},selector:".as-oil-cpc__purpose-container"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{childFilter:{target:{selector:".as-oil-cpc__purpose-header",textFilter:["Måling","Measurement"]}},selector:".as-oil-cpc__purpose-container"},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"B"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:"Google"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:"Google"},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"F"}],type:"consent"}],type:"list"},name:"DO_CONSENT"},{action:{target:{selector:".as-oil__btn-optin"},type:"click"},name:"SAVE_CONSENT"},{action:{target:{selector:"div.as-oil"},type:"hide"},name:"HIDE_CMP"}]},optanon:{detectors:[{presentMatcher:{target:{selector:"#optanon-menu, .optanon-alert-box-wrapper"},type:"css"},showingMatcher:{target:{displayFilter:!0,selector:".optanon-alert-box-wrapper"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".optanon-alert-box-wrapper .optanon-toggle-display, a[onclick*='OneTrust.ToggleInfoDisplay()'], a[onclick*='Optanon.ToggleInfoDisplay()']"},type:"click"}],type:"list"},name:"OPEN_OPTIONS"},{action:{actions:[{target:{selector:".preference-menu-item #Your-privacy"},type:"click"},{target:{selector:"#optanon-vendor-consent-text"},type:"click"},{action:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},target:{selector:"#optanon-vendor-consent-list .vendor-item"},type:"foreach"},{target:{selector:".vendor-consent-back-link"},type:"click"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-performance"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-performance"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-functional"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-functional"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-advertising"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-advertising"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-social"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-social"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Social Media Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Social Media Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalisation"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalisation"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Site monitoring cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Site monitoring cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Third party privacy-enhanced content"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Third party privacy-enhanced content"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Performance & Advertising Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Performance & Advertising Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Information storage and access"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Information storage and access"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"D"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad selection, delivery, reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad selection, delivery, reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content selection, delivery, reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content selection, delivery, reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Measurement"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Measurement"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Recommended Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Recommended Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Unclassified Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Unclassified Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Analytical Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Analytical Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Marketing Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Marketing Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalization"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalization"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad Selection, Delivery & Reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad Selection, Delivery & Reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content Selection, Delivery & Reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content Selection, Delivery & Reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"}],type:"list"},name:"DO_CONSENT"},{action:{parent:{selector:".optanon-save-settings-button"},target:{selector:".optanon-white-button-middle"},type:"click"},name:"SAVE_CONSENT"},{action:{actions:[{target:{selector:"#optanon-popup-wrapper"},type:"hide"},{target:{selector:"#optanon-popup-bg"},type:"hide"},{target:{selector:".optanon-alert-box-wrapper"},type:"hide"}],type:"list"},name:"HIDE_CMP"}]},quantcast2:{detectors:[{presentMatcher:{target:{selector:"[data-tracking-opt-in-overlay]"},type:"css"},showingMatcher:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-learn-more]"},type:"css"}}],methods:[{action:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-learn-more]"},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{type:"wait",waitTime:500},{action:{actions:[{target:{selector:"div",textFilter:["Information storage and access"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"D"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Personalization"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"F"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Ad selection, delivery, reporting"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"F"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Content selection, delivery, reporting"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"E"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Measurement"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"B"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Other Partners"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},type:"ifcss"}],type:"list"},parent:{childFilter:{target:{selector:"input"}},selector:"[data-tracking-opt-in-overlay] > div > div"},target:{childFilter:{target:{selector:"input"}},selector:":scope > div"},type:"foreach"}],type:"list"},name:"DO_CONSENT"},{action:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-save]"},type:"click"},name:"SAVE_CONSENT"}]},springer:{detectors:[{presentMatcher:{parent:null,target:{selector:".cmp-app_gdpr"},type:"css"},showingMatcher:{parent:null,target:{displayFilter:!0,selector:".cmp-popup_popup"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".cmp-intro_rejectAll"},type:"click"},{type:"wait",waitTime:250},{target:{selector:".cmp-purposes_purposeItem:not(.cmp-purposes_selectedPurpose)"},type:"click"}],type:"list"},name:"OPEN_OPTIONS"},{action:{consents:[{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Przechowywanie informacji na urządzeniu lub dostęp do nich",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Przechowywanie informacji na urządzeniu lub dostęp do nich",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"D"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór podstawowych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór podstawowych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"F"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"F"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"E"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"E"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Stosowanie badań rynkowych w celu generowania opinii odbiorców",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Stosowanie badań rynkowych w celu generowania opinii odbiorców",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"X"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Opracowywanie i ulepszanie produktów",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Opracowywanie i ulepszanie produktów",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"X"}],type:"consent"},name:"DO_CONSENT"},{action:{target:{selector:".cmp-details_save"},type:"click"},name:"SAVE_CONSENT"}]},wordpressgdpr:{detectors:[{presentMatcher:{parent:null,target:{selector:".wpgdprc-consent-bar"},type:"css"},showingMatcher:{parent:null,target:{displayFilter:!0,selector:".wpgdprc-consent-bar"},type:"css"}}],methods:[{action:{parent:null,target:{selector:".wpgdprc-consent-bar .wpgdprc-consent-bar__settings",textFilter:null},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{target:{selector:".wpgdprc-consent-modal .wpgdprc-button",textFilter:"Eyeota"},type:"click"},{consents:[{description:"Eyeota Cookies",matcher:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Eyeota"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Eyeota"},target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},{target:{selector:".wpgdprc-consent-modal .wpgdprc-button",textFilter:"Advertising"},type:"click"},{consents:[{description:"Advertising Cookies",matcher:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Advertising"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Advertising"},target:{selector:"label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},name:"DO_CONSENT"},{action:{parent:null,target:{selector:".wpgdprc-button",textFilter:"Save my settings"},type:"click"},name:"SAVE_CONSENT"}]}},A={autoconsent:v,consentomatic:f},E=Object.freeze({__proto__:null,autoconsent:v,consentomatic:f,default:A});const O=new class{constructor(e,t=null,o=null){if(this.id=a(),this.rules=[],this.foundCmp=null,this.state={lifecycle:"loading",prehideOn:!1,findCmpAttempts:0,detectedCmps:[],detectedPopups:[],selfTest:null},r.sendContentMessage=e,this.sendContentMessage=e,this.rules=[],this.updateState({lifecycle:"loading"}),this.addDynamicRules(),t)this.initialize(t,o);else{o&&this.parseDeclarativeRules(o);e({type:"init",url:window.location.href}),this.updateState({lifecycle:"waitingForInitResponse"})}this.domActions=new C(this)}initialize(e,t){const o=function(e){const t={enabled:!0,autoAction:"optOut",disabledCmps:[],enablePrehide:!0,enableCosmeticRules:!0,detectRetries:20,isMainWorld:!1,prehideTimeout:2e3,logs:{lifecycle:!1,rulesteps:!1,evals:!1,errors:!0,messages:!1}},o=structuredClone(t);for(const c of Object.keys(t))void 0!==e[c]&&(o[c]=e[c]);return o}(e);if(o.logs.lifecycle&&console.log("autoconsent init",window.location.href),this.config=o,o.enabled){if(t&&this.parseDeclarativeRules(t),this.rules=function(e,t){return e.filter((e=>(!t.disabledCmps||!t.disabledCmps.includes(e.name))&&(t.enableCosmeticRules||!e.isCosmetic)))}(this.rules,o),e.enablePrehide)if(document.documentElement)this.prehideElements();else{const e=()=>{window.removeEventListener("DOMContentLoaded",e),this.prehideElements()};window.addEventListener("DOMContentLoaded",e)}if("loading"===document.readyState){const e=()=>{window.removeEventListener("DOMContentLoaded",e),this.start()};window.addEventListener("DOMContentLoaded",e)}else this.start();this.updateState({lifecycle:"initialized"})}else o.logs.lifecycle&&console.log("autoconsent is disabled")}addDynamicRules(){w.forEach((e=>{this.rules.push(new e(this))}))}parseDeclarativeRules(e){Object.keys(e.consentomatic).forEach((t=>{this.addConsentomaticCMP(t,e.consentomatic[t])})),e.autoconsent.forEach((e=>{this.addDeclarativeCMP(e)}))}addDeclarativeCMP(e){this.rules.push(new u(e,this))}addConsentomaticCMP(e,t){this.rules.push(new m(`com_${e}`,t))}start(){window.requestIdleCallback?window.requestIdleCallback((()=>this._start()),{timeout:500}):this._start()}async _start(){const e=this.config.logs;e.lifecycle&&console.log(`Detecting CMPs on ${window.location.href}`),this.updateState({lifecycle:"started"});const t=await this.findCmp(this.config.detectRetries);if(this.updateState({detectedCmps:t.map((e=>e.name))}),0===t.length)return e.lifecycle&&console.log("no CMP found",location.href),this.config.enablePrehide&&this.undoPrehide(),this.updateState({lifecycle:"nothingDetected"}),!1;this.updateState({lifecycle:"cmpDetected"});let o=await this.detectPopups(t.filter((e=>!e.isCosmetic)));if(0===o.length&&(o=await this.detectPopups(t.filter((e=>e.isCosmetic)))),0===o.length)return e.lifecycle&&console.log("no popup found"),this.config.enablePrehide&&this.undoPrehide(),!1;if(this.updateState({lifecycle:"openPopupDetected"}),this.config.enablePrehide&&!this.state.prehideOn&&this.prehideElements(),o.length>1){const t={msg:"Found multiple CMPs, check the detection rules.",cmps:o.map((e=>e.name))};e.errors&&console.warn(t.msg,t.cmps),this.sendContentMessage({type:"autoconsentError",details:t})}return this.foundCmp=o[0],"optOut"===this.config.autoAction?await this.doOptOut():"optIn"===this.config.autoAction?await this.doOptIn():(e.lifecycle&&console.log("waiting for opt-out signal...",location.href),!0)}async findCmp(e){const t=this.config.logs;this.updateState({findCmpAttempts:this.state.findCmpAttempts+1});const o=[];for(const e of this.rules)try{if(!e.checkRunContext())continue;await e.detectCmp()&&(t.lifecycle&&console.log(`Found CMP: ${e.name} ${window.location.href}`),this.sendContentMessage({type:"cmpDetected",url:location.href,cmp:e.name}),o.push(e))}catch(o){t.errors&&console.warn(`error detecting ${e.name}`,o)}return 0===o.length&&e>0?(await this.domActions.wait(500),this.findCmp(e-1)):o}async detectPopups(e){const t=this.config.logs,o=[],c=e.map((e=>this.waitForPopup(e).then((t=>{t&&(this.updateState({detectedPopups:this.state.detectedPopups.concat([e.name])}),this.sendContentMessage({type:"popupFound",cmp:e.name,url:location.href}),o.push(e))})).catch((o=>(t.errors&&console.warn(`error waiting for a popup for ${e.name}`,o),null)))));return await Promise.all(c),o}async doOptOut(){const e=this.config.logs;let t;return this.updateState({lifecycle:"runningOptOut"}),this.foundCmp?(e.lifecycle&&console.log(`CMP ${this.foundCmp.name}: opt out on ${window.location.href}`),t=await this.foundCmp.optOut(),e.lifecycle&&console.log(`${this.foundCmp.name}: opt out result ${t}`)):(e.errors&&console.log("no CMP to opt out"),t=!1),this.config.enablePrehide&&this.undoPrehide(),this.sendContentMessage({type:"optOutResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:t,scheduleSelfTest:this.foundCmp&&this.foundCmp.hasSelfTest,url:location.href}),t&&!this.foundCmp.isIntermediate?(this.sendContentMessage({type:"autoconsentDone",cmp:this.foundCmp.name,isCosmetic:this.foundCmp.isCosmetic,url:location.href}),this.updateState({lifecycle:"done"})):this.updateState({lifecycle:t?"optOutSucceeded":"optOutFailed"}),t}async doOptIn(){const e=this.config.logs;let t;return this.updateState({lifecycle:"runningOptIn"}),this.foundCmp?(e.lifecycle&&console.log(`CMP ${this.foundCmp.name}: opt in on ${window.location.href}`),t=await this.foundCmp.optIn(),e.lifecycle&&console.log(`${this.foundCmp.name}: opt in result ${t}`)):(e.errors&&console.log("no CMP to opt in"),t=!1),this.config.enablePrehide&&this.undoPrehide(),this.sendContentMessage({type:"optInResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:t,scheduleSelfTest:!1,url:location.href}),t&&!this.foundCmp.isIntermediate?(this.sendContentMessage({type:"autoconsentDone",cmp:this.foundCmp.name,isCosmetic:this.foundCmp.isCosmetic,url:location.href}),this.updateState({lifecycle:"done"})):this.updateState({lifecycle:t?"optInSucceeded":"optInFailed"}),t}async doSelfTest(){const e=this.config.logs;let t;return this.foundCmp?(e.lifecycle&&console.log(`CMP ${this.foundCmp.name}: self-test on ${window.location.href}`),t=await this.foundCmp.test()):(e.errors&&console.log("no CMP to self test"),t=!1),this.sendContentMessage({type:"selfTestResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:t,url:location.href}),this.updateState({selfTest:t}),t}async waitForPopup(e,t=5,o=500){const c=this.config.logs;c.lifecycle&&console.log("checking if popup is open...",e.name);const i=await e.detectPopup().catch((t=>(c.errors&&console.warn(`error detecting popup for ${e.name}`,t),!1)));return!i&&t>0?(await this.domActions.wait(o),this.waitForPopup(e,t-1,o)):(c.lifecycle&&console.log(e.name,"popup is "+(i?"open":"not open")),i)}prehideElements(){const e=this.config.logs,t=this.rules.reduce(((e,t)=>t.prehideSelectors?[...e,...t.prehideSelectors]:e),["#didomi-popup,.didomi-popup-container,.didomi-popup-notice,.didomi-consent-popup-preferences,#didomi-notice,.didomi-popup-backdrop,.didomi-screen-medium"]);return this.updateState({prehideOn:!0}),setTimeout((()=>{this.config.enablePrehide&&this.state.prehideOn&&!["runningOptOut","runningOptIn"].includes(this.state.lifecycle)&&(e.lifecycle&&console.log("Process is taking too long, unhiding elements"),this.undoPrehide())}),this.config.prehideTimeout||2e3),this.domActions.prehide(t.join(","))}undoPrehide(){return this.updateState({prehideOn:!1}),this.domActions.undoPrehide()}updateState(e){Object.assign(this.state,e),this.sendContentMessage({type:"report",instanceId:this.id,url:window.location.href,mainFrame:window.top===window.self,state:this.state})}async receiveMessageCallback(e){const t=this.config?.logs;switch(t?.messages&&console.log("received from background",e,window.location.href),e.type){case"initResp":this.initialize(e.config,e.rules);break;case"optIn":await this.doOptIn();break;case"optOut":await this.doOptOut();break;case"selfTest":await this.doSelfTest();break;case"evalResp":!function(e,t){const o=r.pending.get(e);o?(r.pending.delete(e),o.timer&&window.clearTimeout(o.timer),o.resolve(t)):console.warn("no eval #",e)}(e.id,e.result)}}}((e=>{window.webkit.messageHandlers[e.type]&&window.webkit.messageHandlers[e.type].postMessage(e).then((e=>{O.receiveMessageCallback(e)}))}),null,E);window.autoconsentMessageCallback=e=>{O.receiveMessageCallback(e)}}(); diff --git a/package-lock.json b/package-lock.json index a096c1ea3b..6ba27d8bf1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "ios", "version": "1.0.0", "dependencies": { - "@duckduckgo/autoconsent": "^6.4.0" + "@duckduckgo/autoconsent": "^9.1.0" }, "devDependencies": { "@rollup/plugin-json": "^4.1.0", @@ -135,9 +135,9 @@ } }, "node_modules/@duckduckgo/autoconsent": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@duckduckgo/autoconsent/-/autoconsent-6.4.0.tgz", - "integrity": "sha512-WhQnE0Zy8ArMyo78tTZSOXVn9IQ0qut4INLHvt7kyPkzQVhfrckVwSlehlTETFu22NkuX1jUW1GaugFtxnYJWg==" + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@duckduckgo/autoconsent/-/autoconsent-9.1.0.tgz", + "integrity": "sha512-zjuYu+Wb/8cscFrjSttA4uMjxwcRAzPcHmou5vgbYAfEV7qdsAPMvuai2REke199BmoI7OK2khtsLq7LGqkDzQ==" }, "node_modules/@eslint/eslintrc": { "version": "0.4.3", diff --git a/package.json b/package.json index bd1ed62a18..d0973a9923 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,6 @@ "rollup-plugin-terser": "^7.0.2" }, "dependencies": { - "@duckduckgo/autoconsent": "^6.4.0" + "@duckduckgo/autoconsent": "^9.1.0" } } From 3678c6491cc814fde3021fe4dfcdf20054ddfcea Mon Sep 17 00:00:00 2001 From: Christopher Brind Date: Thu, 18 Jan 2024 13:22:03 +0000 Subject: [PATCH 07/70] revert using content insets to fix page position problem (#2356) --- .../xcshareddata/swiftpm/Package.resolved | 2 +- DuckDuckGo/MainView.swift | 19 ------- DuckDuckGo/MainViewController.swift | 51 ++++--------------- 3 files changed, 10 insertions(+), 62 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 67fbd40e2b..64a6614e8d 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -156,7 +156,7 @@ { "identity" : "trackerradarkit", "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/TrackerRadarKit", + "location" : "https://github.com/duckduckgo/TrackerRadarKit.git", "state" : { "revision" : "a6b7ba151d9dc6684484f3785293875ec01cc1ff", "version" : "1.2.2" diff --git a/DuckDuckGo/MainView.swift b/DuckDuckGo/MainView.swift index e5cb0798c8..4d5171167e 100644 --- a/DuckDuckGo/MainView.swift +++ b/DuckDuckGo/MainView.swift @@ -54,7 +54,6 @@ class MainViewFactory { extension MainViewFactory { private func createViews() { - createWebViewContainer() createLogoBackground() createContentContainer() createSuggestionTrayContainer() @@ -66,12 +65,6 @@ extension MainViewFactory { createToolbar() } - final class WebViewContainerView: UIView { } - private func createWebViewContainer() { - coordinator.webViewContainer = WebViewContainerView() - superview.addSubview(coordinator.webViewContainer) - } - private func createProgressView() { coordinator.progress = ProgressView() superview.addSubview(coordinator.progress) @@ -163,7 +156,6 @@ extension MainViewFactory { extension MainViewFactory { private func constrainViews() { - constrainWebViewContainer() constrainLogoBackground() constrainContentContainer() constrainSuggestionTrayContainer() @@ -174,16 +166,6 @@ extension MainViewFactory { constrainProgress() constrainToolbar() } - - private func constrainWebViewContainer() { - let webViewContainer = coordinator.webViewContainer! - NSLayoutConstraint.activate([ - webViewContainer.constrainView(superview, by: .width), - webViewContainer.constrainView(superview, by: .height), - webViewContainer.constrainView(superview, by: .centerX), - webViewContainer.constrainView(superview, by: .centerY), - ]) - } private func constrainProgress() { let progress = coordinator.progress! @@ -351,7 +333,6 @@ class MainViewCoordinator { var toolbarFireButton: UIBarButtonItem! var toolbarForwardButton: UIBarButtonItem! var toolbarTabSwitcherButton: UIBarButtonItem! - var webViewContainer: UIView! let constraints = Constraints() diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index b5cb5f8083..8a825a9d73 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -432,7 +432,6 @@ class MainViewController: UIViewController { @objc func onAddressBarPositionChanged() { viewCoordinator.moveAddressBarToPosition(appSettings.currentAddressBarPosition) refreshViewsBasedOnAddressBarPosition(appSettings.currentAddressBarPosition) - refreshWebViewContentInsets() } func refreshViewsBasedOnAddressBarPosition(_ position: AddressBarPosition) { @@ -479,7 +478,6 @@ class MainViewController: UIViewController { findInPageBottomLayoutConstraint.constant = height keyboardHeight = height - refreshWebViewContentInsets() if let suggestionsTray = suggestionTrayController { let suggestionsFrameInView = suggestionsTray.view.convert(suggestionsTray.contentFrame, to: view) @@ -644,7 +642,7 @@ class MainViewController: UIViewController { guard let tab = tabManager.current(createIfNeeded: true) else { fatalError("Unable to create tab") } - addToWebViewContainer(tab: tab) + attachTab(tab: tab) refreshControls() } else { attachHomeScreen() @@ -853,7 +851,7 @@ class MainViewController: UIViewController { private func addTab(url: URL?, inheritedAttribution: AdClickAttributionLogic.State?) { let tab = tabManager.add(url: url, inheritedAttribution: inheritedAttribution) dismissOmniBar() - addToWebViewContainer(tab: tab) + attachTab(tab: tab) } func select(tabAt index: Int) { @@ -867,7 +865,7 @@ class MainViewController: UIViewController { if tab.link == nil { attachHomeScreen() } else { - addToWebViewContainer(tab: tab) + attachTab(tab: tab) refreshControls() } tabsBarController?.refresh(tabsModel: tabManager.model, scrollToSelected: true) @@ -876,21 +874,15 @@ class MainViewController: UIViewController { } } - private func addToWebViewContainer(tab: TabViewController) { + private func attachTab(tab: TabViewController) { removeHomeScreen() updateFindInPage() currentTab?.progressWorker.progressBar = nil currentTab?.chromeDelegate = nil - currentTab?.webView.scrollView.contentInsetAdjustmentBehavior = .never - - addChild(tab) - viewCoordinator.webViewContainer.subviews.forEach { $0.removeFromSuperview() } - viewCoordinator.webViewContainer.addSubview(tab.view) - tab.view.frame = self.viewCoordinator.webViewContainer.bounds - tab.didMove(toParent: self) - + + addToContentContainer(controller: tab) + viewCoordinator.logoContainer.isHidden = true - viewCoordinator.contentContainer.isHidden = true tab.progressWorker.progressBar = viewCoordinator.progress chromeManager.attach(to: tab.webView.scrollView) @@ -1338,35 +1330,11 @@ extension MainViewController: BrowserChromeDelegate { } if animated { - UIView.animate(withDuration: ChromeAnimationConstants.duration, animations: updateBlock) { _ in - self.refreshWebViewContentInsets() - } + UIView.animate(withDuration: ChromeAnimationConstants.duration, animations: updateBlock) } else { updateBlock() - self.refreshWebViewContentInsets() } } - - func refreshWebViewContentInsets() { - guard let webView = currentTab?.webView else { return } - - let top = viewCoordinator.statusBackground.frame.height - let bottom: CGFloat - if isToolbarHidden { - bottom = 0 - } else if appSettings.currentAddressBarPosition.isBottom { - bottom = viewCoordinator.toolbar.frame.height - + viewCoordinator.navigationBarContainer.frame.height - + view.safeAreaInsets.bottom + additionalSafeAreaInsets.bottom - + keyboardHeight - } else { - bottom = viewCoordinator.toolbar.frame.height - + view.safeAreaInsets.bottom + additionalSafeAreaInsets.bottom - + keyboardHeight - } - - webView.scrollView.contentInset = .init(top: top, left: 0, bottom: bottom, right: 0) - } func setNavigationBarHidden(_ hidden: Bool) { if hidden { hideKeyboard() } @@ -1733,7 +1701,7 @@ extension MainViewController: TabDelegate { guard self.tabManager.model.tabs.contains(newTab.tabModel) else { return } self.dismissOmniBar() - self.addToWebViewContainer(tab: newTab) + self.attachTab(tab: newTab) self.refreshOmniBar() } @@ -1837,7 +1805,6 @@ extension MainViewController: TabDelegate { func showBars() { chromeManager.reset() - refreshWebViewContentInsets() } func tabDidRequestFindInPage(tab: TabViewController) { From 992283f53ed4c5911e9475e14f0cd713430693fb Mon Sep 17 00:00:00 2001 From: Graeme Arthur Date: Thu, 18 Jan 2024 15:14:54 +0100 Subject: [PATCH 08/70] Bump build number to 1 --- DuckDuckGo.xcodeproj/project.pbxproj | 56 ++++++++++++++-------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 279d11ea5e..c6b884ff43 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -8135,7 +8135,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProvider.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -8172,7 +8172,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8264,7 +8264,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8292,7 +8292,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8442,7 +8442,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8468,7 +8468,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; INFOPLIST_FILE = DuckDuckGo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8533,7 +8533,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -8568,7 +8568,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8602,7 +8602,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8633,7 +8633,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8920,7 +8920,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8951,7 +8951,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8980,7 +8980,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -9014,7 +9014,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -9045,7 +9045,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProvider.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -9078,11 +9078,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 0; + DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9320,7 +9320,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGoAlpha.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9347,7 +9347,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9380,7 +9380,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9418,7 +9418,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9454,7 +9454,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9489,11 +9489,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 0; + DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9667,11 +9667,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 0; + DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9700,10 +9700,10 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 0; + DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; From dbc306ecd49c23ce67bc3d8ba9045adcf4a2a9a0 Mon Sep 17 00:00:00 2001 From: bwaresiak Date: Thu, 18 Jan 2024 18:07:14 +0100 Subject: [PATCH 09/70] Update translated strings (#2352) Task/Issue URL: https://app.asana.com/0/414235014887631/1204622631026489/f Tech Design URL: CC: Description: Steps to test this PR: Test localization on relevant screens (Settings, Sync). --- DuckDuckGo/bg.lproj/Localizable.strings | 33 ++++ DuckDuckGo/bg.lproj/Settings.strings | 156 ------------------ DuckDuckGo/cs.lproj/Localizable.strings | 33 ++++ DuckDuckGo/cs.lproj/Settings.strings | 156 ------------------ DuckDuckGo/da.lproj/Localizable.strings | 33 ++++ DuckDuckGo/da.lproj/Settings.strings | 156 ------------------ DuckDuckGo/de.lproj/Localizable.strings | 33 ++++ DuckDuckGo/de.lproj/Settings.strings | 156 ------------------ DuckDuckGo/el.lproj/Localizable.strings | 33 ++++ DuckDuckGo/el.lproj/Settings.strings | 156 ------------------ DuckDuckGo/es.lproj/Localizable.strings | 33 ++++ DuckDuckGo/es.lproj/Settings.strings | 156 ------------------ DuckDuckGo/et.lproj/Localizable.strings | 33 ++++ DuckDuckGo/et.lproj/Settings.strings | 156 ------------------ DuckDuckGo/fi.lproj/Localizable.strings | 33 ++++ DuckDuckGo/fi.lproj/Settings.strings | 156 ------------------ DuckDuckGo/fr.lproj/Localizable.strings | 33 ++++ DuckDuckGo/fr.lproj/Settings.strings | 156 ------------------ DuckDuckGo/hr.lproj/Localizable.strings | 33 ++++ DuckDuckGo/hr.lproj/Settings.strings | 156 ------------------ DuckDuckGo/hu.lproj/Localizable.strings | 33 ++++ DuckDuckGo/hu.lproj/Settings.strings | 156 ------------------ DuckDuckGo/it.lproj/Localizable.strings | 33 ++++ DuckDuckGo/it.lproj/Settings.strings | 156 ------------------ DuckDuckGo/lt.lproj/Localizable.strings | 33 ++++ DuckDuckGo/lt.lproj/Settings.strings | 156 ------------------ DuckDuckGo/lv.lproj/Localizable.strings | 33 ++++ DuckDuckGo/lv.lproj/Settings.strings | 156 ------------------ DuckDuckGo/nb.lproj/Localizable.strings | 33 ++++ DuckDuckGo/nb.lproj/Settings.strings | 39 ----- DuckDuckGo/nl.lproj/Localizable.strings | 33 ++++ DuckDuckGo/nl.lproj/Settings.strings | 156 ------------------ DuckDuckGo/pl.lproj/Localizable.strings | 33 ++++ DuckDuckGo/pl.lproj/Settings.strings | 156 ------------------ DuckDuckGo/pt.lproj/Localizable.strings | 33 ++++ DuckDuckGo/pt.lproj/Settings.strings | 156 ------------------ DuckDuckGo/ro.lproj/Localizable.strings | 33 ++++ DuckDuckGo/ro.lproj/Settings.strings | 156 ------------------ DuckDuckGo/ru.lproj/Localizable.strings | 33 ++++ DuckDuckGo/ru.lproj/Settings.strings | 156 ------------------ DuckDuckGo/sk.lproj/Localizable.strings | 33 ++++ DuckDuckGo/sk.lproj/Settings.strings | 156 ------------------ DuckDuckGo/sl.lproj/Localizable.strings | 33 ++++ DuckDuckGo/sl.lproj/Settings.strings | 156 ------------------ DuckDuckGo/sv.lproj/Localizable.strings | 33 ++++ DuckDuckGo/sv.lproj/Settings.strings | 156 ------------------ DuckDuckGo/tr.lproj/Localizable.strings | 33 ++++ DuckDuckGo/tr.lproj/Settings.strings | 156 ------------------ .../Resources/bg.lproj/Localizable.strings | 12 ++ .../Resources/cs.lproj/Localizable.strings | 12 ++ .../Resources/da.lproj/Localizable.strings | 12 ++ .../Resources/de.lproj/Localizable.strings | 12 ++ .../Resources/el.lproj/Localizable.strings | 12 ++ .../Resources/es.lproj/Localizable.strings | 12 ++ .../Resources/et.lproj/Localizable.strings | 12 ++ .../Resources/fi.lproj/Localizable.strings | 12 ++ .../Resources/fr.lproj/Localizable.strings | 12 ++ .../Resources/hr.lproj/Localizable.strings | 12 ++ .../Resources/hu.lproj/Localizable.strings | 12 ++ .../Resources/it.lproj/Localizable.strings | 12 ++ .../Resources/lt.lproj/Localizable.strings | 12 ++ .../Resources/lv.lproj/Localizable.strings | 12 ++ .../Resources/nb.lproj/Localizable.strings | 12 ++ .../Resources/nl.lproj/Localizable.strings | 12 ++ .../Resources/pl.lproj/Localizable.strings | 12 ++ .../Resources/pt.lproj/Localizable.strings | 12 ++ .../Resources/ro.lproj/Localizable.strings | 12 ++ .../Resources/ru.lproj/Localizable.strings | 12 ++ .../Resources/sk.lproj/Localizable.strings | 12 ++ .../Resources/sl.lproj/Localizable.strings | 12 ++ .../Resources/sv.lproj/Localizable.strings | 12 ++ .../Resources/tr.lproj/Localizable.strings | 156 ++++++++++-------- 72 files changed, 1152 insertions(+), 3699 deletions(-) diff --git a/DuckDuckGo/bg.lproj/Localizable.strings b/DuckDuckGo/bg.lproj/Localizable.strings index a36330566f..4813d1983a 100644 --- a/DuckDuckGo/bg.lproj/Localizable.strings +++ b/DuckDuckGo/bg.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Клавиатура"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Пароли"; + /* Settings title for the 'More' section */ "settings.more" = "Още от DuckDuckGo"; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Прегледи с продължително натискане"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Кой уебсайт е повреден?"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Кодът за възстановяване и копиран в клипборда"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Изтриване на данните от сървъра"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "Връзката на всички устройства ще бъде премахната, а синхронизираните данни ще бъдат изтрити от сървъра."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "Изтриване на данните от сървъра?"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Премахване"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "„%@“ вече няма да има достъп до синхронизираните Ви данни."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "Премахване на устройство?"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Премахване"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "Това устройство вече няма да има достъп до синхронизираните Ви данни."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "Изключване на синхронизирането?"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "Съжаляваме, но функцията за синхронизиране и архивиране в момента не е достъпна. Моля, опитайте отново по-късно."; diff --git a/DuckDuckGo/bg.lproj/Settings.strings b/DuckDuckGo/bg.lproj/Settings.strings index 8c8ca9d47d..1ccfd36de8 100644 --- a/DuckDuckGo/bg.lproj/Settings.strings +++ b/DuckDuckGo/bg.lproj/Settings.strings @@ -1,15 +1,3 @@ -/* Class = "UILabel"; text = "Fireproof Sites"; ObjectID = "0DQ-yq-UuT"; */ -"0DQ-yq-UuT.text" = "Огнеустойчиви сайтове"; - -/* Class = "UITableViewSection"; footerTitle = "Disable to prevent links from automatically opening in other installed apps."; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.footerTitle" = "Деактивирайте, за да предотвратите автоматично отваряне на връзки в други инсталирани приложения."; - -/* Class = "UITableViewSection"; headerTitle = "Customize"; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.headerTitle" = "Персонализиране"; - -/* Class = "UILabel"; text = "Version"; ObjectID = "2ky-s9-1aZ"; */ -"2ky-s9-1aZ.text" = "Версия"; - /* Class = "UINavigationItem"; title = "Keyboard"; ObjectID = "2pp-PM-6rW"; */ "2pp-PM-6rW.title" = "Клавиатура"; @@ -31,30 +19,9 @@ /* Class = "UILabel"; text = "Clear Tabs and Data"; ObjectID = "9fc-9r-4aA"; */ "9fc-9r-4aA.text" = "Изчистване на раздели и данни"; -/* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ -"9Ko-0g-T3h.text" = "Размер на текста"; - -/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ -"9kt-6R-XiZ.text" = "Заглавие"; - /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Стартиране на приложение"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Отваряне на връзките в свързаните приложения"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Меню за отстраняване на грешки"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Пароли"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Анимация"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "Защита на имейл"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "Данните и/или разделите ще бъдат изчистени при рестартиране на приложението."; @@ -64,114 +31,42 @@ /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ "btj-ri-kRr.title" = "Управление на прозорци за бисквитки"; -/* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ -"cKo-er-HNj.text" = "Икона на приложението"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "CR5-Al-WIW"; */ -"CR5-Al-WIW.text" = "Етикет"; - /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Глобален контрол на поверителността (GPC)"; /* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ "CxT-QK-iVn.title" = "Добавяне"; -/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ -"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; - /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Изход от приложението, 15 минути без активност"; -/* Class = "UILabel"; text = "10.0.1 (Build 10005)"; ObjectID = "d5n-vG-8kF"; */ -"d5n-vG-8kF.text" = "10.0.1 (Компилация 10005)"; - -/* Class = "UILabel"; text = "Application Lock"; ObjectID = "dBZ-yq-FYj"; */ -"dBZ-yq-FYj.text" = "Заключване на приложение"; - -/* Class = "UITableViewSection"; headerTitle = "Appearance"; ObjectID = "dj9-vh-Rig"; */ -"dj9-vh-Rig.headerTitle" = "Външен Вид"; - -/* Class = "UITableViewSection"; footerTitle = "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening."; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.footerTitle" = "Ако сте задали пръстов отпечатък, лицево разпознаване или системна парола, ще бъдете приканени да отключите приложението при отваряне."; - -/* Class = "UITableViewSection"; headerTitle = "Privacy"; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.headerTitle" = "Поверителност"; - /* Class = "UILabel"; text = "Label"; ObjectID = "dud-qo-Ces"; */ "dud-qo-Ces.text" = "Етикет"; -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Dyd-bm-goj"; */ -"Dyd-bm-goj.title" = "Настройки"; - -/* Class = "UILabel"; text = "100%"; ObjectID = "EB8-09-gt2"; */ -"EB8-09-gt2.text" = "100%"; - /* Class = "UILabel"; text = "Label"; ObjectID = "EIq-Ev-nfj"; */ "EIq-Ev-nfj.text" = "Етикет"; -/* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "ekF-SJ-PAQ"; */ -"ekF-SJ-PAQ.text" = "Автоматично изчистване на данните"; - /* Class = "UILabel"; text = "App Exit, Inactive for 5 Minutes"; ObjectID = "ElX-yE-4PX"; */ "ElX-yE-4PX.text" = "Изход от приложението, 5 минути без активност"; /* Class = "UINavigationItem"; title = "App Icon"; ObjectID = "eMH-uv-Kms"; */ "eMH-uv-Kms.title" = "Икона на приложението"; -/* Class = "UILabel"; text = "Theme"; ObjectID = "f1O-6u-LFY"; */ -"f1O-6u-LFY.text" = "Тема"; - /* Class = "UILabel"; text = "App Exit Only"; ObjectID = "Fal-1Y-o2S"; */ "Fal-1Y-o2S.text" = "Само при изход от приложението"; /* Class = "UITableViewController"; title = "Automatically Clear Data"; ObjectID = "fdJ-b1-Des"; */ "fdJ-b1-Des.title" = "Автоматично изчистване на данните"; -/* Class = "UILabel"; text = "Unprotected Sites"; ObjectID = "FHC-1z-Z3v"; */ -"FHC-1z-Z3v.text" = "Незащитени сайтове"; - -/* Class = "UITableViewSection"; headerTitle = "About"; ObjectID = "FpT-1C-xtx"; */ -"FpT-1C-xtx.headerTitle" = "За нас"; - /* Class = "UITableViewController"; title = "Global Privacy Control (GPC)"; ObjectID = "fV3-86-QQj"; */ "fV3-86-QQj.title" = "Глобален контрол на поверителността (GPC)"; -/* Class = "UILabel"; text = "Add Widget to Home Screen"; ObjectID = "Fxu-zn-51Z"; */ -"Fxu-zn-51Z.text" = "Добавяне на приспособлението към началния екран"; - -/* Class = "UILabel"; text = "Fire Button Animation"; ObjectID = "gBo-Cu-e2k"; Note = "Fire button animation settings item"; */ -"gBo-Cu-e2k.text" = "Анимация на огнения бутон"; - -/* Class = "UILabel"; text = "Default"; ObjectID = "Gbx-kl-uOO"; */ -"Gbx-kl-uOO.text" = "По подразбиране"; - -/* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Управление на прозорци за бисквитки"; - -/* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ -"gS2-mg-l7R.title" = "Тема"; - -/* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ -"HLr-R8-xxF.text" = "Прегледи с продължително натискане"; - -/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ -"hoT-Nu-KXP.text" = "Сърфирайте поверително с нашето приложение за Windows"; - /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Защитата на поверителността е активирана за всички сайтове"; /* Class = "UICollectionViewController"; title = "Icon"; ObjectID = "jbD-Oy-Cmw"; */ "jbD-Oy-Cmw.title" = "Икона"; -/* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ -"m23-t6-9cb.text" = "Споделяне на отзив"; - -/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ -"mLn-1x-Fl5.title" = "Позиция на адресната лента"; - -/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ -"mqV-pf-NZ1.text" = "Позиция на адресната лента"; - /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Позволете на DuckDuckGo да управлява изскачащите прозорци за съгласие за бисквитки"; @@ -181,36 +76,9 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Незащитени сайтове"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "Относно DuckDuckGo"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Най-горе"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "Още от DuckDuckGo"; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Синхронизиране и архивиране"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Сърфирайте поверително с нашето приложение за Mac "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Етикет"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "Приложение DuckDuckGo за Windows"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Размер на текста"; -/* Class = "UILabel"; text = "Private Voice Search"; ObjectID = "Swa-O7-n8W"; */ -"Swa-O7-n8W.text" = "Поверително гласово търсене"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Показване на клавиатура в"; @@ -220,42 +88,18 @@ /* Class = "UITableViewSection"; headerTitle = "Action"; ObjectID = "U2M-6p-6nl"; */ "U2M-6p-6nl.headerTitle" = "Действие"; -/* Class = "UILabel"; text = "Autocomplete Suggestions"; ObjectID = "U8i-cQ-5WW"; */ -"U8i-cQ-5WW.text" = "Показване на предложенията за автоматично довършване"; - -/* Class = "UINavigationItem"; title = "Fire Button Animation"; ObjectID = "uns-8w-IwL"; Note = "Fire button animation setting page title"; */ -"uns-8w-IwL.title" = "Анимация на огнения бутон"; - /* Class = "UILabel"; text = "Remove All Fireproof Sites"; ObjectID = "UZx-52-aer"; */ "UZx-52-aer.text" = "Премахване на всички огнеустойчиви сайтове"; -/* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "vPz-uO-6gB"; */ -"vPz-uO-6gB.text" = "Глобален контрол на поверителността (GPC)"; - /* Class = "UITableViewSection"; footerTitle = "App exit is defined by swiping the app to close it while inactivity is when the app is in the background."; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.footerTitle" = "Определен е изход от приложението чрез плъзгане за затваряне на приложението, а липсата на активност е когато приложението е във фонов режим."; /* Class = "UITableViewSection"; headerTitle = "Desired timing"; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.headerTitle" = "Желан период"; -/* Class = "UILabel"; text = "Set as Default Browser"; ObjectID = "xof-5k-PkI"; */ -"xof-5k-PkI.text" = "Задаване като браузър по подразбиране"; - /* Class = "UINavigationItem"; title = "Fireproof Sites"; ObjectID = "xUX-nF-HOl"; */ "xUX-nF-HOl.title" = "Огнеустойчиви сайтове"; -/* Class = "UILabel"; text = "Block email trackers and hide your address"; ObjectID = "Y6Y-wA-n6Z"; */ -"Y6Y-wA-n6Z.text" = "Блокирайте имейл тракерите и скрийте своя адрес"; - -/* Class = "UILabel"; text = "Keyboard"; ObjectID = "yoZ-jw-Cu3"; */ -"yoZ-jw-Cu3.text" = "Клавиатура"; - -/* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ -"yvj-LL-MiR.text" = "Добавяне на приложението към панела"; - -/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "Приложение DuckDuckGo за Mac"; - /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Нов раздел"; diff --git a/DuckDuckGo/cs.lproj/Localizable.strings b/DuckDuckGo/cs.lproj/Localizable.strings index ee3a8a3285..c3b5ed0be1 100644 --- a/DuckDuckGo/cs.lproj/Localizable.strings +++ b/DuckDuckGo/cs.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Klávesnice"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Hesla"; + /* Settings title for the 'More' section */ "settings.more" = "Další od DuckDuckGo"; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Náhledy dlouhého stisknutí"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Které webové stránky jsou poškozené?"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Kód pro obnovení je zkopírovaný do schránky"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Smazat data ze serveru"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "Dojde k odpojení všech zařízení a smazání synchronizovaných dat ze serveru."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "Smazat data ze serveru?"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Odstranit"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "„%@“ už nebude mít přístup k synchronizovaným datům."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "Smazat zařízení?"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Odstranit"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "Tohle zařízení už nebude mít přístup ke tvým synchronizovaným datům."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "Vypnout synchronizaci?"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "Omlouváme se, ale funkce synchronizace a zálohování teď není dostupná. Zkus to znovu později."; diff --git a/DuckDuckGo/cs.lproj/Settings.strings b/DuckDuckGo/cs.lproj/Settings.strings index 45ffb5e17f..209b3e80b4 100644 --- a/DuckDuckGo/cs.lproj/Settings.strings +++ b/DuckDuckGo/cs.lproj/Settings.strings @@ -1,15 +1,3 @@ -/* Class = "UILabel"; text = "Fireproof Sites"; ObjectID = "0DQ-yq-UuT"; */ -"0DQ-yq-UuT.text" = "Stránky s ochranou"; - -/* Class = "UITableViewSection"; footerTitle = "Disable to prevent links from automatically opening in other installed apps."; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.footerTitle" = "Zakažte, chcete-li zabránit automatickému otevírání odkazů v jiných nainstalovaných aplikacích."; - -/* Class = "UITableViewSection"; headerTitle = "Customize"; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.headerTitle" = "Přizpůsobit"; - -/* Class = "UILabel"; text = "Version"; ObjectID = "2ky-s9-1aZ"; */ -"2ky-s9-1aZ.text" = "Verze"; - /* Class = "UINavigationItem"; title = "Keyboard"; ObjectID = "2pp-PM-6rW"; */ "2pp-PM-6rW.title" = "Klávesnice"; @@ -31,30 +19,9 @@ /* Class = "UILabel"; text = "Clear Tabs and Data"; ObjectID = "9fc-9r-4aA"; */ "9fc-9r-4aA.text" = "Vymazat karty a data"; -/* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ -"9Ko-0g-T3h.text" = "Velikost textu"; - -/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ -"9kt-6R-XiZ.text" = "Název"; - /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Spuštění aplikace"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Otevřít odkazy v přidružených aplikacích"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Nabídka ladění"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Hesla"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Animace"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "Ochrana e-mailu"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "Data nebo karty budou vymazány po restartu aplikace."; @@ -64,114 +31,42 @@ /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ "btj-ri-kRr.title" = "Správa vyskakovacích oken ohledně cookies"; -/* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ -"cKo-er-HNj.text" = "Ikona aplikace"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "CR5-Al-WIW"; */ -"CR5-Al-WIW.text" = "Štítek"; - /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Globální kontrola ochrany osobních údajů (GPC)"; /* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ "CxT-QK-iVn.title" = "Přidat"; -/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ -"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; - /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Ukončení aplikace, neaktivní po dobu 15 minut"; -/* Class = "UILabel"; text = "10.0.1 (Build 10005)"; ObjectID = "d5n-vG-8kF"; */ -"d5n-vG-8kF.text" = "10.0.1 (Build 10005)"; - -/* Class = "UILabel"; text = "Application Lock"; ObjectID = "dBZ-yq-FYj"; */ -"dBZ-yq-FYj.text" = "Zámek aplikace"; - -/* Class = "UITableViewSection"; headerTitle = "Appearance"; ObjectID = "dj9-vh-Rig"; */ -"dj9-vh-Rig.headerTitle" = "Vzhled"; - -/* Class = "UITableViewSection"; footerTitle = "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening."; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.footerTitle" = "Pokud je nastaveno Touch ID, Face ID nebo přístupový kód k systému, budete při otevírání požádáni o odemknutí aplikace."; - -/* Class = "UITableViewSection"; headerTitle = "Privacy"; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.headerTitle" = "Soukromí"; - /* Class = "UILabel"; text = "Label"; ObjectID = "dud-qo-Ces"; */ "dud-qo-Ces.text" = "Štítek"; -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Dyd-bm-goj"; */ -"Dyd-bm-goj.title" = "Nastavení"; - -/* Class = "UILabel"; text = "100%"; ObjectID = "EB8-09-gt2"; */ -"EB8-09-gt2.text" = "100 %"; - /* Class = "UILabel"; text = "Label"; ObjectID = "EIq-Ev-nfj"; */ "EIq-Ev-nfj.text" = "Štítek"; -/* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "ekF-SJ-PAQ"; */ -"ekF-SJ-PAQ.text" = "Automaticky vymazat data"; - /* Class = "UILabel"; text = "App Exit, Inactive for 5 Minutes"; ObjectID = "ElX-yE-4PX"; */ "ElX-yE-4PX.text" = "Ukončení aplikace, neaktivní po dobu 5 minut"; /* Class = "UINavigationItem"; title = "App Icon"; ObjectID = "eMH-uv-Kms"; */ "eMH-uv-Kms.title" = "Ikona aplikace"; -/* Class = "UILabel"; text = "Theme"; ObjectID = "f1O-6u-LFY"; */ -"f1O-6u-LFY.text" = "Téma"; - /* Class = "UILabel"; text = "App Exit Only"; ObjectID = "Fal-1Y-o2S"; */ "Fal-1Y-o2S.text" = "Pouze ukončení aplikace"; /* Class = "UITableViewController"; title = "Automatically Clear Data"; ObjectID = "fdJ-b1-Des"; */ "fdJ-b1-Des.title" = "Automaticky vymazat data"; -/* Class = "UILabel"; text = "Unprotected Sites"; ObjectID = "FHC-1z-Z3v"; */ -"FHC-1z-Z3v.text" = "Nechráněné stránky"; - -/* Class = "UITableViewSection"; headerTitle = "About"; ObjectID = "FpT-1C-xtx"; */ -"FpT-1C-xtx.headerTitle" = "O společnosti"; - /* Class = "UITableViewController"; title = "Global Privacy Control (GPC)"; ObjectID = "fV3-86-QQj"; */ "fV3-86-QQj.title" = "Globální kontrola ochrany osobních údajů (GPC)"; -/* Class = "UILabel"; text = "Add Widget to Home Screen"; ObjectID = "Fxu-zn-51Z"; */ -"Fxu-zn-51Z.text" = "Přidat widget na domovskou obrazovku"; - -/* Class = "UILabel"; text = "Fire Button Animation"; ObjectID = "gBo-Cu-e2k"; Note = "Fire button animation settings item"; */ -"gBo-Cu-e2k.text" = "Animace tlačítka pro mazání"; - -/* Class = "UILabel"; text = "Default"; ObjectID = "Gbx-kl-uOO"; */ -"Gbx-kl-uOO.text" = "Výchozí"; - -/* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Správa vyskakovacích oken ohledně cookies"; - -/* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ -"gS2-mg-l7R.title" = "Téma"; - -/* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ -"HLr-R8-xxF.text" = "Náhledy dlouhého stisknutí"; - -/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ -"hoT-Nu-KXP.text" = "Zajisti si soukromí na webu – v naší aplikaci pro Windows"; - /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Ochrana osobních údajů povolena pro všechny weby"; /* Class = "UICollectionViewController"; title = "Icon"; ObjectID = "jbD-Oy-Cmw"; */ "jbD-Oy-Cmw.title" = "Ikona"; -/* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ -"m23-t6-9cb.text" = "Podělte se o zpětnou vazbu"; - -/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ -"mLn-1x-Fl5.title" = "Pozice adresního řádku"; - -/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ -"mqV-pf-NZ1.text" = "Pozice adresního řádku"; - /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Nechat správu oken pro souhlas s cookies na DuckDuckGo"; @@ -181,36 +76,9 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Nechráněné stránky"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "O DuckDuckGo"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Nahoru"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "Další od DuckDuckGo"; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Synchronizace a zálohování"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Anonymní brouzdání po internetu s naší aplikací pro Mac "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Štítek"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "DuckDuckGo pro Windows"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Velikost textu"; -/* Class = "UILabel"; text = "Private Voice Search"; ObjectID = "Swa-O7-n8W"; */ -"Swa-O7-n8W.text" = "Soukromé hlasové vyhledávání"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Zobrazit klávesnici na"; @@ -220,42 +88,18 @@ /* Class = "UITableViewSection"; headerTitle = "Action"; ObjectID = "U2M-6p-6nl"; */ "U2M-6p-6nl.headerTitle" = "Akce"; -/* Class = "UILabel"; text = "Autocomplete Suggestions"; ObjectID = "U8i-cQ-5WW"; */ -"U8i-cQ-5WW.text" = "Zobrazit návrhy automatického doplňování"; - -/* Class = "UINavigationItem"; title = "Fire Button Animation"; ObjectID = "uns-8w-IwL"; Note = "Fire button animation setting page title"; */ -"uns-8w-IwL.title" = "Animace tlačítka pro mazání"; - /* Class = "UILabel"; text = "Remove All Fireproof Sites"; ObjectID = "UZx-52-aer"; */ "UZx-52-aer.text" = "Odebrat všechny stránky s ochranou"; -/* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "vPz-uO-6gB"; */ -"vPz-uO-6gB.text" = "Globální kontrola ochrany osobních údajů (GPC)"; - /* Class = "UITableViewSection"; footerTitle = "App exit is defined by swiping the app to close it while inactivity is when the app is in the background."; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.footerTitle" = "Ukončení aplikace je definováno přejetím prstem po aplikaci za účelem zavření, zatímco neaktivní aplikace znamená, že aplikace běží na pozadí."; /* Class = "UITableViewSection"; headerTitle = "Desired timing"; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.headerTitle" = "Požadované časování"; -/* Class = "UILabel"; text = "Set as Default Browser"; ObjectID = "xof-5k-PkI"; */ -"xof-5k-PkI.text" = "Nastavit jako výchozí prohlížeč"; - /* Class = "UINavigationItem"; title = "Fireproof Sites"; ObjectID = "xUX-nF-HOl"; */ "xUX-nF-HOl.title" = "Stránky s ochranou"; -/* Class = "UILabel"; text = "Block email trackers and hide your address"; ObjectID = "Y6Y-wA-n6Z"; */ -"Y6Y-wA-n6Z.text" = "Blokování trackerů v e-mailu a skrytí adresy"; - -/* Class = "UILabel"; text = "Keyboard"; ObjectID = "yoZ-jw-Cu3"; */ -"yoZ-jw-Cu3.text" = "Klávesnice"; - -/* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ -"yvj-LL-MiR.text" = "Přidat aplikaci do docku"; - -/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "DuckDuckGo pro Mac"; - /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Nová karta"; diff --git a/DuckDuckGo/da.lproj/Localizable.strings b/DuckDuckGo/da.lproj/Localizable.strings index b2c61e8c43..5175783178 100644 --- a/DuckDuckGo/da.lproj/Localizable.strings +++ b/DuckDuckGo/da.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Tastatur"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Adgangskoder"; + /* Settings title for the 'More' section */ "settings.more" = "Mere fra DuckDuckGo"; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Eksempler med lang tryk"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Hvilket websted er ødelagt?"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Gendannelseskode kopieret til udklipsholder"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Slet serverdata"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "Alle enheder vil blive afbrudt, og dine synkroniserede data vil blive slettet fra serveren."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "Slet serverdata?"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Fjern"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "\"%@ \" kan ikke længere få adgang til dine synkroniserede data."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "Fjern enhed?"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Fjern"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "Denne enhed vil ikke længere have adgang til dine synkroniserede data."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "Slå synkronisering fra?"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "Beklager, men synkronisering og sikkerhedskopiering er ikke tilgængelig i øjeblikket. Prøv igen senere."; diff --git a/DuckDuckGo/da.lproj/Settings.strings b/DuckDuckGo/da.lproj/Settings.strings index 5d7ae353d9..f4ed615e79 100644 --- a/DuckDuckGo/da.lproj/Settings.strings +++ b/DuckDuckGo/da.lproj/Settings.strings @@ -1,15 +1,3 @@ -/* Class = "UILabel"; text = "Fireproof Sites"; ObjectID = "0DQ-yq-UuT"; */ -"0DQ-yq-UuT.text" = "Brandsikre websteder"; - -/* Class = "UITableViewSection"; footerTitle = "Disable to prevent links from automatically opening in other installed apps."; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.footerTitle" = "Deaktiver for at forhindre, at links automatisk åbnes i andre installerede apps."; - -/* Class = "UITableViewSection"; headerTitle = "Customize"; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.headerTitle" = "Tilpas"; - -/* Class = "UILabel"; text = "Version"; ObjectID = "2ky-s9-1aZ"; */ -"2ky-s9-1aZ.text" = "Version"; - /* Class = "UINavigationItem"; title = "Keyboard"; ObjectID = "2pp-PM-6rW"; */ "2pp-PM-6rW.title" = "Tastatur"; @@ -31,30 +19,9 @@ /* Class = "UILabel"; text = "Clear Tabs and Data"; ObjectID = "9fc-9r-4aA"; */ "9fc-9r-4aA.text" = "Ryd faner og data"; -/* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ -"9Ko-0g-T3h.text" = "Tekststørrelse"; - -/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ -"9kt-6R-XiZ.text" = "Titel"; - /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "App-start"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Åbn links i tilknyttede apps"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Fejlfindingsmenu"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Adgangskoder"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Animation"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "E-mailbeskyttelse"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "Data og/eller faner ryddes ved genstart af appen."; @@ -64,114 +31,42 @@ /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ "btj-ri-kRr.title" = "Administrer cookie pop op-vinduer"; -/* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ -"cKo-er-HNj.text" = "App-ikon"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "CR5-Al-WIW"; */ -"CR5-Al-WIW.text" = "Etiket"; - /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Global Privacy Control (GPC)"; /* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ "CxT-QK-iVn.title" = "Tilføj"; -/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ -"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; - /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "App-afslutning, inaktiv i 15 minutter"; -/* Class = "UILabel"; text = "10.0.1 (Build 10005)"; ObjectID = "d5n-vG-8kF"; */ -"d5n-vG-8kF.text" = "10.0.1 (Build 10005)"; - -/* Class = "UILabel"; text = "Application Lock"; ObjectID = "dBZ-yq-FYj"; */ -"dBZ-yq-FYj.text" = "Applikationslås"; - -/* Class = "UITableViewSection"; headerTitle = "Appearance"; ObjectID = "dj9-vh-Rig"; */ -"dj9-vh-Rig.headerTitle" = "udseende"; - -/* Class = "UITableViewSection"; footerTitle = "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening."; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.footerTitle" = "Hvis Touch ID, Face ID eller en systemadgangskode er indstillet, bliver du bedt om at låse appen op, når du åbner."; - -/* Class = "UITableViewSection"; headerTitle = "Privacy"; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.headerTitle" = "Privatliv"; - /* Class = "UILabel"; text = "Label"; ObjectID = "dud-qo-Ces"; */ "dud-qo-Ces.text" = "Etiket"; -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Dyd-bm-goj"; */ -"Dyd-bm-goj.title" = "Indstillinger"; - -/* Class = "UILabel"; text = "100%"; ObjectID = "EB8-09-gt2"; */ -"EB8-09-gt2.text" = "100 %"; - /* Class = "UILabel"; text = "Label"; ObjectID = "EIq-Ev-nfj"; */ "EIq-Ev-nfj.text" = "Etiket"; -/* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "ekF-SJ-PAQ"; */ -"ekF-SJ-PAQ.text" = "Ryd data automatisk"; - /* Class = "UILabel"; text = "App Exit, Inactive for 5 Minutes"; ObjectID = "ElX-yE-4PX"; */ "ElX-yE-4PX.text" = "App-afslutning, inaktiv i 5 minutter"; /* Class = "UINavigationItem"; title = "App Icon"; ObjectID = "eMH-uv-Kms"; */ "eMH-uv-Kms.title" = "App-ikon"; -/* Class = "UILabel"; text = "Theme"; ObjectID = "f1O-6u-LFY"; */ -"f1O-6u-LFY.text" = "Tema"; - /* Class = "UILabel"; text = "App Exit Only"; ObjectID = "Fal-1Y-o2S"; */ "Fal-1Y-o2S.text" = "Kun ved app-afslutning"; /* Class = "UITableViewController"; title = "Automatically Clear Data"; ObjectID = "fdJ-b1-Des"; */ "fdJ-b1-Des.title" = "Ryd data automatisk"; -/* Class = "UILabel"; text = "Unprotected Sites"; ObjectID = "FHC-1z-Z3v"; */ -"FHC-1z-Z3v.text" = "Ubeskyttede websteder"; - -/* Class = "UITableViewSection"; headerTitle = "About"; ObjectID = "FpT-1C-xtx"; */ -"FpT-1C-xtx.headerTitle" = "Omkring"; - /* Class = "UITableViewController"; title = "Global Privacy Control (GPC)"; ObjectID = "fV3-86-QQj"; */ "fV3-86-QQj.title" = "Global Privacy Control (GPC)"; -/* Class = "UILabel"; text = "Add Widget to Home Screen"; ObjectID = "Fxu-zn-51Z"; */ -"Fxu-zn-51Z.text" = "Tilføj widget til startskærmen"; - -/* Class = "UILabel"; text = "Fire Button Animation"; ObjectID = "gBo-Cu-e2k"; Note = "Fire button animation settings item"; */ -"gBo-Cu-e2k.text" = "Ildknap-animation"; - -/* Class = "UILabel"; text = "Default"; ObjectID = "Gbx-kl-uOO"; */ -"Gbx-kl-uOO.text" = "Tema"; - -/* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Administrer cookie pop op-vinduer"; - -/* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ -"gS2-mg-l7R.title" = "Tema"; - -/* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ -"HLr-R8-xxF.text" = "Eksempler med lang tryk"; - -/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ -"hoT-Nu-KXP.text" = "Browse privat med vores app til Windows"; - /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Databeskyttelse aktiveret for alle websteder"; /* Class = "UICollectionViewController"; title = "Icon"; ObjectID = "jbD-Oy-Cmw"; */ "jbD-Oy-Cmw.title" = "Ikon"; -/* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ -"m23-t6-9cb.text" = "Del feedback"; - -/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ -"mLn-1x-Fl5.title" = "Placering af adresselinje"; - -/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ -"mqV-pf-NZ1.text" = "Placering af adresselinje"; - /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Lad DuckDuckGo administrere pop op-vinduer med samtykke til brug af cookies"; @@ -181,36 +76,9 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Ubeskyttede websteder"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "Om DuckDuckGo"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Top"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "Mere fra DuckDuckGo"; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Synkronisering og sikkerhedskopiering"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Browse privat med vores app til Mac "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Etiket"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "DuckDuckGo-app til Windows"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Tekststørrelse"; -/* Class = "UILabel"; text = "Private Voice Search"; ObjectID = "Swa-O7-n8W"; */ -"Swa-O7-n8W.text" = "Privat stemmesøgning"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Vis tastaturet på"; @@ -220,42 +88,18 @@ /* Class = "UITableViewSection"; headerTitle = "Action"; ObjectID = "U2M-6p-6nl"; */ "U2M-6p-6nl.headerTitle" = "Handling"; -/* Class = "UILabel"; text = "Autocomplete Suggestions"; ObjectID = "U8i-cQ-5WW"; */ -"U8i-cQ-5WW.text" = "Vis forslag til Autoudfyld"; - -/* Class = "UINavigationItem"; title = "Fire Button Animation"; ObjectID = "uns-8w-IwL"; Note = "Fire button animation setting page title"; */ -"uns-8w-IwL.title" = "Ildknap-animation"; - /* Class = "UILabel"; text = "Remove All Fireproof Sites"; ObjectID = "UZx-52-aer"; */ "UZx-52-aer.text" = "Fjern alle brandsikre websteder"; -/* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "vPz-uO-6gB"; */ -"vPz-uO-6gB.text" = "Global Privacy Control (GPC)"; - /* Class = "UITableViewSection"; footerTitle = "App exit is defined by swiping the app to close it while inactivity is when the app is in the background."; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.footerTitle" = "Exit af app er defineret ved at swipe appen for at lukke den, mens inaktivitet er når appen er i baggrunden."; /* Class = "UITableViewSection"; headerTitle = "Desired timing"; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.headerTitle" = "Ønsket timing"; -/* Class = "UILabel"; text = "Set as Default Browser"; ObjectID = "xof-5k-PkI"; */ -"xof-5k-PkI.text" = "Angiv som standardbrowser"; - /* Class = "UINavigationItem"; title = "Fireproof Sites"; ObjectID = "xUX-nF-HOl"; */ "xUX-nF-HOl.title" = "Brandsikre websteder"; -/* Class = "UILabel"; text = "Block email trackers and hide your address"; ObjectID = "Y6Y-wA-n6Z"; */ -"Y6Y-wA-n6Z.text" = "Bloker e-mailtrackere, og skjul din adresse"; - -/* Class = "UILabel"; text = "Keyboard"; ObjectID = "yoZ-jw-Cu3"; */ -"yoZ-jw-Cu3.text" = "Tastatur"; - -/* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ -"yvj-LL-MiR.text" = "Føj appen til din dock"; - -/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "DuckDuckGo-app til Mac"; - /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Ny fane"; diff --git a/DuckDuckGo/de.lproj/Localizable.strings b/DuckDuckGo/de.lproj/Localizable.strings index 6503e6c6db..6dd24b2bb8 100644 --- a/DuckDuckGo/de.lproj/Localizable.strings +++ b/DuckDuckGo/de.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Tastatur"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Passwörter"; + /* Settings title for the 'More' section */ "settings.more" = "Mehr von DuckDuckGo"; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Vorschau durch langes Tippen"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Welche Website ist fehlerhaft?"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Wiederherstellungscode in Zwischenablage kopiert"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Serverdaten löschen"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "Alle Geräte werden getrennt und deine synchronisierten Daten werden vom Server gelöscht."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "Serverdaten löschen?"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Entfernen"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "„%@“ kann nicht mehr auf deine synchronisierten Daten zugreifen."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "Gerät entfernen?"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Entfernen"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "Dieses Gerät kann nicht mehr auf deine synchronisierten Daten zugreifen."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "Synchronisierung deaktivieren?"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "Leider ist Synchronisieren und sichern derzeit nicht verfügbar. Bitte versuche es zu einem späteren Zeitpunkt erneut."; diff --git a/DuckDuckGo/de.lproj/Settings.strings b/DuckDuckGo/de.lproj/Settings.strings index 0024fb7911..1601928b75 100644 --- a/DuckDuckGo/de.lproj/Settings.strings +++ b/DuckDuckGo/de.lproj/Settings.strings @@ -1,15 +1,3 @@ -/* Class = "UILabel"; text = "Fireproof Sites"; ObjectID = "0DQ-yq-UuT"; */ -"0DQ-yq-UuT.text" = "Feuerfeste Seiten"; - -/* Class = "UITableViewSection"; footerTitle = "Disable to prevent links from automatically opening in other installed apps."; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.footerTitle" = "Aktivieren, damit sich Links nicht automatisch in anderen installierten Apps öffnen."; - -/* Class = "UITableViewSection"; headerTitle = "Customize"; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.headerTitle" = "Anpassen"; - -/* Class = "UILabel"; text = "Version"; ObjectID = "2ky-s9-1aZ"; */ -"2ky-s9-1aZ.text" = "Version"; - /* Class = "UINavigationItem"; title = "Keyboard"; ObjectID = "2pp-PM-6rW"; */ "2pp-PM-6rW.title" = "Tastatur"; @@ -31,30 +19,9 @@ /* Class = "UILabel"; text = "Clear Tabs and Data"; ObjectID = "9fc-9r-4aA"; */ "9fc-9r-4aA.text" = "Tabs und Daten löschen"; -/* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ -"9Ko-0g-T3h.text" = "Textgröße"; - -/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ -"9kt-6R-XiZ.text" = "Titel"; - /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Start der App"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Links in den dazugehörigen Apps öffnen"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Menü Fehlerbehebung"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Passwörter"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Animation"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "E-Mail-Schutz"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "Beim erneuten Öffnen der App werden Daten und/oder Tabs gelöscht."; @@ -64,114 +31,42 @@ /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ "btj-ri-kRr.title" = "Cookie-Pop-ups verwalten"; -/* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ -"cKo-er-HNj.text" = "App-Symbol"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "CR5-Al-WIW"; */ -"CR5-Al-WIW.text" = "Label"; - /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Global Privacy Control (GPC)"; /* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ "CxT-QK-iVn.title" = "Hinzufügen"; -/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ -"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; - /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Beim Verlassen der App, 15 Minuten lang inaktiv"; -/* Class = "UILabel"; text = "10.0.1 (Build 10005)"; ObjectID = "d5n-vG-8kF"; */ -"d5n-vG-8kF.text" = "10.0.1 (Build 10005)"; - -/* Class = "UILabel"; text = "Application Lock"; ObjectID = "dBZ-yq-FYj"; */ -"dBZ-yq-FYj.text" = "Anwendungssperre"; - -/* Class = "UITableViewSection"; headerTitle = "Appearance"; ObjectID = "dj9-vh-Rig"; */ -"dj9-vh-Rig.headerTitle" = "Aussehen"; - -/* Class = "UITableViewSection"; footerTitle = "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening."; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.footerTitle" = "Wenn Touch ID, Face ID oder ein Systempasswort eingestellt sind, wirst du aufgefordert, die App beim Öffnen zu entsperren."; - -/* Class = "UITableViewSection"; headerTitle = "Privacy"; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.headerTitle" = "Privatsphäre"; - /* Class = "UILabel"; text = "Label"; ObjectID = "dud-qo-Ces"; */ "dud-qo-Ces.text" = "Label"; -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Dyd-bm-goj"; */ -"Dyd-bm-goj.title" = "Einstellungen"; - -/* Class = "UILabel"; text = "100%"; ObjectID = "EB8-09-gt2"; */ -"EB8-09-gt2.text" = "100 %"; - /* Class = "UILabel"; text = "Label"; ObjectID = "EIq-Ev-nfj"; */ "EIq-Ev-nfj.text" = "Label"; -/* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "ekF-SJ-PAQ"; */ -"ekF-SJ-PAQ.text" = "Daten automatisch löschen"; - /* Class = "UILabel"; text = "App Exit, Inactive for 5 Minutes"; ObjectID = "ElX-yE-4PX"; */ "ElX-yE-4PX.text" = "Beim Verlassen der App, 5 Minuten lang inaktiv"; /* Class = "UINavigationItem"; title = "App Icon"; ObjectID = "eMH-uv-Kms"; */ "eMH-uv-Kms.title" = "App-Symbol"; -/* Class = "UILabel"; text = "Theme"; ObjectID = "f1O-6u-LFY"; */ -"f1O-6u-LFY.text" = "Design"; - /* Class = "UILabel"; text = "App Exit Only"; ObjectID = "Fal-1Y-o2S"; */ "Fal-1Y-o2S.text" = "Nur beim Verlassen der App"; /* Class = "UITableViewController"; title = "Automatically Clear Data"; ObjectID = "fdJ-b1-Des"; */ "fdJ-b1-Des.title" = "Daten automatisch löschen"; -/* Class = "UILabel"; text = "Unprotected Sites"; ObjectID = "FHC-1z-Z3v"; */ -"FHC-1z-Z3v.text" = "Ungeschützte Websites"; - -/* Class = "UITableViewSection"; headerTitle = "About"; ObjectID = "FpT-1C-xtx"; */ -"FpT-1C-xtx.headerTitle" = "Über"; - /* Class = "UITableViewController"; title = "Global Privacy Control (GPC)"; ObjectID = "fV3-86-QQj"; */ "fV3-86-QQj.title" = "Global Privacy Control (GPC)"; -/* Class = "UILabel"; text = "Add Widget to Home Screen"; ObjectID = "Fxu-zn-51Z"; */ -"Fxu-zn-51Z.text" = "Widget zum Startbildschirm hinzufügen"; - -/* Class = "UILabel"; text = "Fire Button Animation"; ObjectID = "gBo-Cu-e2k"; Note = "Fire button animation settings item"; */ -"gBo-Cu-e2k.text" = "Animation der Schaltfläche „Feuer“"; - -/* Class = "UILabel"; text = "Default"; ObjectID = "Gbx-kl-uOO"; */ -"Gbx-kl-uOO.text" = "Standard"; - -/* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Cookie-Pop-ups verwalten"; - -/* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ -"gS2-mg-l7R.title" = "Design"; - -/* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ -"HLr-R8-xxF.text" = "Vorschau durch langes Tippen"; - -/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ -"hoT-Nu-KXP.text" = "Privat browsen mit unserer App für Windows"; - /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Datenschutz für alle Websites aktiviert"; /* Class = "UICollectionViewController"; title = "Icon"; ObjectID = "jbD-Oy-Cmw"; */ "jbD-Oy-Cmw.title" = "Symbol"; -/* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ -"m23-t6-9cb.text" = "Feedback teilen"; - -/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ -"mLn-1x-Fl5.title" = "Position der Adressleiste"; - -/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ -"mqV-pf-NZ1.text" = "Position der Adressleiste"; - /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Lasse Cookie-Zustimmungs-Popups von DuckDuckGo verwalten"; @@ -181,36 +76,9 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Ungeschützte Websites"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "Über DuckDuckGo"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Nach oben"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "Mehr von DuckDuckGo"; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Synchronisieren und sichern"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Privat browsen mit unserer App für Mac "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Label"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "DuckDuckGo-App für Windows"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Textgröße"; -/* Class = "UILabel"; text = "Private Voice Search"; ObjectID = "Swa-O7-n8W"; */ -"Swa-O7-n8W.text" = "Private Sprachsuche"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Tastatur anzeigen bei"; @@ -220,42 +88,18 @@ /* Class = "UITableViewSection"; headerTitle = "Action"; ObjectID = "U2M-6p-6nl"; */ "U2M-6p-6nl.headerTitle" = "Aktion"; -/* Class = "UILabel"; text = "Autocomplete Suggestions"; ObjectID = "U8i-cQ-5WW"; */ -"U8i-cQ-5WW.text" = "Vorschläge für die Autovervollständigung"; - -/* Class = "UINavigationItem"; title = "Fire Button Animation"; ObjectID = "uns-8w-IwL"; Note = "Fire button animation setting page title"; */ -"uns-8w-IwL.title" = "Animation der Schaltfläche „Feuer“"; - /* Class = "UILabel"; text = "Remove All Fireproof Sites"; ObjectID = "UZx-52-aer"; */ "UZx-52-aer.text" = "Alle feuerfesten Seiten entfernen"; -/* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "vPz-uO-6gB"; */ -"vPz-uO-6gB.text" = "Global Privacy Control (GPC)"; - /* Class = "UITableViewSection"; footerTitle = "App exit is defined by swiping the app to close it while inactivity is when the app is in the background."; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.footerTitle" = "Die App wird verlassen, wenn sie durch Wegwischen geschlossen wird. Die App ist inaktiv, wenn sie im Hintergrund läuft."; /* Class = "UITableViewSection"; headerTitle = "Desired timing"; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.headerTitle" = "Gewünschter Zeitpunkt"; -/* Class = "UILabel"; text = "Set as Default Browser"; ObjectID = "xof-5k-PkI"; */ -"xof-5k-PkI.text" = "Als Standard-Browser festlegen"; - /* Class = "UINavigationItem"; title = "Fireproof Sites"; ObjectID = "xUX-nF-HOl"; */ "xUX-nF-HOl.title" = "Feuerfeste Seiten"; -/* Class = "UILabel"; text = "Block email trackers and hide your address"; ObjectID = "Y6Y-wA-n6Z"; */ -"Y6Y-wA-n6Z.text" = "E-Mail-Tracker blockieren und deine Adresse verbergen"; - -/* Class = "UILabel"; text = "Keyboard"; ObjectID = "yoZ-jw-Cu3"; */ -"yoZ-jw-Cu3.text" = "Tastatur"; - -/* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ -"yvj-LL-MiR.text" = "App zum Dock hinzufügen"; - -/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "DuckDuckGo-App für Mac"; - /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Neuer Tab"; diff --git a/DuckDuckGo/el.lproj/Localizable.strings b/DuckDuckGo/el.lproj/Localizable.strings index c1d8dadb77..1b49494fe1 100644 --- a/DuckDuckGo/el.lproj/Localizable.strings +++ b/DuckDuckGo/el.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Πληκτρολόγιο"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Κωδικός πρόσβασης"; + /* Settings title for the 'More' section */ "settings.more" = "Περισσότερα από το DuckDuckGo"; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Προεπισκοπήσεις με παρατεταμένο πάτημα"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Ποιος ιστότοπος είναι κατεστραμμένος;"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Ο κωδικός ανάκτησης αντιγράφηκε στο πρόχειρο"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Διαγραφή δεδομένων διακομιστή"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "Όλες οι συσκευές θα αποσυνδεθούν και τα συγχρονισμένα δεδομένα σας θα διαγραφούν από τον διακομιστή."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "Διαγραφή δεδομένων διακομιστή;"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Αφαίρεση"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "Το «%@» δεν θα μπορεί πλέον να έχει πρόσβαση στα συγχρονισμένα δεδομένα σας."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "Αφαίρεση συσκευής;"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Αφαίρεση"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "Αυτή η Συσκευή δεν θα μπορεί πλέον να έχει πρόσβαση στα συγχρονισμένα δεδομένα σας."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "Απενεργοποίηση συγχρονισμού;"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "Δυστυχώς, η λειτουργία Συγχρονισμός και δημιουργία αντιγράφων ασφαλείας δεν είναι διαθέσιμη προς το παρόν. Ξαναδοκιμάστε αργότερα."; diff --git a/DuckDuckGo/el.lproj/Settings.strings b/DuckDuckGo/el.lproj/Settings.strings index 8021852746..6f07da6585 100644 --- a/DuckDuckGo/el.lproj/Settings.strings +++ b/DuckDuckGo/el.lproj/Settings.strings @@ -1,15 +1,3 @@ -/* Class = "UILabel"; text = "Fireproof Sites"; ObjectID = "0DQ-yq-UuT"; */ -"0DQ-yq-UuT.text" = "Ασφαλείς ιστότοποι"; - -/* Class = "UITableViewSection"; footerTitle = "Disable to prevent links from automatically opening in other installed apps."; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.footerTitle" = "Απενεργοποιήστε για να αποτρέψετε το αυτόματο άνοιγμα συνδέσμων σε άλλες εγκατεστημένες εφαρμογές."; - -/* Class = "UITableViewSection"; headerTitle = "Customize"; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.headerTitle" = "Προσαρμογή"; - -/* Class = "UILabel"; text = "Version"; ObjectID = "2ky-s9-1aZ"; */ -"2ky-s9-1aZ.text" = "Έκδοση"; - /* Class = "UINavigationItem"; title = "Keyboard"; ObjectID = "2pp-PM-6rW"; */ "2pp-PM-6rW.title" = "Πληκτρολόγιο"; @@ -31,30 +19,9 @@ /* Class = "UILabel"; text = "Clear Tabs and Data"; ObjectID = "9fc-9r-4aA"; */ "9fc-9r-4aA.text" = "Εκκαθάριση καρτελών και δεδομένων"; -/* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ -"9Ko-0g-T3h.text" = "Μέγεθος κειμένου"; - -/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ -"9kt-6R-XiZ.text" = "Τίτλος"; - /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Εκκίνηση εφαρμογής"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Ανοίξτε τους συνδέσμους στις σχετικές εφαρμογές"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Μενού επανόρθωσης σφαλμάτων"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Κωδικοί πρόσβασης"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Κίνηση"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "Προστασία email"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "Τα δεδομένα ή/και οι καρτέλες θα διαγραφούν κατά την επανεκκίνηση της εφαρμογής."; @@ -64,114 +31,42 @@ /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ "btj-ri-kRr.title" = "Διαχείριση αναδυόμενων παραθύρων cookies"; -/* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ -"cKo-er-HNj.text" = "Εικονίδιο Εφαρμογής"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "CR5-Al-WIW"; */ -"CR5-Al-WIW.text" = "Label"; - /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Παγκόσμιος έλεγχος απορρήτου (GPC)"; /* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ "CxT-QK-iVn.title" = "Προσθήκη"; -/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ -"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; - /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Έξοδος εφαρμογής, ανενεργή για 15 λεπτά"; -/* Class = "UILabel"; text = "10.0.1 (Build 10005)"; ObjectID = "d5n-vG-8kF"; */ -"d5n-vG-8kF.text" = "10.0.1 (Δομή 10005)"; - -/* Class = "UILabel"; text = "Application Lock"; ObjectID = "dBZ-yq-FYj"; */ -"dBZ-yq-FYj.text" = "Κλείδωμα εφαρμογής"; - -/* Class = "UITableViewSection"; headerTitle = "Appearance"; ObjectID = "dj9-vh-Rig"; */ -"dj9-vh-Rig.headerTitle" = "Εμφάνιση"; - -/* Class = "UITableViewSection"; footerTitle = "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening."; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.footerTitle" = "Εάν έχει οριστεί Touch ID, Face ID ή κωδικός πρόσβασης συστήματος, θα σας ζητηθεί να ξεκλειδώσετε την εφαρμογή κατά το άνοιγμά της."; - -/* Class = "UITableViewSection"; headerTitle = "Privacy"; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.headerTitle" = "Ιδιωτικότητα"; - /* Class = "UILabel"; text = "Label"; ObjectID = "dud-qo-Ces"; */ "dud-qo-Ces.text" = "Label"; -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Dyd-bm-goj"; */ -"Dyd-bm-goj.title" = "Ρυθμίσεις"; - -/* Class = "UILabel"; text = "100%"; ObjectID = "EB8-09-gt2"; */ -"EB8-09-gt2.text" = "100%"; - /* Class = "UILabel"; text = "Label"; ObjectID = "EIq-Ev-nfj"; */ "EIq-Ev-nfj.text" = "Label"; -/* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "ekF-SJ-PAQ"; */ -"ekF-SJ-PAQ.text" = "Αυτόματη εκκαθάριση δεδομένων"; - /* Class = "UILabel"; text = "App Exit, Inactive for 5 Minutes"; ObjectID = "ElX-yE-4PX"; */ "ElX-yE-4PX.text" = "Έξοδος εφαρμογής, ανενεργή για 5 λεπτά"; /* Class = "UINavigationItem"; title = "App Icon"; ObjectID = "eMH-uv-Kms"; */ "eMH-uv-Kms.title" = "Εικονίδιο Εφαρμογής"; -/* Class = "UILabel"; text = "Theme"; ObjectID = "f1O-6u-LFY"; */ -"f1O-6u-LFY.text" = "Θέμα"; - /* Class = "UILabel"; text = "App Exit Only"; ObjectID = "Fal-1Y-o2S"; */ "Fal-1Y-o2S.text" = "Μόνο έξοδος εφαρμογής"; /* Class = "UITableViewController"; title = "Automatically Clear Data"; ObjectID = "fdJ-b1-Des"; */ "fdJ-b1-Des.title" = "Αυτόματη απαλοιφή δεδομένων"; -/* Class = "UILabel"; text = "Unprotected Sites"; ObjectID = "FHC-1z-Z3v"; */ -"FHC-1z-Z3v.text" = "Μη προστατευόμενοι ιστότοποι"; - -/* Class = "UITableViewSection"; headerTitle = "About"; ObjectID = "FpT-1C-xtx"; */ -"FpT-1C-xtx.headerTitle" = "Σχετικά"; - /* Class = "UITableViewController"; title = "Global Privacy Control (GPC)"; ObjectID = "fV3-86-QQj"; */ "fV3-86-QQj.title" = "Παγκόσμιος έλεγχος απορρήτου (GPC)"; -/* Class = "UILabel"; text = "Add Widget to Home Screen"; ObjectID = "Fxu-zn-51Z"; */ -"Fxu-zn-51Z.text" = "Προσθήκη μικροεφαρμογής στην Αρχική οθόνη"; - -/* Class = "UILabel"; text = "Fire Button Animation"; ObjectID = "gBo-Cu-e2k"; Note = "Fire button animation settings item"; */ -"gBo-Cu-e2k.text" = "Κινούμενο κουμπί φωτιάς"; - -/* Class = "UILabel"; text = "Default"; ObjectID = "Gbx-kl-uOO"; */ -"Gbx-kl-uOO.text" = "Προεπιλογή"; - -/* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Διαχείριση αναδυόμενων παραθύρων cookies"; - -/* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ -"gS2-mg-l7R.title" = "Θέμα"; - -/* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ -"HLr-R8-xxF.text" = "Προεπισκοπήσεις με παρατεταμένο πάτημα"; - -/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ -"hoT-Nu-KXP.text" = "Περιηγηθείτε ιδιωτικά με την εφαρμογή μας για Windows"; - /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Η προστασία προσωπικών δεδομένων είναι ενεργοποιημένη για όλους τους ιστότοπους"; /* Class = "UICollectionViewController"; title = "Icon"; ObjectID = "jbD-Oy-Cmw"; */ "jbD-Oy-Cmw.title" = "Εικονίδιο"; -/* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ -"m23-t6-9cb.text" = "Κοινοποίηση σχολίου"; - -/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ -"mLn-1x-Fl5.title" = "Θέση γραμμής διευθύνσεων"; - -/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ -"mqV-pf-NZ1.text" = "Θέση γραμμής διευθύνσεων"; - /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Επιτρέψτε στο DuckDuckGo να διαχειρίζεται τα αναδυόμενα παράθυρα συναίνεσης για cookies"; @@ -181,36 +76,9 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Μη προστατευόμενοι ιστότοποι"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "Σχετικά με το DuckDuckGo"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Κορυφή"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "Περισσότερα από το DuckDuckGo"; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Συγχρονισμός και δημιουργία αντιγράφων ασφαλείας"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Περιηγηθείτε ιδιωτικά με την εφαρμογή μας για Mac "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Label"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "Εφαρμογή DuckDuckGo Windows"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Μέγεθος κειμένου"; -/* Class = "UILabel"; text = "Private Voice Search"; ObjectID = "Swa-O7-n8W"; */ -"Swa-O7-n8W.text" = "Ιδιωτική φωνητική αναζήτηση"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Εμφάνιση πληκτρολογίου όταν"; @@ -220,42 +88,18 @@ /* Class = "UITableViewSection"; headerTitle = "Action"; ObjectID = "U2M-6p-6nl"; */ "U2M-6p-6nl.headerTitle" = "Ενέργεια"; -/* Class = "UILabel"; text = "Autocomplete Suggestions"; ObjectID = "U8i-cQ-5WW"; */ -"U8i-cQ-5WW.text" = "Εμφάνιση προτάσεων αυτόματης συμπλήρωσης"; - -/* Class = "UINavigationItem"; title = "Fire Button Animation"; ObjectID = "uns-8w-IwL"; Note = "Fire button animation setting page title"; */ -"uns-8w-IwL.title" = "Κινούμενο κουμπί φωτιάς"; - /* Class = "UILabel"; text = "Remove All Fireproof Sites"; ObjectID = "UZx-52-aer"; */ "UZx-52-aer.text" = "Κατάργηση όλων των ασφαλών ιστότοπων"; -/* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "vPz-uO-6gB"; */ -"vPz-uO-6gB.text" = "Παγκόσμιος έλεγχος απορρήτου (GPC)"; - /* Class = "UITableViewSection"; footerTitle = "App exit is defined by swiping the app to close it while inactivity is when the app is in the background."; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.footerTitle" = "Η έξοδος από την εφαρμογή ορίζεται σύροντας την εφαρμογή για να κλείσει, ενώ είναι σε αδράνεια όταν η εφαρμογή βρίσκεται στο παρασκήνιο."; /* Class = "UITableViewSection"; headerTitle = "Desired timing"; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.headerTitle" = "Επιθυμητός χρονισμός"; -/* Class = "UILabel"; text = "Set as Default Browser"; ObjectID = "xof-5k-PkI"; */ -"xof-5k-PkI.text" = "Ορισμός ως προεπιλεγμένο πρόγραμμα περιήγησης"; - /* Class = "UINavigationItem"; title = "Fireproof Sites"; ObjectID = "xUX-nF-HOl"; */ "xUX-nF-HOl.title" = "Ασφαλείς ιστότοποι"; -/* Class = "UILabel"; text = "Block email trackers and hide your address"; ObjectID = "Y6Y-wA-n6Z"; */ -"Y6Y-wA-n6Z.text" = "Αποκλείστε εφαρμογές παρακολούθησης email και αποκρύψτε τη διεύθυνσή σας"; - -/* Class = "UILabel"; text = "Keyboard"; ObjectID = "yoZ-jw-Cu3"; */ -"yoZ-jw-Cu3.text" = "Πληκτρολόγιο"; - -/* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ -"yvj-LL-MiR.text" = "Προσθήκη εφαρμογής στο Dock σας"; - -/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "Εφαρμογή DuckDuckGo Mac"; - /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Νέα καρτέλα"; diff --git a/DuckDuckGo/es.lproj/Localizable.strings b/DuckDuckGo/es.lproj/Localizable.strings index 26463e12de..3d79ed55c8 100644 --- a/DuckDuckGo/es.lproj/Localizable.strings +++ b/DuckDuckGo/es.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Teclado"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Contraseñas"; + /* Settings title for the 'More' section */ "settings.more" = "Más sobre DuckDuckGo"; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Vistas previas con pulsación larga"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "¿Qué sitio web no funciona?"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Código de recuperación copiado en el portapapeles"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Eliminar datos del servidor"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "Todos los dispositivos se desconectarán y tus datos sincronizados se eliminarán del servidor."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "¿Eliminar datos del servidor?"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Eliminar"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "\"%@\" ya no podrá acceder a tus datos sincronizados."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "¿Eliminar dispositivo?"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Eliminar"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "Este Dispositivo ya no podrá acceder a tus datos sincronizados."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "¿Desactivar sincronización?"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "Lo sentimos, pero la sincronización y la copia de seguridad no están disponibles en este momento. Inténtalo de nuevo más tarde."; diff --git a/DuckDuckGo/es.lproj/Settings.strings b/DuckDuckGo/es.lproj/Settings.strings index afc0a95560..062b36d7bb 100644 --- a/DuckDuckGo/es.lproj/Settings.strings +++ b/DuckDuckGo/es.lproj/Settings.strings @@ -1,15 +1,3 @@ -/* Class = "UILabel"; text = "Fireproof Sites"; ObjectID = "0DQ-yq-UuT"; */ -"0DQ-yq-UuT.text" = "Sitios web a prueba de fuego"; - -/* Class = "UITableViewSection"; footerTitle = "Disable to prevent links from automatically opening in other installed apps."; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.footerTitle" = "Desactivar para evitar que los enlaces se abran automáticamente en otras aplicaciones instaladas."; - -/* Class = "UITableViewSection"; headerTitle = "Customize"; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.headerTitle" = "Personalizar"; - -/* Class = "UILabel"; text = "Version"; ObjectID = "2ky-s9-1aZ"; */ -"2ky-s9-1aZ.text" = "Versión"; - /* Class = "UINavigationItem"; title = "Keyboard"; ObjectID = "2pp-PM-6rW"; */ "2pp-PM-6rW.title" = "Teclado"; @@ -31,30 +19,9 @@ /* Class = "UILabel"; text = "Clear Tabs and Data"; ObjectID = "9fc-9r-4aA"; */ "9fc-9r-4aA.text" = "Borrar datos y pestañas"; -/* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ -"9Ko-0g-T3h.text" = "Tamaño del texto"; - -/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ -"9kt-6R-XiZ.text" = "Título"; - /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Abrir aplicación"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Abrir enlaces en aplicaciones asociadas"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Menú Depurar"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Contraseñas"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Animación"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "Protección del correo electrónico"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "Se borrarán los datos o las pestañas al reiniciar la aplicación."; @@ -64,114 +31,42 @@ /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ "btj-ri-kRr.title" = "Administrar ventanas emergentes de cookies"; -/* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ -"cKo-er-HNj.text" = "Icono de la aplicación"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "CR5-Al-WIW"; */ -"CR5-Al-WIW.text" = "Etiqueta"; - /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Control Global de Privacidad (GPC)"; /* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ "CxT-QK-iVn.title" = "Añadir"; -/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ -"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; - /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Salir de la aplicación (15 minutos de inactividad)"; -/* Class = "UILabel"; text = "10.0.1 (Build 10005)"; ObjectID = "d5n-vG-8kF"; */ -"d5n-vG-8kF.text" = "10.0.1 (Build 10005)"; - -/* Class = "UILabel"; text = "Application Lock"; ObjectID = "dBZ-yq-FYj"; */ -"dBZ-yq-FYj.text" = "Bloqueo de aplicación"; - -/* Class = "UITableViewSection"; headerTitle = "Appearance"; ObjectID = "dj9-vh-Rig"; */ -"dj9-vh-Rig.headerTitle" = "Apariencia"; - -/* Class = "UITableViewSection"; footerTitle = "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening."; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.footerTitle" = "Si se establece Touch ID, Face ID o una contraseña del sistema, deberás desbloquear la aplicación al abrirla."; - -/* Class = "UITableViewSection"; headerTitle = "Privacy"; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.headerTitle" = "Privacidad"; - /* Class = "UILabel"; text = "Label"; ObjectID = "dud-qo-Ces"; */ "dud-qo-Ces.text" = "Etiqueta"; -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Dyd-bm-goj"; */ -"Dyd-bm-goj.title" = "Ajustes"; - -/* Class = "UILabel"; text = "100%"; ObjectID = "EB8-09-gt2"; */ -"EB8-09-gt2.text" = "100 %"; - /* Class = "UILabel"; text = "Label"; ObjectID = "EIq-Ev-nfj"; */ "EIq-Ev-nfj.text" = "Etiqueta"; -/* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "ekF-SJ-PAQ"; */ -"ekF-SJ-PAQ.text" = "Borrar datos automáticamente"; - /* Class = "UILabel"; text = "App Exit, Inactive for 5 Minutes"; ObjectID = "ElX-yE-4PX"; */ "ElX-yE-4PX.text" = "Salir de la aplicación (5 minutos de inactividad)"; /* Class = "UINavigationItem"; title = "App Icon"; ObjectID = "eMH-uv-Kms"; */ "eMH-uv-Kms.title" = "Icono de la aplicación"; -/* Class = "UILabel"; text = "Theme"; ObjectID = "f1O-6u-LFY"; */ -"f1O-6u-LFY.text" = "Tema"; - /* Class = "UILabel"; text = "App Exit Only"; ObjectID = "Fal-1Y-o2S"; */ "Fal-1Y-o2S.text" = "Salir de la aplicación"; /* Class = "UITableViewController"; title = "Automatically Clear Data"; ObjectID = "fdJ-b1-Des"; */ "fdJ-b1-Des.title" = "Borrar datos automáticamente"; -/* Class = "UILabel"; text = "Unprotected Sites"; ObjectID = "FHC-1z-Z3v"; */ -"FHC-1z-Z3v.text" = "Sitios no protegidos"; - -/* Class = "UITableViewSection"; headerTitle = "About"; ObjectID = "FpT-1C-xtx"; */ -"FpT-1C-xtx.headerTitle" = "Acerca de"; - /* Class = "UITableViewController"; title = "Global Privacy Control (GPC)"; ObjectID = "fV3-86-QQj"; */ "fV3-86-QQj.title" = "Control Global de Privacidad (GPC)"; -/* Class = "UILabel"; text = "Add Widget to Home Screen"; ObjectID = "Fxu-zn-51Z"; */ -"Fxu-zn-51Z.text" = "Añadir widget a la pantalla de inicio"; - -/* Class = "UILabel"; text = "Fire Button Animation"; ObjectID = "gBo-Cu-e2k"; Note = "Fire button animation settings item"; */ -"gBo-Cu-e2k.text" = "Animación del botón Fuego"; - -/* Class = "UILabel"; text = "Default"; ObjectID = "Gbx-kl-uOO"; */ -"Gbx-kl-uOO.text" = "Predeterminado"; - -/* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Administrar ventanas emergentes de cookies"; - -/* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ -"gS2-mg-l7R.title" = "Tema"; - -/* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ -"HLr-R8-xxF.text" = "Vistas previas con pulsación larga"; - -/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ -"hoT-Nu-KXP.text" = "Navega de forma privada con nuestra aplicación para Windows"; - /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Protección de privacidad habilitada para todos los sitios"; /* Class = "UICollectionViewController"; title = "Icon"; ObjectID = "jbD-Oy-Cmw"; */ "jbD-Oy-Cmw.title" = "Icono"; -/* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ -"m23-t6-9cb.text" = "Compartir opiniones"; - -/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ -"mLn-1x-Fl5.title" = "Posición de la barra de direcciones"; - -/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ -"mqV-pf-NZ1.text" = "Posición de la barra de direcciones"; - /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Deja que DuckDuckGo gestione las ventanas emergentes de consentimiento de cookies"; @@ -181,36 +76,9 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Sitios no protegidos"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "Acerca de DuckDuckGo"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Arriba"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "Más sobre DuckDuckGo"; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Sincronización y copia de seguridad"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Navega de forma privada con nuestra aplicación para Mac "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Etiqueta"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "Aplicación DuckDuckGo para Windows"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Tamaño del texto"; -/* Class = "UILabel"; text = "Private Voice Search"; ObjectID = "Swa-O7-n8W"; */ -"Swa-O7-n8W.text" = "Búsqueda privada por voz"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Mostrar teclado en"; @@ -220,42 +88,18 @@ /* Class = "UITableViewSection"; headerTitle = "Action"; ObjectID = "U2M-6p-6nl"; */ "U2M-6p-6nl.headerTitle" = "Acción"; -/* Class = "UILabel"; text = "Autocomplete Suggestions"; ObjectID = "U8i-cQ-5WW"; */ -"U8i-cQ-5WW.text" = "Sugerencias de autocompletado"; - -/* Class = "UINavigationItem"; title = "Fire Button Animation"; ObjectID = "uns-8w-IwL"; Note = "Fire button animation setting page title"; */ -"uns-8w-IwL.title" = "Animación del botón Fuego"; - /* Class = "UILabel"; text = "Remove All Fireproof Sites"; ObjectID = "UZx-52-aer"; */ "UZx-52-aer.text" = "Eliminar todos los sitios web a prueba de fuego"; -/* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "vPz-uO-6gB"; */ -"vPz-uO-6gB.text" = "Control Global de Privacidad (GPC)"; - /* Class = "UITableViewSection"; footerTitle = "App exit is defined by swiping the app to close it while inactivity is when the app is in the background."; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.footerTitle" = "La salida de la aplicación consiste en deslizar para cerrar la aplicación mientras que la inactividad es cuando la aplicación está en segundo plano."; /* Class = "UITableViewSection"; headerTitle = "Desired timing"; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.headerTitle" = "Tiempo deseado"; -/* Class = "UILabel"; text = "Set as Default Browser"; ObjectID = "xof-5k-PkI"; */ -"xof-5k-PkI.text" = "Definir como navegador predeterminado"; - /* Class = "UINavigationItem"; title = "Fireproof Sites"; ObjectID = "xUX-nF-HOl"; */ "xUX-nF-HOl.title" = "Sitios web a prueba de fuego"; -/* Class = "UILabel"; text = "Block email trackers and hide your address"; ObjectID = "Y6Y-wA-n6Z"; */ -"Y6Y-wA-n6Z.text" = "Bloquea los rastreadores de correo electrónico y oculta tu dirección"; - -/* Class = "UILabel"; text = "Keyboard"; ObjectID = "yoZ-jw-Cu3"; */ -"yoZ-jw-Cu3.text" = "Teclado"; - -/* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ -"yvj-LL-MiR.text" = "Añadir una aplicación a tu Dock"; - -/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "Aplicación DuckDuckGo para Mac"; - /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Nueva pestaña"; diff --git a/DuckDuckGo/et.lproj/Localizable.strings b/DuckDuckGo/et.lproj/Localizable.strings index 7f929ad35b..d0c8c88798 100644 --- a/DuckDuckGo/et.lproj/Localizable.strings +++ b/DuckDuckGo/et.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Klaviatuur"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Paroolid"; + /* Settings title for the 'More' section */ "settings.more" = "Veel DuckDuckGo'lt"; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Pika vajutusega eelvaated"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Milline veebisait on katki?"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Taastamiskood kopeeriti lõikelauale"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Kustuta serveri andmed"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "Kõigi seadmete ühendus katkestatakse ja sinu sünkroonitud andmed kustutatakse serverist."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "Kas kustutada serveri andmed?"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Eemaldage"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "%@ ei pääse enam sinu sünkroonitud andmetele juurde."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "Kas seade eemaldada?"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Eemaldage"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "See seade ei pääse enam sinu sünkroonitud andmetele ligi."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "Kas lülitada sünkroonimine välja?"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "Kahjuks pole sünkroonimine ja varundamine praegu saadaval. Proovi hiljem uuesti."; diff --git a/DuckDuckGo/et.lproj/Settings.strings b/DuckDuckGo/et.lproj/Settings.strings index d8050162ee..1aaa03a0a4 100644 --- a/DuckDuckGo/et.lproj/Settings.strings +++ b/DuckDuckGo/et.lproj/Settings.strings @@ -1,15 +1,3 @@ -/* Class = "UILabel"; text = "Fireproof Sites"; ObjectID = "0DQ-yq-UuT"; */ -"0DQ-yq-UuT.text" = "Tulekindlad veebisaidid"; - -/* Class = "UITableViewSection"; footerTitle = "Disable to prevent links from automatically opening in other installed apps."; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.footerTitle" = "Keela, et takistada linkide automaatset avanemist teistes installitud rakendustes."; - -/* Class = "UITableViewSection"; headerTitle = "Customize"; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.headerTitle" = "Kohanda"; - -/* Class = "UILabel"; text = "Version"; ObjectID = "2ky-s9-1aZ"; */ -"2ky-s9-1aZ.text" = "Versioon"; - /* Class = "UINavigationItem"; title = "Keyboard"; ObjectID = "2pp-PM-6rW"; */ "2pp-PM-6rW.title" = "Klaviatuur"; @@ -31,30 +19,9 @@ /* Class = "UILabel"; text = "Clear Tabs and Data"; ObjectID = "9fc-9r-4aA"; */ "9fc-9r-4aA.text" = "Kustuta vahekaardid ja andmed"; -/* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ -"9Ko-0g-T3h.text" = "Teksti suurus"; - -/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ -"9kt-6R-XiZ.text" = "Pealkiri"; - /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Rakenduse käivitamine"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Ava lingid seotud rakendustes"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Silumismenüü"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Paroolid"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Animatsioon"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "E-posti kaitse"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "Andmed ja/või vahekaardid kustutatakse rakenduse taaskäivitamisel."; @@ -64,114 +31,42 @@ /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ "btj-ri-kRr.title" = "Halda küpsiste hüpikaknaid"; -/* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ -"cKo-er-HNj.text" = "Rakenduse ikoon"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "CR5-Al-WIW"; */ -"CR5-Al-WIW.text" = "Label"; - /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Üleilmne privaatsuskontroll (Global Privacy Control (GPC))"; /* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ "CxT-QK-iVn.title" = "Lisa"; -/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ -"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; - /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Rakendusest väljumisel, 15-minutilise passiivsuse järel"; -/* Class = "UILabel"; text = "10.0.1 (Build 10005)"; ObjectID = "d5n-vG-8kF"; */ -"d5n-vG-8kF.text" = "10.0.1 (Versioon 10005)"; - -/* Class = "UILabel"; text = "Application Lock"; ObjectID = "dBZ-yq-FYj"; */ -"dBZ-yq-FYj.text" = "Rakenduse lukk"; - -/* Class = "UITableViewSection"; headerTitle = "Appearance"; ObjectID = "dj9-vh-Rig"; */ -"dj9-vh-Rig.headerTitle" = "Välimus"; - -/* Class = "UITableViewSection"; footerTitle = "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening."; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.footerTitle" = "Kui on määratud Touch ID, Face ID või süsteemi pääsukood, palutakse avamisel rakendus avada."; - -/* Class = "UITableViewSection"; headerTitle = "Privacy"; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.headerTitle" = "Privaatsus"; - /* Class = "UILabel"; text = "Label"; ObjectID = "dud-qo-Ces"; */ "dud-qo-Ces.text" = "Silt"; -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Dyd-bm-goj"; */ -"Dyd-bm-goj.title" = "Seaded"; - -/* Class = "UILabel"; text = "100%"; ObjectID = "EB8-09-gt2"; */ -"EB8-09-gt2.text" = "100%"; - /* Class = "UILabel"; text = "Label"; ObjectID = "EIq-Ev-nfj"; */ "EIq-Ev-nfj.text" = "Label"; -/* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "ekF-SJ-PAQ"; */ -"ekF-SJ-PAQ.text" = "Kustuta andmed automaatselt"; - /* Class = "UILabel"; text = "App Exit, Inactive for 5 Minutes"; ObjectID = "ElX-yE-4PX"; */ "ElX-yE-4PX.text" = "Rakendusest väljumisel, 5-minutilise passiivsuse järel"; /* Class = "UINavigationItem"; title = "App Icon"; ObjectID = "eMH-uv-Kms"; */ "eMH-uv-Kms.title" = "Rakenduse ikoon"; -/* Class = "UILabel"; text = "Theme"; ObjectID = "f1O-6u-LFY"; */ -"f1O-6u-LFY.text" = "Teema"; - /* Class = "UILabel"; text = "App Exit Only"; ObjectID = "Fal-1Y-o2S"; */ "Fal-1Y-o2S.text" = "Ainult rakendusest väljumisel"; /* Class = "UITableViewController"; title = "Automatically Clear Data"; ObjectID = "fdJ-b1-Des"; */ "fdJ-b1-Des.title" = "Kustuta andmed automaatselt"; -/* Class = "UILabel"; text = "Unprotected Sites"; ObjectID = "FHC-1z-Z3v"; */ -"FHC-1z-Z3v.text" = "Kaitseta saidid"; - -/* Class = "UITableViewSection"; headerTitle = "About"; ObjectID = "FpT-1C-xtx"; */ -"FpT-1C-xtx.headerTitle" = "Teave"; - /* Class = "UITableViewController"; title = "Global Privacy Control (GPC)"; ObjectID = "fV3-86-QQj"; */ "fV3-86-QQj.title" = "Üleilmne privaatsuskontroll (Global Privacy Control (GPC))"; -/* Class = "UILabel"; text = "Add Widget to Home Screen"; ObjectID = "Fxu-zn-51Z"; */ -"Fxu-zn-51Z.text" = "Lisa vidin avakuvale"; - -/* Class = "UILabel"; text = "Fire Button Animation"; ObjectID = "gBo-Cu-e2k"; Note = "Fire button animation settings item"; */ -"gBo-Cu-e2k.text" = "Tulenupu animatsioon"; - -/* Class = "UILabel"; text = "Default"; ObjectID = "Gbx-kl-uOO"; */ -"Gbx-kl-uOO.text" = "Vaikimisi"; - -/* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Halda küpsiste hüpikaknaid"; - -/* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ -"gS2-mg-l7R.title" = "Teema"; - -/* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ -"HLr-R8-xxF.text" = "Pika vajutusega eelvaated"; - -/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ -"hoT-Nu-KXP.text" = "Sirvi privaatselt meie Windowsi rakendusega"; - /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Privaatsuse kaitse on lubatud kõigil saitidel"; /* Class = "UICollectionViewController"; title = "Icon"; ObjectID = "jbD-Oy-Cmw"; */ "jbD-Oy-Cmw.title" = "Ikoon"; -/* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ -"m23-t6-9cb.text" = "Jaga tagasisidet"; - -/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ -"mLn-1x-Fl5.title" = "Aadressiriba asukoht"; - -/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ -"mqV-pf-NZ1.text" = "Aadressiriba asukoht"; - /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Luba DuckDuckGo'l hallata küpsiste nõusoleku hüpikaknaid"; @@ -181,36 +76,9 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Kaitseta saidid"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "DuckDuckGo'st"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Populaarseimad"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "Veel DuckDuckGo'lt"; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Sünkroonimine ja varundamine"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Sirvi privaatselt meie Maci rakendusega "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Silt"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "DuckDuckGo Windowsi rakendus"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Teksti suurus"; -/* Class = "UILabel"; text = "Private Voice Search"; ObjectID = "Swa-O7-n8W"; */ -"Swa-O7-n8W.text" = "Privaatne häälotsing"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Näita klaviatuuri"; @@ -220,42 +88,18 @@ /* Class = "UITableViewSection"; headerTitle = "Action"; ObjectID = "U2M-6p-6nl"; */ "U2M-6p-6nl.headerTitle" = "Tegevus"; -/* Class = "UILabel"; text = "Autocomplete Suggestions"; ObjectID = "U8i-cQ-5WW"; */ -"U8i-cQ-5WW.text" = "Automaatselt täidetavad ettepanekud"; - -/* Class = "UINavigationItem"; title = "Fire Button Animation"; ObjectID = "uns-8w-IwL"; Note = "Fire button animation setting page title"; */ -"uns-8w-IwL.title" = "Tulenupu animatsioon"; - /* Class = "UILabel"; text = "Remove All Fireproof Sites"; ObjectID = "UZx-52-aer"; */ "UZx-52-aer.text" = "Eemalda kõik tulekindlad veebisaidid"; -/* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "vPz-uO-6gB"; */ -"vPz-uO-6gB.text" = "Üleilmne privaatsuskontroll (Global Privacy Control (GPC))"; - /* Class = "UITableViewSection"; footerTitle = "App exit is defined by swiping the app to close it while inactivity is when the app is in the background."; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.footerTitle" = "Rakendusest väljumise määratlemiseks pühi rakendust selle sulgemiseks tegevusetuse ajal, kui rakendus on taustal."; /* Class = "UITableViewSection"; headerTitle = "Desired timing"; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.headerTitle" = "Soovitud ajastus"; -/* Class = "UILabel"; text = "Set as Default Browser"; ObjectID = "xof-5k-PkI"; */ -"xof-5k-PkI.text" = "Määrake vaikebrauseriks"; - /* Class = "UINavigationItem"; title = "Fireproof Sites"; ObjectID = "xUX-nF-HOl"; */ "xUX-nF-HOl.title" = "Tulekindlad veebisaidid"; -/* Class = "UILabel"; text = "Block email trackers and hide your address"; ObjectID = "Y6Y-wA-n6Z"; */ -"Y6Y-wA-n6Z.text" = "Blokeeri meilijälgurid ja peida oma aadress"; - -/* Class = "UILabel"; text = "Keyboard"; ObjectID = "yoZ-jw-Cu3"; */ -"yoZ-jw-Cu3.text" = "Klaviatuur"; - -/* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ -"yvj-LL-MiR.text" = "Lisa rakendus oma dokki"; - -/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "DuckDuckGo Maci rakendus"; - /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Uus vaheleht"; diff --git a/DuckDuckGo/fi.lproj/Localizable.strings b/DuckDuckGo/fi.lproj/Localizable.strings index 27e65fe237..d6410ae06a 100644 --- a/DuckDuckGo/fi.lproj/Localizable.strings +++ b/DuckDuckGo/fi.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Näppäimistö"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Salasanat"; + /* Settings title for the 'More' section */ "settings.more" = "Lisää DuckDuckGolta"; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Esikatselu pitkällä painalluksella"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Mikä verkkosivusto on viallinen?"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Palautuskoodi kopioitu leikepöydälle"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Poista palvelimen tiedot"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "Kaikkien laitteiden yhteys katkaistaan, ja synkronoidut tietosi poistetaan palvelimelta."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "Poista palvelimen tiedot?"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Poista"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "\"%@\" ei enää pääse käsiksi synkronoituihin tietoihisi."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "Poistetaanko laite?"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Poista"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "Tämä laite ei enää pääse käsiksi synkronoituihin tietoihisi."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "Poistetaanko synkronointi käytöstä?"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "Sync & Backup ei valitettavasti ole tällä hetkellä käytettävissä. Yritä myöhemmin uudelleen."; diff --git a/DuckDuckGo/fi.lproj/Settings.strings b/DuckDuckGo/fi.lproj/Settings.strings index 914b61e5bb..57c52799c3 100644 --- a/DuckDuckGo/fi.lproj/Settings.strings +++ b/DuckDuckGo/fi.lproj/Settings.strings @@ -1,15 +1,3 @@ -/* Class = "UILabel"; text = "Fireproof Sites"; ObjectID = "0DQ-yq-UuT"; */ -"0DQ-yq-UuT.text" = "Tee sivustoista palonkestäviä"; - -/* Class = "UITableViewSection"; footerTitle = "Disable to prevent links from automatically opening in other installed apps."; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.footerTitle" = "Ota pois käytöstä estääksesi linkkejä avautumasta automaattisesti muissa asennetuissa sovelluksissa."; - -/* Class = "UITableViewSection"; headerTitle = "Customize"; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.headerTitle" = "Mukauta"; - -/* Class = "UILabel"; text = "Version"; ObjectID = "2ky-s9-1aZ"; */ -"2ky-s9-1aZ.text" = "Versio"; - /* Class = "UINavigationItem"; title = "Keyboard"; ObjectID = "2pp-PM-6rW"; */ "2pp-PM-6rW.title" = "Näppäimistö"; @@ -31,30 +19,9 @@ /* Class = "UILabel"; text = "Clear Tabs and Data"; ObjectID = "9fc-9r-4aA"; */ "9fc-9r-4aA.text" = "Tyhjennä välilehdet ja tiedot"; -/* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ -"9Ko-0g-T3h.text" = "Tekstin koko"; - -/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ -"9kt-6R-XiZ.text" = "Otsikko"; - /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Sovelluksen käynnistys"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Avaa linkit liittyvissä sovelluksissa"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Virheenkorjausvalikko"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Salasanat"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Animaatio"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "Sähköpostisuojaus"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "Tiedot ja/tai välilehdet tyhjennetään, kun sovellus käynnistetään uudelleen."; @@ -64,114 +31,42 @@ /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ "btj-ri-kRr.title" = "Evästeiden hallinnan ponnahdusikkunat"; -/* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ -"cKo-er-HNj.text" = "Sovelluskuvake"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "CR5-Al-WIW"; */ -"CR5-Al-WIW.text" = "Label"; - /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Global Privacy Control (GPC)"; /* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ "CxT-QK-iVn.title" = "Lisää"; -/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ -"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; - /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Sovelluksesta poistuttaessa, 15 minuutin toimettomuuden jälkeen"; -/* Class = "UILabel"; text = "10.0.1 (Build 10005)"; ObjectID = "d5n-vG-8kF"; */ -"d5n-vG-8kF.text" = "10.0.1 (koontiversio 10005)"; - -/* Class = "UILabel"; text = "Application Lock"; ObjectID = "dBZ-yq-FYj"; */ -"dBZ-yq-FYj.text" = "Sovelluksen lukitus"; - -/* Class = "UITableViewSection"; headerTitle = "Appearance"; ObjectID = "dj9-vh-Rig"; */ -"dj9-vh-Rig.headerTitle" = "Ulkoasu"; - -/* Class = "UITableViewSection"; footerTitle = "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening."; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.footerTitle" = "Jos käytössä on Touch ID, Face ID tai järjestelmän salasana, sinua pyydetään poistamaan lukitus, kun avaat sovelluksen."; - -/* Class = "UITableViewSection"; headerTitle = "Privacy"; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.headerTitle" = "Tietosuoja"; - /* Class = "UILabel"; text = "Label"; ObjectID = "dud-qo-Ces"; */ "dud-qo-Ces.text" = "Label"; -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Dyd-bm-goj"; */ -"Dyd-bm-goj.title" = "Asetukset"; - -/* Class = "UILabel"; text = "100%"; ObjectID = "EB8-09-gt2"; */ -"EB8-09-gt2.text" = "100 %"; - /* Class = "UILabel"; text = "Label"; ObjectID = "EIq-Ev-nfj"; */ "EIq-Ev-nfj.text" = "Label"; -/* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "ekF-SJ-PAQ"; */ -"ekF-SJ-PAQ.text" = "Tyhjennä tiedot automaattisesti"; - /* Class = "UILabel"; text = "App Exit, Inactive for 5 Minutes"; ObjectID = "ElX-yE-4PX"; */ "ElX-yE-4PX.text" = "Sovelluksesta poistuttaessa, 5 minuutin toimettomuuden jälkeen"; /* Class = "UINavigationItem"; title = "App Icon"; ObjectID = "eMH-uv-Kms"; */ "eMH-uv-Kms.title" = "Sovelluskuvake"; -/* Class = "UILabel"; text = "Theme"; ObjectID = "f1O-6u-LFY"; */ -"f1O-6u-LFY.text" = "Teema"; - /* Class = "UILabel"; text = "App Exit Only"; ObjectID = "Fal-1Y-o2S"; */ "Fal-1Y-o2S.text" = "Vain sovelluksesta poistuttaessa"; /* Class = "UITableViewController"; title = "Automatically Clear Data"; ObjectID = "fdJ-b1-Des"; */ "fdJ-b1-Des.title" = "Tyhjennä tiedot automaattisesti"; -/* Class = "UILabel"; text = "Unprotected Sites"; ObjectID = "FHC-1z-Z3v"; */ -"FHC-1z-Z3v.text" = "Suojaamattomat sivustot"; - -/* Class = "UITableViewSection"; headerTitle = "About"; ObjectID = "FpT-1C-xtx"; */ -"FpT-1C-xtx.headerTitle" = "Tietoja"; - /* Class = "UITableViewController"; title = "Global Privacy Control (GPC)"; ObjectID = "fV3-86-QQj"; */ "fV3-86-QQj.title" = "Global Privacy Control (GPC)"; -/* Class = "UILabel"; text = "Add Widget to Home Screen"; ObjectID = "Fxu-zn-51Z"; */ -"Fxu-zn-51Z.text" = "Lisää widget aloitusnäyttöön"; - -/* Class = "UILabel"; text = "Fire Button Animation"; ObjectID = "gBo-Cu-e2k"; Note = "Fire button animation settings item"; */ -"gBo-Cu-e2k.text" = "Liekki-painikkeen animaatio"; - -/* Class = "UILabel"; text = "Default"; ObjectID = "Gbx-kl-uOO"; */ -"Gbx-kl-uOO.text" = "Oletus"; - -/* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Evästeiden hallinnan ponnahdusikkunat"; - -/* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ -"gS2-mg-l7R.title" = "Teema"; - -/* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ -"HLr-R8-xxF.text" = "Esikatselu pitkällä painalluksella"; - -/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ -"hoT-Nu-KXP.text" = "Selaa yksityisesti Windows-sovelluksellamme"; - /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Tietosuoja on käytössä kaikilla sivustoilla"; /* Class = "UICollectionViewController"; title = "Icon"; ObjectID = "jbD-Oy-Cmw"; */ "jbD-Oy-Cmw.title" = "Kuvake"; -/* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ -"m23-t6-9cb.text" = "Jaa palaute"; - -/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ -"mLn-1x-Fl5.title" = "Osoitepalkin sijainti"; - -/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ -"mqV-pf-NZ1.text" = "Osoitepalkin sijainti"; - /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Anna DuckDuckGon hallinnoida evästeiden hallinnan ponnahdusikkunoita"; @@ -181,36 +76,9 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Suojaamattomat sivustot"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "Tietoa DuckDuckGo:sta"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Suosituin"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "Lisää DuckDuckGolta"; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Synkronoi ja varmuuskopioi"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Selaa yksityisesti Mac-sovelluksellamme "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Label"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "DuckDuckGo-sovellus Windowsille"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Tekstin koko"; -/* Class = "UILabel"; text = "Private Voice Search"; ObjectID = "Swa-O7-n8W"; */ -"Swa-O7-n8W.text" = "Yksityinen äänihaku"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Näytä näppäimistö kun"; @@ -220,42 +88,18 @@ /* Class = "UITableViewSection"; headerTitle = "Action"; ObjectID = "U2M-6p-6nl"; */ "U2M-6p-6nl.headerTitle" = "Toiminto"; -/* Class = "UILabel"; text = "Autocomplete Suggestions"; ObjectID = "U8i-cQ-5WW"; */ -"U8i-cQ-5WW.text" = "Näytä automaattisen täydennyksen ehdotukset"; - -/* Class = "UINavigationItem"; title = "Fire Button Animation"; ObjectID = "uns-8w-IwL"; Note = "Fire button animation setting page title"; */ -"uns-8w-IwL.title" = "Liekki-painikkeen animaatio"; - /* Class = "UILabel"; text = "Remove All Fireproof Sites"; ObjectID = "UZx-52-aer"; */ "UZx-52-aer.text" = "Poista kaikki palonkestävät sivustot"; -/* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "vPz-uO-6gB"; */ -"vPz-uO-6gB.text" = "Global Privacy Control (GPC)"; - /* Class = "UITableViewSection"; footerTitle = "App exit is defined by swiping the app to close it while inactivity is when the app is in the background."; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.footerTitle" = "Sovelluksesta poistuminen tarkoittaa, että sovellus suljetaan pyyhkäisemällä, kun taas toimettomuus tarkoittaa, että sovellus on käynnissä taustalla."; /* Class = "UITableViewSection"; headerTitle = "Desired timing"; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.headerTitle" = "Haluttu aika"; -/* Class = "UILabel"; text = "Set as Default Browser"; ObjectID = "xof-5k-PkI"; */ -"xof-5k-PkI.text" = "Aseta oletusselaimeksi"; - /* Class = "UINavigationItem"; title = "Fireproof Sites"; ObjectID = "xUX-nF-HOl"; */ "xUX-nF-HOl.title" = "Tee sivustoista palonkestäviä"; -/* Class = "UILabel"; text = "Block email trackers and hide your address"; ObjectID = "Y6Y-wA-n6Z"; */ -"Y6Y-wA-n6Z.text" = "Estä sähköpostiseuraajat ja piilota osoitteesi"; - -/* Class = "UILabel"; text = "Keyboard"; ObjectID = "yoZ-jw-Cu3"; */ -"yoZ-jw-Cu3.text" = "Näppäimistö"; - -/* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ -"yvj-LL-MiR.text" = "Lisää sovellus telakkaasi"; - -/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "DuckDuckGo-sovellus Macille"; - /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Uusi välilehti"; diff --git a/DuckDuckGo/fr.lproj/Localizable.strings b/DuckDuckGo/fr.lproj/Localizable.strings index 59034f6ae2..75303ca4ee 100644 --- a/DuckDuckGo/fr.lproj/Localizable.strings +++ b/DuckDuckGo/fr.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Clavier"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Mots de passe"; + /* Settings title for the 'More' section */ "settings.more" = "Plus de la part de DuckDuckGo"; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Aperçus obtenus en appuyant longuement"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Quel site Web pose problème ?"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Code de récupération copié dans le presse-papiers"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Supprimer les données du serveur"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "Tous les appareils seront déconnectés et vos données synchronisées seront supprimées du serveur."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "Supprimer les données du serveur ?"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Supprimer"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "« %@ » ne pourra plus accéder à vos données synchronisées."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "Supprimer l'appareil ?"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Supprimer"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "Cet appareil ne pourra plus accéder à vos données synchronisées."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "Désactiver la synchronisation ?"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "Désolés, mais la synchronisation et la sauvegarde sont actuellement indisponibles. Veuillez réessayer ultérieurement."; diff --git a/DuckDuckGo/fr.lproj/Settings.strings b/DuckDuckGo/fr.lproj/Settings.strings index a2d18d10b5..d31e63f098 100644 --- a/DuckDuckGo/fr.lproj/Settings.strings +++ b/DuckDuckGo/fr.lproj/Settings.strings @@ -1,15 +1,3 @@ -/* Class = "UILabel"; text = "Fireproof Sites"; ObjectID = "0DQ-yq-UuT"; */ -"0DQ-yq-UuT.text" = "Sites coupe-feu"; - -/* Class = "UITableViewSection"; footerTitle = "Disable to prevent links from automatically opening in other installed apps."; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.footerTitle" = "Désactiver pour empêcher l'ouverture automatique des liens dans d'autres applications installées."; - -/* Class = "UITableViewSection"; headerTitle = "Customize"; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.headerTitle" = "Personnaliser"; - -/* Class = "UILabel"; text = "Version"; ObjectID = "2ky-s9-1aZ"; */ -"2ky-s9-1aZ.text" = "Version"; - /* Class = "UINavigationItem"; title = "Keyboard"; ObjectID = "2pp-PM-6rW"; */ "2pp-PM-6rW.title" = "Clavier"; @@ -31,30 +19,9 @@ /* Class = "UILabel"; text = "Clear Tabs and Data"; ObjectID = "9fc-9r-4aA"; */ "9fc-9r-4aA.text" = "Effacer les onglets et données"; -/* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ -"9Ko-0g-T3h.text" = "Taille du texte"; - -/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ -"9kt-6R-XiZ.text" = "Titre"; - /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Au lancement de l'application"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Ouvrir les liens dans les applications associées"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Menu de débogage"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Mots de passe"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Animation"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "Protection des e-mails"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "Les données et/ou les onglets seront effacés lorsque l'application redémarrera."; @@ -64,114 +31,42 @@ /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ "btj-ri-kRr.title" = "Gérer les fenêtres contextuelles (cookies)"; -/* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ -"cKo-er-HNj.text" = "Icône de l'appli"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "CR5-Al-WIW"; */ -"CR5-Al-WIW.text" = "Label"; - /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Global Privacy Control (GPC)"; /* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ "CxT-QK-iVn.title" = "Ajouter"; -/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ -"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; - /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Quitter l'application, inactivité pendant 15 minutes"; -/* Class = "UILabel"; text = "10.0.1 (Build 10005)"; ObjectID = "d5n-vG-8kF"; */ -"d5n-vG-8kF.text" = "10.0.1 (Version 10005)"; - -/* Class = "UILabel"; text = "Application Lock"; ObjectID = "dBZ-yq-FYj"; */ -"dBZ-yq-FYj.text" = "Verrouillage de l'application"; - -/* Class = "UITableViewSection"; headerTitle = "Appearance"; ObjectID = "dj9-vh-Rig"; */ -"dj9-vh-Rig.headerTitle" = "Apparence"; - -/* Class = "UITableViewSection"; footerTitle = "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening."; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.footerTitle" = "Si Touch ID, Face ID ou un code d'accès au système est mis en place, il vous sera demandé de déverrouiller l'application lors de l'ouverture."; - -/* Class = "UITableViewSection"; headerTitle = "Privacy"; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.headerTitle" = "Confidentialité"; - /* Class = "UILabel"; text = "Label"; ObjectID = "dud-qo-Ces"; */ "dud-qo-Ces.text" = "Label"; -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Dyd-bm-goj"; */ -"Dyd-bm-goj.title" = "Paramètres"; - -/* Class = "UILabel"; text = "100%"; ObjectID = "EB8-09-gt2"; */ -"EB8-09-gt2.text" = "100 %"; - /* Class = "UILabel"; text = "Label"; ObjectID = "EIq-Ev-nfj"; */ "EIq-Ev-nfj.text" = "Label"; -/* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "ekF-SJ-PAQ"; */ -"ekF-SJ-PAQ.text" = "Effacer automatiquement les données"; - /* Class = "UILabel"; text = "App Exit, Inactive for 5 Minutes"; ObjectID = "ElX-yE-4PX"; */ "ElX-yE-4PX.text" = "Quitter l'application, inactivité pendant 5 minutes"; /* Class = "UINavigationItem"; title = "App Icon"; ObjectID = "eMH-uv-Kms"; */ "eMH-uv-Kms.title" = "Icône de l'appli"; -/* Class = "UILabel"; text = "Theme"; ObjectID = "f1O-6u-LFY"; */ -"f1O-6u-LFY.text" = "Thème"; - /* Class = "UILabel"; text = "App Exit Only"; ObjectID = "Fal-1Y-o2S"; */ "Fal-1Y-o2S.text" = "Quitter l'application uniquement"; /* Class = "UITableViewController"; title = "Automatically Clear Data"; ObjectID = "fdJ-b1-Des"; */ "fdJ-b1-Des.title" = "Effacer automatiquement les données"; -/* Class = "UILabel"; text = "Unprotected Sites"; ObjectID = "FHC-1z-Z3v"; */ -"FHC-1z-Z3v.text" = "Sites non protégés"; - -/* Class = "UITableViewSection"; headerTitle = "About"; ObjectID = "FpT-1C-xtx"; */ -"FpT-1C-xtx.headerTitle" = "À propos"; - /* Class = "UITableViewController"; title = "Global Privacy Control (GPC)"; ObjectID = "fV3-86-QQj"; */ "fV3-86-QQj.title" = "Global Privacy Control (GPC)"; -/* Class = "UILabel"; text = "Add Widget to Home Screen"; ObjectID = "Fxu-zn-51Z"; */ -"Fxu-zn-51Z.text" = "Ajouter le widget à l'écran d'accueil"; - -/* Class = "UILabel"; text = "Fire Button Animation"; ObjectID = "gBo-Cu-e2k"; Note = "Fire button animation settings item"; */ -"gBo-Cu-e2k.text" = "Animation du bouton en forme de flamme"; - -/* Class = "UILabel"; text = "Default"; ObjectID = "Gbx-kl-uOO"; */ -"Gbx-kl-uOO.text" = "Défaut"; - -/* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Gérer les fenêtres contextuelles (cookies)"; - -/* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ -"gS2-mg-l7R.title" = "Thème"; - -/* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ -"HLr-R8-xxF.text" = "Aperçus obtenus en appuyant longuement"; - -/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ -"hoT-Nu-KXP.text" = "Naviguez incognito avec notre application pour Windows"; - /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "La protection de la confidentialité est activée pour tous les sites"; /* Class = "UICollectionViewController"; title = "Icon"; ObjectID = "jbD-Oy-Cmw"; */ "jbD-Oy-Cmw.title" = "Icône"; -/* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ -"m23-t6-9cb.text" = "Partagez vos commentaires"; - -/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ -"mLn-1x-Fl5.title" = "Position de la barre d'adresse"; - -/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ -"mqV-pf-NZ1.text" = "Position de la barre d'adresse"; - /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Autoriser DuckDuckGo à gérer les fenêtres contextuelles de consentement aux cookies"; @@ -181,36 +76,9 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Sites non protégés"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "À propos de DuckDuckGo"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Haut de page"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "Plus de la part de DuckDuckGo"; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Synchronisation et sauvegarde"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Naviguez incognito avec notre application pour Mac "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Label"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "Application DuckDuckGo pour Windows"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Taille du texte"; -/* Class = "UILabel"; text = "Private Voice Search"; ObjectID = "Swa-O7-n8W"; */ -"Swa-O7-n8W.text" = "Recherche vocale privée"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Afficher le clavier"; @@ -220,42 +88,18 @@ /* Class = "UITableViewSection"; headerTitle = "Action"; ObjectID = "U2M-6p-6nl"; */ "U2M-6p-6nl.headerTitle" = "Action"; -/* Class = "UILabel"; text = "Autocomplete Suggestions"; ObjectID = "U8i-cQ-5WW"; */ -"U8i-cQ-5WW.text" = "Afficher les suggestions de saisie semi-automatique"; - -/* Class = "UINavigationItem"; title = "Fire Button Animation"; ObjectID = "uns-8w-IwL"; Note = "Fire button animation setting page title"; */ -"uns-8w-IwL.title" = "Animation du bouton en forme de flamme"; - /* Class = "UILabel"; text = "Remove All Fireproof Sites"; ObjectID = "UZx-52-aer"; */ "UZx-52-aer.text" = "Supprimer tous les sites coupe-feu"; -/* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "vPz-uO-6gB"; */ -"vPz-uO-6gB.text" = "Global Privacy Control (GPC)"; - /* Class = "UITableViewSection"; footerTitle = "App exit is defined by swiping the app to close it while inactivity is when the app is in the background."; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.footerTitle" = "L'application est fermée lorsque vous la faites glisser vers le haut. En cas d'inactivité, l'application reste ouverte en arrière-plan."; /* Class = "UITableViewSection"; headerTitle = "Desired timing"; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.headerTitle" = "Délai souhaité"; -/* Class = "UILabel"; text = "Set as Default Browser"; ObjectID = "xof-5k-PkI"; */ -"xof-5k-PkI.text" = "Définir comme navigateur par défaut"; - /* Class = "UINavigationItem"; title = "Fireproof Sites"; ObjectID = "xUX-nF-HOl"; */ "xUX-nF-HOl.title" = "Sites coupe-feu"; -/* Class = "UILabel"; text = "Block email trackers and hide your address"; ObjectID = "Y6Y-wA-n6Z"; */ -"Y6Y-wA-n6Z.text" = "Bloquez les traqueurs d'e-mails et masquez votre adresse"; - -/* Class = "UILabel"; text = "Keyboard"; ObjectID = "yoZ-jw-Cu3"; */ -"yoZ-jw-Cu3.text" = "Clavier"; - -/* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ -"yvj-LL-MiR.text" = "Ajouter l'application à votre Dock"; - -/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "Application DuckDuckGo pour Mac"; - /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Nouvel onglet"; diff --git a/DuckDuckGo/hr.lproj/Localizable.strings b/DuckDuckGo/hr.lproj/Localizable.strings index 7b3f1fb95a..c85e257952 100644 --- a/DuckDuckGo/hr.lproj/Localizable.strings +++ b/DuckDuckGo/hr.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Tipkovnica"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Lozinke"; + /* Settings title for the 'More' section */ "settings.more" = "Više od DuckDuckGoa"; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Pretpregledi na dugi pritisak"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Koje je web-mjesto neispravno?"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Šifra za oporavak kopirana je u međuspremnik"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Brisanje podataka poslužitelja"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "Svi uređaji bit će isključeni i tvoji sinkronizirani podaci bit će izbrisani s poslužitelja."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "Izbrisati podatke poslužitelja?"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Ukloni"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "\"%@\" više neće moći pristupiti tvojim sinkroniziranim podacima."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "Želiš li ukloniti uređaj?"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Ukloni"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "Ovaj uređaj više neće moći pristupiti tvojim sinkroniziranim podacima."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "Želiš li isključiti sinkronizaciju?"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "Nažalost, Sync & Backup (sinkronizacija i sigurnosno kopiranje) trenutno nisu dostupni. Pokušaj ponovno nešto kasnije."; diff --git a/DuckDuckGo/hr.lproj/Settings.strings b/DuckDuckGo/hr.lproj/Settings.strings index e3576946b9..59fb1f1d7c 100644 --- a/DuckDuckGo/hr.lproj/Settings.strings +++ b/DuckDuckGo/hr.lproj/Settings.strings @@ -1,15 +1,3 @@ -/* Class = "UILabel"; text = "Fireproof Sites"; ObjectID = "0DQ-yq-UuT"; */ -"0DQ-yq-UuT.text" = "Zaštićena web-mjesta"; - -/* Class = "UITableViewSection"; footerTitle = "Disable to prevent links from automatically opening in other installed apps."; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.footerTitle" = "Onemogući da spriječiš automatsko otvaranje poveznica u drugim instaliranim aplikacijama."; - -/* Class = "UITableViewSection"; headerTitle = "Customize"; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.headerTitle" = "Prilagodba"; - -/* Class = "UILabel"; text = "Version"; ObjectID = "2ky-s9-1aZ"; */ -"2ky-s9-1aZ.text" = "Verzija"; - /* Class = "UINavigationItem"; title = "Keyboard"; ObjectID = "2pp-PM-6rW"; */ "2pp-PM-6rW.title" = "Tipkovnica"; @@ -31,30 +19,9 @@ /* Class = "UILabel"; text = "Clear Tabs and Data"; ObjectID = "9fc-9r-4aA"; */ "9fc-9r-4aA.text" = "Obriši kartice i podatke"; -/* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ -"9Ko-0g-T3h.text" = "Veličina teksta"; - -/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ -"9kt-6R-XiZ.text" = "Naslov"; - /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Pokretanje aplikacije"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Otvori veze u povezanim aplikacijama"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Izbornik za ispravljanje pogrešaka"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Lozinke"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Animacija"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "Zaštita e-pošte"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "Podaci i/ili kartice bit će uklonjeni nakon ponovnog pokretanja aplikacije."; @@ -64,114 +31,42 @@ /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ "btj-ri-kRr.title" = "Upravljanje skočnim prozorima kolačića"; -/* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ -"cKo-er-HNj.text" = "Ikona aplikacije"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "CR5-Al-WIW"; */ -"CR5-Al-WIW.text" = "Label"; - /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Globalna kontrola privatnosti (GPC)"; /* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ "CxT-QK-iVn.title" = "Dodaj"; -/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ -"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; - /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Izlaz iz aplikacije, neaktivna 15 minuta"; -/* Class = "UILabel"; text = "10.0.1 (Build 10005)"; ObjectID = "d5n-vG-8kF"; */ -"d5n-vG-8kF.text" = "10.0.1 (Verzija 10005)"; - -/* Class = "UILabel"; text = "Application Lock"; ObjectID = "dBZ-yq-FYj"; */ -"dBZ-yq-FYj.text" = "Zaključavanje aplikacije"; - -/* Class = "UITableViewSection"; headerTitle = "Appearance"; ObjectID = "dj9-vh-Rig"; */ -"dj9-vh-Rig.headerTitle" = "Izgled"; - -/* Class = "UITableViewSection"; footerTitle = "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening."; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.footerTitle" = "Ako su postavljeni Touch ID, Face ID ili pristupni kôd sustava, od tebe će se tražiti da otključaš aplikaciju prilikom otvaranja."; - -/* Class = "UITableViewSection"; headerTitle = "Privacy"; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.headerTitle" = "Zaštita privatnosti"; - /* Class = "UILabel"; text = "Label"; ObjectID = "dud-qo-Ces"; */ "dud-qo-Ces.text" = "Label"; -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Dyd-bm-goj"; */ -"Dyd-bm-goj.title" = "Postavke"; - -/* Class = "UILabel"; text = "100%"; ObjectID = "EB8-09-gt2"; */ -"EB8-09-gt2.text" = "100 %"; - /* Class = "UILabel"; text = "Label"; ObjectID = "EIq-Ev-nfj"; */ "EIq-Ev-nfj.text" = "Label"; -/* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "ekF-SJ-PAQ"; */ -"ekF-SJ-PAQ.text" = "Automatsko brisanje podataka"; - /* Class = "UILabel"; text = "App Exit, Inactive for 5 Minutes"; ObjectID = "ElX-yE-4PX"; */ "ElX-yE-4PX.text" = "Izlaz iz aplikacije, neaktivna 5 minuta"; /* Class = "UINavigationItem"; title = "App Icon"; ObjectID = "eMH-uv-Kms"; */ "eMH-uv-Kms.title" = "Ikona aplikacije"; -/* Class = "UILabel"; text = "Theme"; ObjectID = "f1O-6u-LFY"; */ -"f1O-6u-LFY.text" = "Tema"; - /* Class = "UILabel"; text = "App Exit Only"; ObjectID = "Fal-1Y-o2S"; */ "Fal-1Y-o2S.text" = "Samo izlaz iz aplikacije"; /* Class = "UITableViewController"; title = "Automatically Clear Data"; ObjectID = "fdJ-b1-Des"; */ "fdJ-b1-Des.title" = "Automatsko brisanje podataka"; -/* Class = "UILabel"; text = "Unprotected Sites"; ObjectID = "FHC-1z-Z3v"; */ -"FHC-1z-Z3v.text" = "Nezaštićena web-mjesta"; - -/* Class = "UITableViewSection"; headerTitle = "About"; ObjectID = "FpT-1C-xtx"; */ -"FpT-1C-xtx.headerTitle" = "O nama"; - /* Class = "UITableViewController"; title = "Global Privacy Control (GPC)"; ObjectID = "fV3-86-QQj"; */ "fV3-86-QQj.title" = "Globalna kontrola privatnosti (GPC)"; -/* Class = "UILabel"; text = "Add Widget to Home Screen"; ObjectID = "Fxu-zn-51Z"; */ -"Fxu-zn-51Z.text" = "Dodajte widget na početni zaslon"; - -/* Class = "UILabel"; text = "Fire Button Animation"; ObjectID = "gBo-Cu-e2k"; Note = "Fire button animation settings item"; */ -"gBo-Cu-e2k.text" = "Animacija gumba Vatre"; - -/* Class = "UILabel"; text = "Default"; ObjectID = "Gbx-kl-uOO"; */ -"Gbx-kl-uOO.text" = "Zadano"; - -/* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Upravljanje skočnim prozorima kolačića"; - -/* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ -"gS2-mg-l7R.title" = "Tema"; - -/* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ -"HLr-R8-xxF.text" = "Pretpregledi na dugi pritisak"; - -/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ -"hoT-Nu-KXP.text" = "Privatno pregledavanje s našom aplikacijom za Windows"; - /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Zaštita privatnosti omogućena za sva web-mjesta"; /* Class = "UICollectionViewController"; title = "Icon"; ObjectID = "jbD-Oy-Cmw"; */ "jbD-Oy-Cmw.title" = "Ikona"; -/* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ -"m23-t6-9cb.text" = "Podijeli povratne informacije"; - -/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ -"mLn-1x-Fl5.title" = "Položaj adresne trake"; - -/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ -"mqV-pf-NZ1.text" = "Položaj adresne trake"; - /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Neka DuckDuckGo upravlja skočnim prozorima za pristanak na kolačiće"; @@ -181,36 +76,9 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Nezaštićena web-mjesta"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "O DuckDuckGou"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Vrh"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "Više od DuckDuckGoa"; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Sinkronizacija i sigurnosno kopiranje"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Pretražujte privatno s našom aplikacijom za Mac "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Label"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "Aplikacija DuckDuckGo za Windows"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Veličina teksta"; -/* Class = "UILabel"; text = "Private Voice Search"; ObjectID = "Swa-O7-n8W"; */ -"Swa-O7-n8W.text" = "Privatno glasovno pretraživanje"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Prikaži tipkovnicu"; @@ -220,42 +88,18 @@ /* Class = "UITableViewSection"; headerTitle = "Action"; ObjectID = "U2M-6p-6nl"; */ "U2M-6p-6nl.headerTitle" = "Radnja"; -/* Class = "UILabel"; text = "Autocomplete Suggestions"; ObjectID = "U8i-cQ-5WW"; */ -"U8i-cQ-5WW.text" = "Prikaži prijedloge za automatsko ispunjavanje"; - -/* Class = "UINavigationItem"; title = "Fire Button Animation"; ObjectID = "uns-8w-IwL"; Note = "Fire button animation setting page title"; */ -"uns-8w-IwL.title" = "Animacija gumba Vatre"; - /* Class = "UILabel"; text = "Remove All Fireproof Sites"; ObjectID = "UZx-52-aer"; */ "UZx-52-aer.text" = "Ukloni sva zaštićena web-mjesta"; -/* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "vPz-uO-6gB"; */ -"vPz-uO-6gB.text" = "Globalna kontrola privatnosti (GPC)"; - /* Class = "UITableViewSection"; footerTitle = "App exit is defined by swiping the app to close it while inactivity is when the app is in the background."; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.footerTitle" = "Izlaz iz aplikacije definira se povlačenjem prsta po zaslonu za zatvaranje, a neaktivnost znači da je aplikacija otvorena u pozadini."; /* Class = "UITableViewSection"; headerTitle = "Desired timing"; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.headerTitle" = "Željeno vrijeme brisanja"; -/* Class = "UILabel"; text = "Set as Default Browser"; ObjectID = "xof-5k-PkI"; */ -"xof-5k-PkI.text" = "Postavi kao zadani preglednik"; - /* Class = "UINavigationItem"; title = "Fireproof Sites"; ObjectID = "xUX-nF-HOl"; */ "xUX-nF-HOl.title" = "Zaštićena web-mjesta"; -/* Class = "UILabel"; text = "Block email trackers and hide your address"; ObjectID = "Y6Y-wA-n6Z"; */ -"Y6Y-wA-n6Z.text" = "Blokiraj programe za praćenje e-pošte i sakrij svoju adresu"; - -/* Class = "UILabel"; text = "Keyboard"; ObjectID = "yoZ-jw-Cu3"; */ -"yoZ-jw-Cu3.text" = "Tipkovnica"; - -/* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ -"yvj-LL-MiR.text" = "Dodajte aplikaciju na Dock"; - -/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "Aplikacija DuckDuckGo za Mac"; - /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Nova kartica"; diff --git a/DuckDuckGo/hu.lproj/Localizable.strings b/DuckDuckGo/hu.lproj/Localizable.strings index ad73f354d0..45a918ddae 100644 --- a/DuckDuckGo/hu.lproj/Localizable.strings +++ b/DuckDuckGo/hu.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Billentyűzet"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Jelszavak"; + /* Settings title for the 'More' section */ "settings.more" = "Továbbiak a DuckDuckGo-tól"; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Hosszú lenyomásos előnézetek"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Melyik weboldal nem működik?”"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Helyreállítási kód a vágólapra másolva"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Szerveradatok törlése"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "A rendszer minden eszközt lecsatlakoztat, és a szinkronizált adatokat törli a szerverről."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "Szerveradatok törlése?"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Eltávolítás"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "A(z) „%@” többé nem férhet hozzá a szinkronizált adatokhoz."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "Eszköz eltávolítása?"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Eltávolítás"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "Az eszköz többé nem férhet hozzá a szinkronizált adatokhoz."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "Szinkronizálás kikapcsolása?"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "A szinkronizálás és biztonsági mentés jelenleg sajnos nem érhető el. Próbálkozz újra később."; diff --git a/DuckDuckGo/hu.lproj/Settings.strings b/DuckDuckGo/hu.lproj/Settings.strings index 8c3c75212a..04065a78e1 100644 --- a/DuckDuckGo/hu.lproj/Settings.strings +++ b/DuckDuckGo/hu.lproj/Settings.strings @@ -1,15 +1,3 @@ -/* Class = "UILabel"; text = "Fireproof Sites"; ObjectID = "0DQ-yq-UuT"; */ -"0DQ-yq-UuT.text" = "Tűzálló weboldalak"; - -/* Class = "UITableViewSection"; footerTitle = "Disable to prevent links from automatically opening in other installed apps."; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.footerTitle" = "Letiltásával megakadályozhatod, hogy a hivatkozások automatikusan megnyíljanak más telepített alkalmazásokban."; - -/* Class = "UITableViewSection"; headerTitle = "Customize"; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.headerTitle" = "Testreszabás"; - -/* Class = "UILabel"; text = "Version"; ObjectID = "2ky-s9-1aZ"; */ -"2ky-s9-1aZ.text" = "Változat"; - /* Class = "UINavigationItem"; title = "Keyboard"; ObjectID = "2pp-PM-6rW"; */ "2pp-PM-6rW.title" = "Billentyűzet"; @@ -31,30 +19,9 @@ /* Class = "UILabel"; text = "Clear Tabs and Data"; ObjectID = "9fc-9r-4aA"; */ "9fc-9r-4aA.text" = "Lapok és adatok törlése"; -/* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ -"9Ko-0g-T3h.text" = "Szövegméret"; - -/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ -"9kt-6R-XiZ.text" = "Cím"; - /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Alkalmazás indítása"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Hivatkozások megnyitása társított alkalmazásokban"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Hibakeresés menü"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Jelszavak"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Animáció"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "E-mail védelem"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "Az adatok és/vagy lapok az alkalmazás újraindításakor törlődnek."; @@ -64,114 +31,42 @@ /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ "btj-ri-kRr.title" = "Felugró sütiablakok kezelése"; -/* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ -"cKo-er-HNj.text" = "Alkalmazásikon"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "CR5-Al-WIW"; */ -"CR5-Al-WIW.text" = "Címke"; - /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Nemzetközi adatvédelmi szabályozás (Global Privacy Control, GPC)"; /* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ "CxT-QK-iVn.title" = "Hozzáadás"; -/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ -"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; - /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Kilépés az alkalmazásból 15 perc inaktivitás után"; -/* Class = "UILabel"; text = "10.0.1 (Build 10005)"; ObjectID = "d5n-vG-8kF"; */ -"d5n-vG-8kF.text" = "10.0.1 (Build 10005)"; - -/* Class = "UILabel"; text = "Application Lock"; ObjectID = "dBZ-yq-FYj"; */ -"dBZ-yq-FYj.text" = "Alkalmazás zárolás"; - -/* Class = "UITableViewSection"; headerTitle = "Appearance"; ObjectID = "dj9-vh-Rig"; */ -"dj9-vh-Rig.headerTitle" = "Megjelenés"; - -/* Class = "UITableViewSection"; footerTitle = "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening."; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.footerTitle" = "Ha be van állítva ujjlenyomat- vagy arcfelismerés, illetve rendszerjelszó, megnyitásakor fel kell oldanod az alkalmazást."; - -/* Class = "UITableViewSection"; headerTitle = "Privacy"; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.headerTitle" = "Adatvédelem"; - /* Class = "UILabel"; text = "Label"; ObjectID = "dud-qo-Ces"; */ "dud-qo-Ces.text" = "Címke"; -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Dyd-bm-goj"; */ -"Dyd-bm-goj.title" = "Beállítások"; - -/* Class = "UILabel"; text = "100%"; ObjectID = "EB8-09-gt2"; */ -"EB8-09-gt2.text" = "100%"; - /* Class = "UILabel"; text = "Label"; ObjectID = "EIq-Ev-nfj"; */ "EIq-Ev-nfj.text" = "Címke"; -/* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "ekF-SJ-PAQ"; */ -"ekF-SJ-PAQ.text" = "Adatok automatikus törlése"; - /* Class = "UILabel"; text = "App Exit, Inactive for 5 Minutes"; ObjectID = "ElX-yE-4PX"; */ "ElX-yE-4PX.text" = "Kilépés az alkalmazásból 5 perc inaktivitás után"; /* Class = "UINavigationItem"; title = "App Icon"; ObjectID = "eMH-uv-Kms"; */ "eMH-uv-Kms.title" = "Alkalmazásikon"; -/* Class = "UILabel"; text = "Theme"; ObjectID = "f1O-6u-LFY"; */ -"f1O-6u-LFY.text" = "Kinézet"; - /* Class = "UILabel"; text = "App Exit Only"; ObjectID = "Fal-1Y-o2S"; */ "Fal-1Y-o2S.text" = "Kilépés csak az alkalmazásból"; /* Class = "UITableViewController"; title = "Automatically Clear Data"; ObjectID = "fdJ-b1-Des"; */ "fdJ-b1-Des.title" = "Adatok automatikus törlése"; -/* Class = "UILabel"; text = "Unprotected Sites"; ObjectID = "FHC-1z-Z3v"; */ -"FHC-1z-Z3v.text" = "Védelem nélküli weboldalak"; - -/* Class = "UITableViewSection"; headerTitle = "About"; ObjectID = "FpT-1C-xtx"; */ -"FpT-1C-xtx.headerTitle" = "Rólunk"; - /* Class = "UITableViewController"; title = "Global Privacy Control (GPC)"; ObjectID = "fV3-86-QQj"; */ "fV3-86-QQj.title" = "Nemzetközi adatvédelmi szabályozás (Global Privacy Control, GPC)"; -/* Class = "UILabel"; text = "Add Widget to Home Screen"; ObjectID = "Fxu-zn-51Z"; */ -"Fxu-zn-51Z.text" = "Minialkalmazás hozzáadása a kezdőképernyőhöz"; - -/* Class = "UILabel"; text = "Fire Button Animation"; ObjectID = "gBo-Cu-e2k"; Note = "Fire button animation settings item"; */ -"gBo-Cu-e2k.text" = "Tűz gomb animáció"; - -/* Class = "UILabel"; text = "Default"; ObjectID = "Gbx-kl-uOO"; */ -"Gbx-kl-uOO.text" = "Alapértelmezett"; - -/* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Felugró sütiablakok kezelése"; - -/* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ -"gS2-mg-l7R.title" = "Kinézet"; - -/* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ -"HLr-R8-xxF.text" = "Hosszú lenyomásos előnézetek"; - -/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ -"hoT-Nu-KXP.text" = "Privát böngészés windowsos alkalmazásunkkal"; - /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Adatvédelem engedélyezve minden weboldalhoz"; /* Class = "UICollectionViewController"; title = "Icon"; ObjectID = "jbD-Oy-Cmw"; */ "jbD-Oy-Cmw.title" = "Ikon"; -/* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ -"m23-t6-9cb.text" = "Visszajelzés megosztása"; - -/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ -"mLn-1x-Fl5.title" = "Címsor elhelyezkedése"; - -/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ -"mqV-pf-NZ1.text" = "Címsor elhelyezkedése"; - /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "A DuckDuckGo kezelheti a sütik elfogadására szolgáló felugró ablakokat"; @@ -181,36 +76,9 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Védelem nélküli weboldalak"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "A DuckDuckGóról"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Fel"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "Továbbiak a DuckDuckGótól"; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Szinkronizálás és biztonsági mentés"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Privát böngészés Maces alkalmazásunkkal "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Címke"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "DuckDuckGo Windows alkalmazás"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Szövegméret"; -/* Class = "UILabel"; text = "Private Voice Search"; ObjectID = "Swa-O7-n8W"; */ -"Swa-O7-n8W.text" = "Privát beszédhangalapú keresés"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Billentyűzet megjelenítése bekapcsolva"; @@ -220,42 +88,18 @@ /* Class = "UITableViewSection"; headerTitle = "Action"; ObjectID = "U2M-6p-6nl"; */ "U2M-6p-6nl.headerTitle" = "Művelet"; -/* Class = "UILabel"; text = "Autocomplete Suggestions"; ObjectID = "U8i-cQ-5WW"; */ -"U8i-cQ-5WW.text" = "Automatikus kiegészítési javaslatok megjelenítése"; - -/* Class = "UINavigationItem"; title = "Fire Button Animation"; ObjectID = "uns-8w-IwL"; Note = "Fire button animation setting page title"; */ -"uns-8w-IwL.title" = "Tűz gomb animáció"; - /* Class = "UILabel"; text = "Remove All Fireproof Sites"; ObjectID = "UZx-52-aer"; */ "UZx-52-aer.text" = "Összes tűzálló weboldal eltávolítása"; -/* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "vPz-uO-6gB"; */ -"vPz-uO-6gB.text" = "Nemzetközi adatvédelmi szabályozás (Global Privacy Control, GPC)"; - /* Class = "UITableViewSection"; footerTitle = "App exit is defined by swiping the app to close it while inactivity is when the app is in the background."; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.footerTitle" = "Az alkalmazásból való kilépést úgy határozzák meg, hogy elhúzva bezárják az alkalmazást inaktivitása alatt, amikor az alkalmazás a háttérben van."; /* Class = "UITableViewSection"; headerTitle = "Desired timing"; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.headerTitle" = "Kívánt időzítés"; -/* Class = "UILabel"; text = "Set as Default Browser"; ObjectID = "xof-5k-PkI"; */ -"xof-5k-PkI.text" = "Beállítás alapértelmezett böngészőként"; - /* Class = "UINavigationItem"; title = "Fireproof Sites"; ObjectID = "xUX-nF-HOl"; */ "xUX-nF-HOl.title" = "Tűzálló weboldalak"; -/* Class = "UILabel"; text = "Block email trackers and hide your address"; ObjectID = "Y6Y-wA-n6Z"; */ -"Y6Y-wA-n6Z.text" = "E-mail nyomkövetők letiltása, és a cím elrejtése"; - -/* Class = "UILabel"; text = "Keyboard"; ObjectID = "yoZ-jw-Cu3"; */ -"yoZ-jw-Cu3.text" = "Billentyűzet"; - -/* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ -"yvj-LL-MiR.text" = "Alkalmazás hozzáadása a dokkodhoz"; - -/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "DuckDuckGo Mac alkalmazás"; - /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Új lap"; diff --git a/DuckDuckGo/it.lproj/Localizable.strings b/DuckDuckGo/it.lproj/Localizable.strings index 684b99592b..bb2ac6be4c 100644 --- a/DuckDuckGo/it.lproj/Localizable.strings +++ b/DuckDuckGo/it.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Tastiera"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Password"; + /* Settings title for the 'More' section */ "settings.more" = "Ulteriori informazioni su DuckDuckGo"; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Anteprime con pressione prolungata"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Quale sito web è danneggiato?"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Codice di recupero copiato negli appunti"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Eliminare i dati del server"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "Tutti i dispositivi saranno disconnessi e i dati sincronizzati verranno eliminati dal server."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "Eliminare i dati del server?"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Rimuovi"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "\"%@\" non potrà più accedere ai tuoi dati sincronizzati."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "Rimuovere il dispositivo?"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Rimuovi"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "Questo dispositivo non potrà più accedere ai tuoi dati sincronizzati."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "Disattivare la sincronizzazione?"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "Siamo spiacenti, ma Sincronizzazione e backup non è attualmente disponibile. Riprova più tardi."; diff --git a/DuckDuckGo/it.lproj/Settings.strings b/DuckDuckGo/it.lproj/Settings.strings index ee63f1773e..983d5e7c25 100644 --- a/DuckDuckGo/it.lproj/Settings.strings +++ b/DuckDuckGo/it.lproj/Settings.strings @@ -1,15 +1,3 @@ -/* Class = "UILabel"; text = "Fireproof Sites"; ObjectID = "0DQ-yq-UuT"; */ -"0DQ-yq-UuT.text" = "Siti ignifughi"; - -/* Class = "UITableViewSection"; footerTitle = "Disable to prevent links from automatically opening in other installed apps."; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.footerTitle" = "Disattiva per impedire che i collegamenti si aprano automaticamente in altre app installate."; - -/* Class = "UITableViewSection"; headerTitle = "Customize"; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.headerTitle" = "Personalizza"; - -/* Class = "UILabel"; text = "Version"; ObjectID = "2ky-s9-1aZ"; */ -"2ky-s9-1aZ.text" = "Versione"; - /* Class = "UINavigationItem"; title = "Keyboard"; ObjectID = "2pp-PM-6rW"; */ "2pp-PM-6rW.title" = "Tastiera"; @@ -31,30 +19,9 @@ /* Class = "UILabel"; text = "Clear Tabs and Data"; ObjectID = "9fc-9r-4aA"; */ "9fc-9r-4aA.text" = "Cancella tutte le schede e i dati"; -/* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ -"9Ko-0g-T3h.text" = "Dimensione testo"; - -/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ -"9kt-6R-XiZ.text" = "Titolo"; - /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Avvio dell'app"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Apri i collegamenti nelle loro app associate"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Menu di debug"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Password"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Animazione"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "Protezione email"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "I dati e/o le schede saranno cancellati al riavvio dell'app."; @@ -64,114 +31,42 @@ /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ "btj-ri-kRr.title" = "Gestione popup dei cookie"; -/* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ -"cKo-er-HNj.text" = "Icona dell'app"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "CR5-Al-WIW"; */ -"CR5-Al-WIW.text" = "Etichetta"; - /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Global Privacy Control (GPC)"; /* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ "CxT-QK-iVn.title" = "Aggiungi"; -/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ -"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; - /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Uscita dall'app, inattività di 15 minuti"; -/* Class = "UILabel"; text = "10.0.1 (Build 10005)"; ObjectID = "d5n-vG-8kF"; */ -"d5n-vG-8kF.text" = "10.0.1 (Build 10005)"; - -/* Class = "UILabel"; text = "Application Lock"; ObjectID = "dBZ-yq-FYj"; */ -"dBZ-yq-FYj.text" = "Blocco applicazione"; - -/* Class = "UITableViewSection"; headerTitle = "Appearance"; ObjectID = "dj9-vh-Rig"; */ -"dj9-vh-Rig.headerTitle" = "Aspetto"; - -/* Class = "UITableViewSection"; footerTitle = "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening."; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.footerTitle" = "Se hai impostato Touch ID, Face ID o un codice di accesso al sistema, ti verrà richiesto di sbloccare l'app all'apertura."; - -/* Class = "UITableViewSection"; headerTitle = "Privacy"; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.headerTitle" = "Privacy"; - /* Class = "UILabel"; text = "Label"; ObjectID = "dud-qo-Ces"; */ "dud-qo-Ces.text" = "Etichetta"; -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Dyd-bm-goj"; */ -"Dyd-bm-goj.title" = "Impostazioni"; - -/* Class = "UILabel"; text = "100%"; ObjectID = "EB8-09-gt2"; */ -"EB8-09-gt2.text" = "100%"; - /* Class = "UILabel"; text = "Label"; ObjectID = "EIq-Ev-nfj"; */ "EIq-Ev-nfj.text" = "Etichetta"; -/* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "ekF-SJ-PAQ"; */ -"ekF-SJ-PAQ.text" = "Cancellazione automatica dei dati"; - /* Class = "UILabel"; text = "App Exit, Inactive for 5 Minutes"; ObjectID = "ElX-yE-4PX"; */ "ElX-yE-4PX.text" = "Uscita dall'app, inattività di 5 minuti"; /* Class = "UINavigationItem"; title = "App Icon"; ObjectID = "eMH-uv-Kms"; */ "eMH-uv-Kms.title" = "Icona dell'app"; -/* Class = "UILabel"; text = "Theme"; ObjectID = "f1O-6u-LFY"; */ -"f1O-6u-LFY.text" = "Tema"; - /* Class = "UILabel"; text = "App Exit Only"; ObjectID = "Fal-1Y-o2S"; */ "Fal-1Y-o2S.text" = "Solo in caso di uscita dall'app"; /* Class = "UITableViewController"; title = "Automatically Clear Data"; ObjectID = "fdJ-b1-Des"; */ "fdJ-b1-Des.title" = "Cancellazione automatica dei dati"; -/* Class = "UILabel"; text = "Unprotected Sites"; ObjectID = "FHC-1z-Z3v"; */ -"FHC-1z-Z3v.text" = "Siti non protetti"; - -/* Class = "UITableViewSection"; headerTitle = "About"; ObjectID = "FpT-1C-xtx"; */ -"FpT-1C-xtx.headerTitle" = "Informazioni"; - /* Class = "UITableViewController"; title = "Global Privacy Control (GPC)"; ObjectID = "fV3-86-QQj"; */ "fV3-86-QQj.title" = "Global Privacy Control (GPC)"; -/* Class = "UILabel"; text = "Add Widget to Home Screen"; ObjectID = "Fxu-zn-51Z"; */ -"Fxu-zn-51Z.text" = "Aggiungi widget alla schermata iniziale"; - -/* Class = "UILabel"; text = "Fire Button Animation"; ObjectID = "gBo-Cu-e2k"; Note = "Fire button animation settings item"; */ -"gBo-Cu-e2k.text" = "Animazione Fire Button"; - -/* Class = "UILabel"; text = "Default"; ObjectID = "Gbx-kl-uOO"; */ -"Gbx-kl-uOO.text" = "Predefinito"; - -/* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Gestisci popup dei cookie"; - -/* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ -"gS2-mg-l7R.title" = "Tema"; - -/* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ -"HLr-R8-xxF.text" = "Anteprime con pressione prolungata"; - -/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ -"hoT-Nu-KXP.text" = "Naviga privatamente con la nostra app per Windows"; - /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Tutela della privacy attivata per tutti i siti"; /* Class = "UICollectionViewController"; title = "Icon"; ObjectID = "jbD-Oy-Cmw"; */ "jbD-Oy-Cmw.title" = "Icona"; -/* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ -"m23-t6-9cb.text" = "Condividi feedback"; - -/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ -"mLn-1x-Fl5.title" = "Posizione Barra degli indirizzi"; - -/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ -"mqV-pf-NZ1.text" = "Posizione Barra degli indirizzi"; - /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Consenti a DuckDuckGo di gestire i popup per il consenso ai cookie"; @@ -181,36 +76,9 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Siti non protetti"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "Info su DuckDuckGo"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Inizio"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "Ulteriori informazioni su DuckDuckGo"; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Sincronizzazione e backup"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Naviga in privato con la nostra app per Mac "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Etichetta"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "App Windows DuckDuckGo"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Dimensione testo"; -/* Class = "UILabel"; text = "Private Voice Search"; ObjectID = "Swa-O7-n8W"; */ -"Swa-O7-n8W.text" = "Ricerca vocale privata"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Visualizza tastiera su"; @@ -220,42 +88,18 @@ /* Class = "UITableViewSection"; headerTitle = "Action"; ObjectID = "U2M-6p-6nl"; */ "U2M-6p-6nl.headerTitle" = "Azione"; -/* Class = "UILabel"; text = "Autocomplete Suggestions"; ObjectID = "U8i-cQ-5WW"; */ -"U8i-cQ-5WW.text" = "Mostra suggerimenti di completamento automatico"; - -/* Class = "UINavigationItem"; title = "Fire Button Animation"; ObjectID = "uns-8w-IwL"; Note = "Fire button animation setting page title"; */ -"uns-8w-IwL.title" = "Animazione Fire Button"; - /* Class = "UILabel"; text = "Remove All Fireproof Sites"; ObjectID = "UZx-52-aer"; */ "UZx-52-aer.text" = "Rimuovi tutti i siti ignifughi"; -/* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "vPz-uO-6gB"; */ -"vPz-uO-6gB.text" = "Global Privacy Control (GPC)"; - /* Class = "UITableViewSection"; footerTitle = "App exit is defined by swiping the app to close it while inactivity is when the app is in the background."; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.footerTitle" = "L'uscita dall'app avviene scorrendo l'app per chiuderla mentre l'inattività si verifica quando l'app rimane aperta in background."; /* Class = "UITableViewSection"; headerTitle = "Desired timing"; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.headerTitle" = "Intervallo desiderato"; -/* Class = "UILabel"; text = "Set as Default Browser"; ObjectID = "xof-5k-PkI"; */ -"xof-5k-PkI.text" = "Imposta come browser predefinito"; - /* Class = "UINavigationItem"; title = "Fireproof Sites"; ObjectID = "xUX-nF-HOl"; */ "xUX-nF-HOl.title" = "Siti ignifughi"; -/* Class = "UILabel"; text = "Block email trackers and hide your address"; ObjectID = "Y6Y-wA-n6Z"; */ -"Y6Y-wA-n6Z.text" = "Blocca i sistemi di tracciamento delle email e nascondi il tuo indirizzo"; - -/* Class = "UILabel"; text = "Keyboard"; ObjectID = "yoZ-jw-Cu3"; */ -"yoZ-jw-Cu3.text" = "Tastiera"; - -/* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ -"yvj-LL-MiR.text" = "Aggiungi app al tuo dock"; - -/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "App Mac DuckDuckGo"; - /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Nuova scheda"; diff --git a/DuckDuckGo/lt.lproj/Localizable.strings b/DuckDuckGo/lt.lproj/Localizable.strings index 4e4bec80ab..0b243fbd45 100644 --- a/DuckDuckGo/lt.lproj/Localizable.strings +++ b/DuckDuckGo/lt.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Klaviatūra"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Slaptažodžiai"; + /* Settings title for the 'More' section */ "settings.more" = "Daugiau iš „DuckDuckGo“"; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Peržiūros ilgai spaudžiant"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Kuri svetainė neveikia?"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Atkūrimo kodas nukopijuotas į mainų sritį"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Ištrinti serverio duomenis"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "Visi įrenginiai bus atjungti, o sinchronizuoti duomenys bus ištrinti iš serverio."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "Ištrinti serverio duomenis?"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Pašalinti"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "„%@“ nebegalės pasiekti jūsų sinchronizuotų duomenų."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "Pašalinti įrenginį?"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Pašalinti"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "Šis įrenginys nebegalės pasiekti jūsų sinchronizuotų duomenų."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "Išjungti sinchronizavimą?"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "Atsiprašome, bet sinchronizavimas ir atsarginės kopijos kūrimas šiuo metu nepasiekiami. Pabandykite dar kartą vėliau."; diff --git a/DuckDuckGo/lt.lproj/Settings.strings b/DuckDuckGo/lt.lproj/Settings.strings index 054d5b4102..4a069ef60a 100644 --- a/DuckDuckGo/lt.lproj/Settings.strings +++ b/DuckDuckGo/lt.lproj/Settings.strings @@ -1,15 +1,3 @@ -/* Class = "UILabel"; text = "Fireproof Sites"; ObjectID = "0DQ-yq-UuT"; */ -"0DQ-yq-UuT.text" = "Apsaugoti svetaines"; - -/* Class = "UITableViewSection"; footerTitle = "Disable to prevent links from automatically opening in other installed apps."; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.footerTitle" = "Išjungti, kad nuorodos automatiškai neatsidarytų kitose įdiegtose programose."; - -/* Class = "UITableViewSection"; headerTitle = "Customize"; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.headerTitle" = "Tinkinti"; - -/* Class = "UILabel"; text = "Version"; ObjectID = "2ky-s9-1aZ"; */ -"2ky-s9-1aZ.text" = "Versija"; - /* Class = "UINavigationItem"; title = "Keyboard"; ObjectID = "2pp-PM-6rW"; */ "2pp-PM-6rW.title" = "Klaviatūra"; @@ -31,30 +19,9 @@ /* Class = "UILabel"; text = "Clear Tabs and Data"; ObjectID = "9fc-9r-4aA"; */ "9fc-9r-4aA.text" = "Valyti skirtukus ir duomenis"; -/* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ -"9Ko-0g-T3h.text" = "Teksto dydis"; - -/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ -"9kt-6R-XiZ.text" = "Pavadinimas"; - /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Programos paleidimas"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Atidaryti nuorodas susijusiose programose"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Derinimo meniu"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Slaptažodžiai"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Animacija"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "El. pašto apsauga"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "Duomenys ir (arba) skirtukai bus išvalyti iš naujo paleidus programą."; @@ -64,114 +31,42 @@ /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ "btj-ri-kRr.title" = "Valdyti slapukų iššokančiuosius langus"; -/* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ -"cKo-er-HNj.text" = "Programėlės piktograma"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "CR5-Al-WIW"; */ -"CR5-Al-WIW.text" = "Etiketė"; - /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Visuotinė privatumo kontrolė (VPK)"; /* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ "CxT-QK-iVn.title" = "Papildyti"; -/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ -"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; - /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Išeinant iš programos, po 15 min. neveikimo"; -/* Class = "UILabel"; text = "10.0.1 (Build 10005)"; ObjectID = "d5n-vG-8kF"; */ -"d5n-vG-8kF.text" = "10.0.1 (10005 versija)"; - -/* Class = "UILabel"; text = "Application Lock"; ObjectID = "dBZ-yq-FYj"; */ -"dBZ-yq-FYj.text" = "Programos užraktas"; - -/* Class = "UITableViewSection"; headerTitle = "Appearance"; ObjectID = "dj9-vh-Rig"; */ -"dj9-vh-Rig.headerTitle" = "Išvaizda"; - -/* Class = "UITableViewSection"; footerTitle = "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening."; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.footerTitle" = "Jei nustatytas „Touch ID“, „Face ID“ arba sistemos slaptažodis, prieš atidarydami būsite paprašyti atrakinti programą."; - -/* Class = "UITableViewSection"; headerTitle = "Privacy"; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.headerTitle" = "Privatumas"; - /* Class = "UILabel"; text = "Label"; ObjectID = "dud-qo-Ces"; */ "dud-qo-Ces.text" = "Etiketė"; -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Dyd-bm-goj"; */ -"Dyd-bm-goj.title" = "Nustatymai"; - -/* Class = "UILabel"; text = "100%"; ObjectID = "EB8-09-gt2"; */ -"EB8-09-gt2.text" = "100 %"; - /* Class = "UILabel"; text = "Label"; ObjectID = "EIq-Ev-nfj"; */ "EIq-Ev-nfj.text" = "Etiketė"; -/* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "ekF-SJ-PAQ"; */ -"ekF-SJ-PAQ.text" = "Automatiškai valyti duomenis"; - /* Class = "UILabel"; text = "App Exit, Inactive for 5 Minutes"; ObjectID = "ElX-yE-4PX"; */ "ElX-yE-4PX.text" = "Išeinant iš programos, po 5 min. neveikimo"; /* Class = "UINavigationItem"; title = "App Icon"; ObjectID = "eMH-uv-Kms"; */ "eMH-uv-Kms.title" = "Programėlės piktograma"; -/* Class = "UILabel"; text = "Theme"; ObjectID = "f1O-6u-LFY"; */ -"f1O-6u-LFY.text" = "Tema"; - /* Class = "UILabel"; text = "App Exit Only"; ObjectID = "Fal-1Y-o2S"; */ "Fal-1Y-o2S.text" = "Tik išeinant iš programos"; /* Class = "UITableViewController"; title = "Automatically Clear Data"; ObjectID = "fdJ-b1-Des"; */ "fdJ-b1-Des.title" = "Automatiškai valyti duomenis"; -/* Class = "UILabel"; text = "Unprotected Sites"; ObjectID = "FHC-1z-Z3v"; */ -"FHC-1z-Z3v.text" = "Neapsaugotos svetainės"; - -/* Class = "UITableViewSection"; headerTitle = "About"; ObjectID = "FpT-1C-xtx"; */ -"FpT-1C-xtx.headerTitle" = "Apie"; - /* Class = "UITableViewController"; title = "Global Privacy Control (GPC)"; ObjectID = "fV3-86-QQj"; */ "fV3-86-QQj.title" = "Visuotinė privatumo kontrolė (VPK)"; -/* Class = "UILabel"; text = "Add Widget to Home Screen"; ObjectID = "Fxu-zn-51Z"; */ -"Fxu-zn-51Z.text" = "Įtraukti valdiklį į pagrindinį ekraną"; - -/* Class = "UILabel"; text = "Fire Button Animation"; ObjectID = "gBo-Cu-e2k"; Note = "Fire button animation settings item"; */ -"gBo-Cu-e2k.text" = "Mygtuko „Fire“ animacija"; - -/* Class = "UILabel"; text = "Default"; ObjectID = "Gbx-kl-uOO"; */ -"Gbx-kl-uOO.text" = "Numatytoji"; - -/* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Valdyti slapukų iššokančiuosius langus"; - -/* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ -"gS2-mg-l7R.title" = "Tema"; - -/* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ -"HLr-R8-xxF.text" = "Peržiūros ilgai spaudžiant"; - -/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ -"hoT-Nu-KXP.text" = "Naršykite privačiai naudodami mūsų programą, skirtą „Windows“"; - /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Privatumo apsauga įjungta visose svetainėse"; /* Class = "UICollectionViewController"; title = "Icon"; ObjectID = "jbD-Oy-Cmw"; */ "jbD-Oy-Cmw.title" = "Piktograma"; -/* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ -"m23-t6-9cb.text" = "Bendrinti atsiliepimą"; - -/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ -"mLn-1x-Fl5.title" = "Adreso juostos padėtis"; - -/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ -"mqV-pf-NZ1.text" = "Adreso juostos padėtis"; - /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Leiskite „DuckDuckGo“ valdyti sutikimo su slapukais iššokančiuosius langus"; @@ -181,36 +76,9 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Neapsaugotos svetainės"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "Apie DuckDuckGo"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Viršus"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "Daugiau iš „DuckDuckGo“"; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Sinchronizuoti ir kurti atsarginę kopiją"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Naršykite privačiai su mūsų „Mac“ skirta programa "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Etiketė"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "„DuckDuckGo“ programa „Windows“"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Teksto dydis"; -/* Class = "UILabel"; text = "Private Voice Search"; ObjectID = "Swa-O7-n8W"; */ -"Swa-O7-n8W.text" = "Privati paieška balsu"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Rodyti klaviatūrą"; @@ -220,42 +88,18 @@ /* Class = "UITableViewSection"; headerTitle = "Action"; ObjectID = "U2M-6p-6nl"; */ "U2M-6p-6nl.headerTitle" = "Veiksmas"; -/* Class = "UILabel"; text = "Autocomplete Suggestions"; ObjectID = "U8i-cQ-5WW"; */ -"U8i-cQ-5WW.text" = "Rodyti automatinio užbaigimo pasiūlymus"; - -/* Class = "UINavigationItem"; title = "Fire Button Animation"; ObjectID = "uns-8w-IwL"; Note = "Fire button animation setting page title"; */ -"uns-8w-IwL.title" = "Mygtuko „Fire“ animacija"; - /* Class = "UILabel"; text = "Remove All Fireproof Sites"; ObjectID = "UZx-52-aer"; */ "UZx-52-aer.text" = "Pašalinti visas apsaugotas svetaines"; -/* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "vPz-uO-6gB"; */ -"vPz-uO-6gB.text" = "Visuotinė privatumo kontrolė (VPK)"; - /* Class = "UITableViewSection"; footerTitle = "App exit is defined by swiping the app to close it while inactivity is when the app is in the background."; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.footerTitle" = "Išėjimas iš programos apibrėžiamas kaip programos uždarymas braukiant, o neaktyvumas – fone veikianti programa."; /* Class = "UITableViewSection"; headerTitle = "Desired timing"; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.headerTitle" = "Pageidaujamas laikas"; -/* Class = "UILabel"; text = "Set as Default Browser"; ObjectID = "xof-5k-PkI"; */ -"xof-5k-PkI.text" = "Nustatyti kaip numatytąją naršyklę"; - /* Class = "UINavigationItem"; title = "Fireproof Sites"; ObjectID = "xUX-nF-HOl"; */ "xUX-nF-HOl.title" = "Apsaugoti svetaines"; -/* Class = "UILabel"; text = "Block email trackers and hide your address"; ObjectID = "Y6Y-wA-n6Z"; */ -"Y6Y-wA-n6Z.text" = "Blokuokite el. laiškų sekimo priemones ir paslėpkite savo adresą"; - -/* Class = "UILabel"; text = "Keyboard"; ObjectID = "yoZ-jw-Cu3"; */ -"yoZ-jw-Cu3.text" = "Klaviatūra"; - -/* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ -"yvj-LL-MiR.text" = "Įtraukti programėlę į stotelę"; - -/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "„DuckDuckGo“ programa „Mac“"; - /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Nauja kortelė"; diff --git a/DuckDuckGo/lv.lproj/Localizable.strings b/DuckDuckGo/lv.lproj/Localizable.strings index 63199eb68c..c163f07853 100644 --- a/DuckDuckGo/lv.lproj/Localizable.strings +++ b/DuckDuckGo/lv.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Tastatūra"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Paroles"; + /* Settings title for the 'More' section */ "settings.more" = "Vairāk no DuckDuckGo"; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Ilgas nospiešanas rezultātu priekšskatījumi"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Kura vietne ir bojāta?"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Atgūšanas kods ir nokopēts starpliktuvē"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Dzēst servera datus"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "Visas ierīces tiks atvienotas un tavi sinhronizētie dati tiks dzēsti no servera."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "Dzēst servera datus?"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Noņemt"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "\"%@\" vairs nevarēs piekļūt taviem sinhronizētajiem datiem."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "Noņemt ierīci?"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Noņemt"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "Šī ierīce vairs nevarēs piekļūt taviem sinhronizētajiem datiem."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "Izslēgt sinhronizāciju?"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "Diemžēl sinhronizācija un dublēšana pašlaik nav pieejama. Lūdzu, mēģini vēlreiz vēlāk."; diff --git a/DuckDuckGo/lv.lproj/Settings.strings b/DuckDuckGo/lv.lproj/Settings.strings index 6df96f933d..985402f0ea 100644 --- a/DuckDuckGo/lv.lproj/Settings.strings +++ b/DuckDuckGo/lv.lproj/Settings.strings @@ -1,15 +1,3 @@ -/* Class = "UILabel"; text = "Fireproof Sites"; ObjectID = "0DQ-yq-UuT"; */ -"0DQ-yq-UuT.text" = "Ugunsdrošas vietnes"; - -/* Class = "UITableViewSection"; footerTitle = "Disable to prevent links from automatically opening in other installed apps."; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.footerTitle" = "Atspējo, lai novērstu saišu automātisku atvēršanu citās instalētajās lietotnēs."; - -/* Class = "UITableViewSection"; headerTitle = "Customize"; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.headerTitle" = "Pielāgošana"; - -/* Class = "UILabel"; text = "Version"; ObjectID = "2ky-s9-1aZ"; */ -"2ky-s9-1aZ.text" = "Versija"; - /* Class = "UINavigationItem"; title = "Keyboard"; ObjectID = "2pp-PM-6rW"; */ "2pp-PM-6rW.title" = "Tastatūra"; @@ -31,30 +19,9 @@ /* Class = "UILabel"; text = "Clear Tabs and Data"; ObjectID = "9fc-9r-4aA"; */ "9fc-9r-4aA.text" = "Notīrīt cilnes un datus"; -/* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ -"9Ko-0g-T3h.text" = "Burtu izmērs"; - -/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ -"9kt-6R-XiZ.text" = "Nosaukums"; - /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Lietotnes palaišana"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Atvērt saites saistītajās lietotnēs"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Atkļūdošanas izvēlne"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Paroles"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Animācija"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "E-pasta aizsardzība"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "Restartējot lietotni, dati un/vai cilnes tiks notīrītas."; @@ -64,114 +31,42 @@ /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ "btj-ri-kRr.title" = "Pārvaldīt sīkfailu uznirstošos logus"; -/* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ -"cKo-er-HNj.text" = "Lietotnes ikona"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "CR5-Al-WIW"; */ -"CR5-Al-WIW.text" = "Etiķete"; - /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Globālā privātuma kontrole (GPC)"; /* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ "CxT-QK-iVn.title" = "Pievienot"; -/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ -"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; - /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Iziešana no lietotnes, neaktīva 15 minūtes"; -/* Class = "UILabel"; text = "10.0.1 (Build 10005)"; ObjectID = "d5n-vG-8kF"; */ -"d5n-vG-8kF.text" = "10.0.1 (būvējums 10005)"; - -/* Class = "UILabel"; text = "Application Lock"; ObjectID = "dBZ-yq-FYj"; */ -"dBZ-yq-FYj.text" = "Lietojumprogrammas bloķēšana"; - -/* Class = "UITableViewSection"; headerTitle = "Appearance"; ObjectID = "dj9-vh-Rig"; */ -"dj9-vh-Rig.headerTitle" = "Parādas"; - -/* Class = "UITableViewSection"; footerTitle = "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening."; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.footerTitle" = "Ja ir iestatīts Touch ID, Face ID vai sistēmas piekļuves kods, atverot lietotni, tev tā būs jāatbloķē."; - -/* Class = "UITableViewSection"; headerTitle = "Privacy"; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.headerTitle" = "Privātums"; - /* Class = "UILabel"; text = "Label"; ObjectID = "dud-qo-Ces"; */ "dud-qo-Ces.text" = "Etiķete"; -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Dyd-bm-goj"; */ -"Dyd-bm-goj.title" = "Iestatījumi"; - -/* Class = "UILabel"; text = "100%"; ObjectID = "EB8-09-gt2"; */ -"EB8-09-gt2.text" = "100%"; - /* Class = "UILabel"; text = "Label"; ObjectID = "EIq-Ev-nfj"; */ "EIq-Ev-nfj.text" = "Etiķete"; -/* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "ekF-SJ-PAQ"; */ -"ekF-SJ-PAQ.text" = "Automātiski notīrīt datus"; - /* Class = "UILabel"; text = "App Exit, Inactive for 5 Minutes"; ObjectID = "ElX-yE-4PX"; */ "ElX-yE-4PX.text" = "Iziešana no lietotnes, neaktīva 5 minūtes"; /* Class = "UINavigationItem"; title = "App Icon"; ObjectID = "eMH-uv-Kms"; */ "eMH-uv-Kms.title" = "Lietotnes ikona"; -/* Class = "UILabel"; text = "Theme"; ObjectID = "f1O-6u-LFY"; */ -"f1O-6u-LFY.text" = "Motīvs"; - /* Class = "UILabel"; text = "App Exit Only"; ObjectID = "Fal-1Y-o2S"; */ "Fal-1Y-o2S.text" = "Tikai iziešana no lietotnes"; /* Class = "UITableViewController"; title = "Automatically Clear Data"; ObjectID = "fdJ-b1-Des"; */ "fdJ-b1-Des.title" = "Automātiski notīrīt datus"; -/* Class = "UILabel"; text = "Unprotected Sites"; ObjectID = "FHC-1z-Z3v"; */ -"FHC-1z-Z3v.text" = "Neaizsargātas vietnes"; - -/* Class = "UITableViewSection"; headerTitle = "About"; ObjectID = "FpT-1C-xtx"; */ -"FpT-1C-xtx.headerTitle" = "Par"; - /* Class = "UITableViewController"; title = "Global Privacy Control (GPC)"; ObjectID = "fV3-86-QQj"; */ "fV3-86-QQj.title" = "Globālā privātuma kontrole (GPC)"; -/* Class = "UILabel"; text = "Add Widget to Home Screen"; ObjectID = "Fxu-zn-51Z"; */ -"Fxu-zn-51Z.text" = "Pievienot logrīku sākuma ekrānam"; - -/* Class = "UILabel"; text = "Fire Button Animation"; ObjectID = "gBo-Cu-e2k"; Note = "Fire button animation settings item"; */ -"gBo-Cu-e2k.text" = "Uguns pogas animācija"; - -/* Class = "UILabel"; text = "Default"; ObjectID = "Gbx-kl-uOO"; */ -"Gbx-kl-uOO.text" = "Noklusējums"; - -/* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Pārvaldīt sīkfailu uznirstošos logus"; - -/* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ -"gS2-mg-l7R.title" = "Motīvs"; - -/* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ -"HLr-R8-xxF.text" = "Ilgas nospiešanas rezultātu priekšskatījumi"; - -/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ -"hoT-Nu-KXP.text" = "Pārlūko privāti ar mūsu Windows lietotni"; - /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Privātuma aizsardzība ir iespējota visām vietnēm"; /* Class = "UICollectionViewController"; title = "Icon"; ObjectID = "jbD-Oy-Cmw"; */ "jbD-Oy-Cmw.title" = "Ikona"; -/* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ -"m23-t6-9cb.text" = "Kopīgot atsauksmes"; - -/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ -"mLn-1x-Fl5.title" = "Adreses joslas pozīcija"; - -/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ -"mqV-pf-NZ1.text" = "Adreses joslas pozīcija"; - /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Ļaut DuckDuckGo pārvaldīt sīkfailu piekrišanas uznirstošos logus"; @@ -181,36 +76,9 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Neaizsargātas vietnes"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "Par DuckDuckGo"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Populārākie"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "Vairāk no DuckDuckGo"; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Sinhronizācija un dublēšana"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Pārlūko privāti, izmantojot mūsu Mac lietotni "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Etiķete"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "DuckDuckGo Windows lietotne"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Burtu izmērs"; -/* Class = "UILabel"; text = "Private Voice Search"; ObjectID = "Swa-O7-n8W"; */ -"Swa-O7-n8W.text" = "Privātā balss meklēšana"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Rādīt tastatūru"; @@ -220,42 +88,18 @@ /* Class = "UITableViewSection"; headerTitle = "Action"; ObjectID = "U2M-6p-6nl"; */ "U2M-6p-6nl.headerTitle" = "Darbība"; -/* Class = "UILabel"; text = "Autocomplete Suggestions"; ObjectID = "U8i-cQ-5WW"; */ -"U8i-cQ-5WW.text" = "Automātiski pabeigt ieteikumus"; - -/* Class = "UINavigationItem"; title = "Fire Button Animation"; ObjectID = "uns-8w-IwL"; Note = "Fire button animation setting page title"; */ -"uns-8w-IwL.title" = "Uguns pogas animācija"; - /* Class = "UILabel"; text = "Remove All Fireproof Sites"; ObjectID = "UZx-52-aer"; */ "UZx-52-aer.text" = "Noņemt visas ugunsdrošās vietnes"; -/* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "vPz-uO-6gB"; */ -"vPz-uO-6gB.text" = "Globālā privātuma kontrole (GPC)"; - /* Class = "UITableViewSection"; footerTitle = "App exit is defined by swiping the app to close it while inactivity is when the app is in the background."; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.footerTitle" = "Iziešana no lietotnes tiek veikta, pavelkot lietotni, lai to aizvērtu, un lietotne nav aktīva tad, kad tā darbojas fonā."; /* Class = "UITableViewSection"; headerTitle = "Desired timing"; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.headerTitle" = "Vēlamais laiks"; -/* Class = "UILabel"; text = "Set as Default Browser"; ObjectID = "xof-5k-PkI"; */ -"xof-5k-PkI.text" = "Iestatīt kā noklusējuma pārlūku"; - /* Class = "UINavigationItem"; title = "Fireproof Sites"; ObjectID = "xUX-nF-HOl"; */ "xUX-nF-HOl.title" = "Ugunsdrošas vietnes"; -/* Class = "UILabel"; text = "Block email trackers and hide your address"; ObjectID = "Y6Y-wA-n6Z"; */ -"Y6Y-wA-n6Z.text" = "Bloķē e-pasta izsekotājus un paslēp savu adresi"; - -/* Class = "UILabel"; text = "Keyboard"; ObjectID = "yoZ-jw-Cu3"; */ -"yoZ-jw-Cu3.text" = "Tastatūra"; - -/* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ -"yvj-LL-MiR.text" = "Pievienot lietotni dokam"; - -/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "DuckDuckGo Mac lietotne"; - /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Jauna cilne"; diff --git a/DuckDuckGo/nb.lproj/Localizable.strings b/DuckDuckGo/nb.lproj/Localizable.strings index df08d26c91..f0880a9b16 100644 --- a/DuckDuckGo/nb.lproj/Localizable.strings +++ b/DuckDuckGo/nb.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Tastatur"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Passord"; + /* Settings title for the 'More' section */ "settings.more" = "Mer fra DuckDuckGo"; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Forhåndsvisning ved å trykke og holde"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Hvilket nettsted fungerer ikke?"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Gjenopprettingskoden er kopiert til utklippstavlen"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Slett serverdata"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "Alle enheter blir frakoblet og dine synkroniserte data blir slettet fra serveren."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "Vil du slette serverdata?"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Fjern"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "\"%@\" vil ikke lenger ha tilgang til dine synkroniserte data."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "Vil du fjerne enheten?"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Fjern"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "Denne enheten får ikke lenger tilgang til de synkroniserte dataene dine."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "Vil du slå av synkronisering?\n"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "Beklager, men synkronisering og sikkerhetskopiering er ikke tilgjengelig for øyeblikket. Prøv igjen senere."; diff --git a/DuckDuckGo/nb.lproj/Settings.strings b/DuckDuckGo/nb.lproj/Settings.strings index cf184964a9..95cb40ddef 100644 --- a/DuckDuckGo/nb.lproj/Settings.strings +++ b/DuckDuckGo/nb.lproj/Settings.strings @@ -22,21 +22,6 @@ /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Appoversikt"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Åpne lenker i tilknyttede apper"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Feilsøkingsmeny"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Passord"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Animasjon"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "E-postbeskyttelse"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "Data og/eller faner slettes/lukkes ved omstart av appen."; @@ -91,30 +76,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Ubeskyttede nettsteder"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "Om DuckDuckGo"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Topp"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "Mer fra DuckDuckGo"; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Synkronisering og sikkerhetskopiering"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Surf privat med vår app for Mac "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Etikett"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "DuckDuckGo Windows-app"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Tekststørrelse"; diff --git a/DuckDuckGo/nl.lproj/Localizable.strings b/DuckDuckGo/nl.lproj/Localizable.strings index 8bb6435aff..11d462af04 100644 --- a/DuckDuckGo/nl.lproj/Localizable.strings +++ b/DuckDuckGo/nl.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Toetsenbord"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Wachtwoorden"; + /* Settings title for the 'More' section */ "settings.more" = "Meer over DuckDuckGo"; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Voorbeeldweergave bij lang indrukken"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Welke website is defect?"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Herstelcode gekopieerd naar klembord"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Servergegevens verwijderen"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "De verbinding met alle apparaten wordt verbroken en je gesynchroniseerde gegevens worden van de server verwijderd."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "Wil je de servergegevens verwijderen?"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Verwijderen"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "'%@' heeft geen toegang meer tot je gesynchroniseerde gegevens."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "Apparaat verwijderen?"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Verwijderen"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "Dit apparaat heeft geen toegang meer tot je gesynchroniseerde gegevens."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "Synchronisatie uitschakelen?"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "Sorry, maar synchroniseren en back-up is momenteel niet beschikbaar. Probeer het later opnieuw."; diff --git a/DuckDuckGo/nl.lproj/Settings.strings b/DuckDuckGo/nl.lproj/Settings.strings index 7479418778..33a73b1b46 100644 --- a/DuckDuckGo/nl.lproj/Settings.strings +++ b/DuckDuckGo/nl.lproj/Settings.strings @@ -1,15 +1,3 @@ -/* Class = "UILabel"; text = "Fireproof Sites"; ObjectID = "0DQ-yq-UuT"; */ -"0DQ-yq-UuT.text" = "Brandveilige websites"; - -/* Class = "UITableViewSection"; footerTitle = "Disable to prevent links from automatically opening in other installed apps."; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.footerTitle" = "Uitschakelen om te voorkomen dat links automatisch geopend worden in andere geïnstalleerde apps."; - -/* Class = "UITableViewSection"; headerTitle = "Customize"; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.headerTitle" = "Aanpassen"; - -/* Class = "UILabel"; text = "Version"; ObjectID = "2ky-s9-1aZ"; */ -"2ky-s9-1aZ.text" = "Versie"; - /* Class = "UINavigationItem"; title = "Keyboard"; ObjectID = "2pp-PM-6rW"; */ "2pp-PM-6rW.title" = "Toetsenbord"; @@ -31,30 +19,9 @@ /* Class = "UILabel"; text = "Clear Tabs and Data"; ObjectID = "9fc-9r-4aA"; */ "9fc-9r-4aA.text" = "Tabbladen en gegevens wissen"; -/* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ -"9Ko-0g-T3h.text" = "Lettergrootte"; - -/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ -"9kt-6R-XiZ.text" = "Titel"; - /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Starten van app"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Links openen in bijbehorende apps"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Probleemoplossingsmenu"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Wachtwoorden"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Animatie"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "E-mailbescherming"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "Gegevens en/of tabbladen worden gewist bij het opnieuw starten van de app."; @@ -64,114 +31,42 @@ /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ "btj-ri-kRr.title" = "Cookiepop-ups beheren"; -/* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ -"cKo-er-HNj.text" = "App-pictogram"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "CR5-Al-WIW"; */ -"CR5-Al-WIW.text" = "Label"; - /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Global Privacy Control (GPC)"; /* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ "CxT-QK-iVn.title" = "Toevoegen"; -/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ -"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; - /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "App afsluiten, 15 minuten inactief"; -/* Class = "UILabel"; text = "10.0.1 (Build 10005)"; ObjectID = "d5n-vG-8kF"; */ -"d5n-vG-8kF.text" = "10.0.1 (Build 10005)"; - -/* Class = "UILabel"; text = "Application Lock"; ObjectID = "dBZ-yq-FYj"; */ -"dBZ-yq-FYj.text" = "App-vergrendeling"; - -/* Class = "UITableViewSection"; headerTitle = "Appearance"; ObjectID = "dj9-vh-Rig"; */ -"dj9-vh-Rig.headerTitle" = "Uiterlijk"; - -/* Class = "UITableViewSection"; footerTitle = "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening."; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.footerTitle" = "Als je Touch ID, Face ID of een systeemwachtwoord hebt ingesteld, word je gevraagd om de app te ontgrendelen als je deze opent."; - -/* Class = "UITableViewSection"; headerTitle = "Privacy"; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.headerTitle" = "Privacy"; - /* Class = "UILabel"; text = "Label"; ObjectID = "dud-qo-Ces"; */ "dud-qo-Ces.text" = "Label"; -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Dyd-bm-goj"; */ -"Dyd-bm-goj.title" = "Instellingen"; - -/* Class = "UILabel"; text = "100%"; ObjectID = "EB8-09-gt2"; */ -"EB8-09-gt2.text" = "100%"; - /* Class = "UILabel"; text = "Label"; ObjectID = "EIq-Ev-nfj"; */ "EIq-Ev-nfj.text" = "Label"; -/* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "ekF-SJ-PAQ"; */ -"ekF-SJ-PAQ.text" = "Gegevens automatisch wissen"; - /* Class = "UILabel"; text = "App Exit, Inactive for 5 Minutes"; ObjectID = "ElX-yE-4PX"; */ "ElX-yE-4PX.text" = "App afsluiten, 5 minuten inactief"; /* Class = "UINavigationItem"; title = "App Icon"; ObjectID = "eMH-uv-Kms"; */ "eMH-uv-Kms.title" = "App-pictogram"; -/* Class = "UILabel"; text = "Theme"; ObjectID = "f1O-6u-LFY"; */ -"f1O-6u-LFY.text" = "Thema"; - /* Class = "UILabel"; text = "App Exit Only"; ObjectID = "Fal-1Y-o2S"; */ "Fal-1Y-o2S.text" = "Alleen app afsluiten"; /* Class = "UITableViewController"; title = "Automatically Clear Data"; ObjectID = "fdJ-b1-Des"; */ "fdJ-b1-Des.title" = "Gegevens automatisch wissen"; -/* Class = "UILabel"; text = "Unprotected Sites"; ObjectID = "FHC-1z-Z3v"; */ -"FHC-1z-Z3v.text" = "Onbeschermde sites"; - -/* Class = "UITableViewSection"; headerTitle = "About"; ObjectID = "FpT-1C-xtx"; */ -"FpT-1C-xtx.headerTitle" = "Over"; - /* Class = "UITableViewController"; title = "Global Privacy Control (GPC)"; ObjectID = "fV3-86-QQj"; */ "fV3-86-QQj.title" = "Global Privacy Control (GPC)"; -/* Class = "UILabel"; text = "Add Widget to Home Screen"; ObjectID = "Fxu-zn-51Z"; */ -"Fxu-zn-51Z.text" = "Widget toevoegen aan startscherm"; - -/* Class = "UILabel"; text = "Fire Button Animation"; ObjectID = "gBo-Cu-e2k"; Note = "Fire button animation settings item"; */ -"gBo-Cu-e2k.text" = "Animatie vuurknop"; - -/* Class = "UILabel"; text = "Default"; ObjectID = "Gbx-kl-uOO"; */ -"Gbx-kl-uOO.text" = "Standaard"; - -/* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Cookiepop-ups beheren"; - -/* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ -"gS2-mg-l7R.title" = "Thema"; - -/* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ -"HLr-R8-xxF.text" = "Voorbeeldweergave bij lang indrukken"; - -/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ -"hoT-Nu-KXP.text" = "Browse privé met onze app voor Windows"; - /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Privacybescherming ingeschakeld voor alle sites"; /* Class = "UICollectionViewController"; title = "Icon"; ObjectID = "jbD-Oy-Cmw"; */ "jbD-Oy-Cmw.title" = "Pictogram"; -/* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ -"m23-t6-9cb.text" = "Feedback delen"; - -/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ -"mLn-1x-Fl5.title" = "Positie van adresbalk"; - -/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ -"mqV-pf-NZ1.text" = "Positie van adresbalk"; - /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Laat DuckDuckGo pop-ups voor cookietoestemming beheren"; @@ -181,36 +76,9 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Onbeschermde sites"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "Over DuckDuckGo"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Boven"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "Meer over DuckDuckGo"; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Synchronisatie en back-up"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Browse privé met onze app voor Mac "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Label"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "DuckDuckGo-app voor Windows"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Lettergrootte"; -/* Class = "UILabel"; text = "Private Voice Search"; ObjectID = "Swa-O7-n8W"; */ -"Swa-O7-n8W.text" = "Private spraakzoekopdracht"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Toetsenbord weergeven bij"; @@ -220,42 +88,18 @@ /* Class = "UITableViewSection"; headerTitle = "Action"; ObjectID = "U2M-6p-6nl"; */ "U2M-6p-6nl.headerTitle" = "Actie"; -/* Class = "UILabel"; text = "Autocomplete Suggestions"; ObjectID = "U8i-cQ-5WW"; */ -"U8i-cQ-5WW.text" = "Suggesties voor automatisch aanvullen weergeven"; - -/* Class = "UINavigationItem"; title = "Fire Button Animation"; ObjectID = "uns-8w-IwL"; Note = "Fire button animation setting page title"; */ -"uns-8w-IwL.title" = "Animatie vuurknop"; - /* Class = "UILabel"; text = "Remove All Fireproof Sites"; ObjectID = "UZx-52-aer"; */ "UZx-52-aer.text" = "Alle brandveilige websites verwijderen"; -/* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "vPz-uO-6gB"; */ -"vPz-uO-6gB.text" = "Global Privacy Control (GPC)"; - /* Class = "UITableViewSection"; footerTitle = "App exit is defined by swiping the app to close it while inactivity is when the app is in the background."; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.footerTitle" = "App afsluiten gebeurt door de app te swipen om deze te sluiten. Inactiviteit betekent dat de app op de achtergrond blijft."; /* Class = "UITableViewSection"; headerTitle = "Desired timing"; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.headerTitle" = "Gewenste timing"; -/* Class = "UILabel"; text = "Set as Default Browser"; ObjectID = "xof-5k-PkI"; */ -"xof-5k-PkI.text" = "Instellen als standaardbrowser"; - /* Class = "UINavigationItem"; title = "Fireproof Sites"; ObjectID = "xUX-nF-HOl"; */ "xUX-nF-HOl.title" = "Brandveilige websites"; -/* Class = "UILabel"; text = "Block email trackers and hide your address"; ObjectID = "Y6Y-wA-n6Z"; */ -"Y6Y-wA-n6Z.text" = "Blokkeer e-mailtrackers en verberg je adres"; - -/* Class = "UILabel"; text = "Keyboard"; ObjectID = "yoZ-jw-Cu3"; */ -"yoZ-jw-Cu3.text" = "Toetsenbord"; - -/* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ -"yvj-LL-MiR.text" = "App toevoegen aan je dock"; - -/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "DuckDuckGo-app voor Mac"; - /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Nieuw tabblad"; diff --git a/DuckDuckGo/pl.lproj/Localizable.strings b/DuckDuckGo/pl.lproj/Localizable.strings index 9ff0ac664b..3667ccf72c 100644 --- a/DuckDuckGo/pl.lproj/Localizable.strings +++ b/DuckDuckGo/pl.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Klawiatura"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Hasła"; + /* Settings title for the 'More' section */ "settings.more" = "Więcej możliwości DuckDuckGo"; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Podglądy po dłuższym przyciśnięciu"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Która witryna nie działa poprawnie?"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Kod odzyskiwania skopiowany do schowka"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Usuń dane serwera"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "Wszystkie urządzenia zostaną odłączone, a zsynchronizowane dane usunięte z serwera."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "Usunąć dane serwera?"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Usuń"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "„%@” nie będzie już mieć dostępu do zsynchronizowanych danych."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "Usunąć urządzenie?"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Usuń"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "To urządzenie nie będzie już mieć dostępu do zsynchronizowanych danych."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "Wyłączyć synchronizację?"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "Niestety synchronizacja i kopia zapasowa jest obecnie niedostępna. Spróbuj ponownie później."; diff --git a/DuckDuckGo/pl.lproj/Settings.strings b/DuckDuckGo/pl.lproj/Settings.strings index 5c028ba125..b75934c547 100644 --- a/DuckDuckGo/pl.lproj/Settings.strings +++ b/DuckDuckGo/pl.lproj/Settings.strings @@ -1,15 +1,3 @@ -/* Class = "UILabel"; text = "Fireproof Sites"; ObjectID = "0DQ-yq-UuT"; */ -"0DQ-yq-UuT.text" = "Bezpieczne witryny internetowe"; - -/* Class = "UITableViewSection"; footerTitle = "Disable to prevent links from automatically opening in other installed apps."; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.footerTitle" = "Wyłącz, aby uniemożliwić automatyczne otwieranie łączy w innych zainstalowanych aplikacjach."; - -/* Class = "UITableViewSection"; headerTitle = "Customize"; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.headerTitle" = "Spersonalizuj"; - -/* Class = "UILabel"; text = "Version"; ObjectID = "2ky-s9-1aZ"; */ -"2ky-s9-1aZ.text" = "Wersja"; - /* Class = "UINavigationItem"; title = "Keyboard"; ObjectID = "2pp-PM-6rW"; */ "2pp-PM-6rW.title" = "Klawiatura"; @@ -31,30 +19,9 @@ /* Class = "UILabel"; text = "Clear Tabs and Data"; ObjectID = "9fc-9r-4aA"; */ "9fc-9r-4aA.text" = "Wyczyść karty i dane"; -/* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ -"9Ko-0g-T3h.text" = "Rozmiar tekstu"; - -/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ -"9kt-6R-XiZ.text" = "Tytuł"; - /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Uruchomienie aplikacji"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Otwieraj łącza w powiązanych aplikacjach"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Menu debugowania"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Hasła"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Animacja"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "Ochrona poczty e-mail"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "Dane i/lub karty zostaną wyczyszczone po ponownym uruchomieniu aplikacji."; @@ -64,114 +31,42 @@ /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ "btj-ri-kRr.title" = "Zarządzaj okienkami z prośbą o zgodę"; -/* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ -"cKo-er-HNj.text" = "Ikona aplikacji"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "CR5-Al-WIW"; */ -"CR5-Al-WIW.text" = "Etykieta"; - /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Globalna Kontrola Prywatności (GPC)"; /* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ "CxT-QK-iVn.title" = "Dodaj"; -/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ -"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; - /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Zamknięcie aplikacji, brak aktywności przez 15 minut"; -/* Class = "UILabel"; text = "10.0.1 (Build 10005)"; ObjectID = "d5n-vG-8kF"; */ -"d5n-vG-8kF.text" = "10.0.1 (kompilacja 10005)"; - -/* Class = "UILabel"; text = "Application Lock"; ObjectID = "dBZ-yq-FYj"; */ -"dBZ-yq-FYj.text" = "Blokada aplikacji"; - -/* Class = "UITableViewSection"; headerTitle = "Appearance"; ObjectID = "dj9-vh-Rig"; */ -"dj9-vh-Rig.headerTitle" = "Wygląd"; - -/* Class = "UITableViewSection"; footerTitle = "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening."; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.footerTitle" = "Jeśli ustawiono Touch ID, Face ID lub hasło systemowe, pojawi się prośba o odblokowanie aplikacji podczas otwierania."; - -/* Class = "UITableViewSection"; headerTitle = "Privacy"; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.headerTitle" = "Prywatność"; - /* Class = "UILabel"; text = "Label"; ObjectID = "dud-qo-Ces"; */ "dud-qo-Ces.text" = "Etykieta"; -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Dyd-bm-goj"; */ -"Dyd-bm-goj.title" = "Ustawienia"; - -/* Class = "UILabel"; text = "100%"; ObjectID = "EB8-09-gt2"; */ -"EB8-09-gt2.text" = "100%"; - /* Class = "UILabel"; text = "Label"; ObjectID = "EIq-Ev-nfj"; */ "EIq-Ev-nfj.text" = "Etykieta"; -/* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "ekF-SJ-PAQ"; */ -"ekF-SJ-PAQ.text" = "Automatyczne czyszczenie danych"; - /* Class = "UILabel"; text = "App Exit, Inactive for 5 Minutes"; ObjectID = "ElX-yE-4PX"; */ "ElX-yE-4PX.text" = "Zamknięcie aplikacji, brak aktywności przez 5 minut"; /* Class = "UINavigationItem"; title = "App Icon"; ObjectID = "eMH-uv-Kms"; */ "eMH-uv-Kms.title" = "Ikona aplikacji"; -/* Class = "UILabel"; text = "Theme"; ObjectID = "f1O-6u-LFY"; */ -"f1O-6u-LFY.text" = "Motyw"; - /* Class = "UILabel"; text = "App Exit Only"; ObjectID = "Fal-1Y-o2S"; */ "Fal-1Y-o2S.text" = "Wyłącznie zamknięcie aplikacji"; /* Class = "UITableViewController"; title = "Automatically Clear Data"; ObjectID = "fdJ-b1-Des"; */ "fdJ-b1-Des.title" = "Automatyczne czyszczenie danych"; -/* Class = "UILabel"; text = "Unprotected Sites"; ObjectID = "FHC-1z-Z3v"; */ -"FHC-1z-Z3v.text" = "Niezabezpieczone witryny"; - -/* Class = "UITableViewSection"; headerTitle = "About"; ObjectID = "FpT-1C-xtx"; */ -"FpT-1C-xtx.headerTitle" = "Informacje"; - /* Class = "UITableViewController"; title = "Global Privacy Control (GPC)"; ObjectID = "fV3-86-QQj"; */ "fV3-86-QQj.title" = "Globalna Kontrola Prywatności (GPC)"; -/* Class = "UILabel"; text = "Add Widget to Home Screen"; ObjectID = "Fxu-zn-51Z"; */ -"Fxu-zn-51Z.text" = "Dodaj widżet do ekranu głównego"; - -/* Class = "UILabel"; text = "Fire Button Animation"; ObjectID = "gBo-Cu-e2k"; Note = "Fire button animation settings item"; */ -"gBo-Cu-e2k.text" = "Animacja przycisku ognia"; - -/* Class = "UILabel"; text = "Default"; ObjectID = "Gbx-kl-uOO"; */ -"Gbx-kl-uOO.text" = "Domyślny"; - -/* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Zarządzaj okienkami z prośbą o zgodę"; - -/* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ -"gS2-mg-l7R.title" = "Motyw"; - -/* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ -"HLr-R8-xxF.text" = "Podglądy po dłuższym przyciśnięciu"; - -/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ -"hoT-Nu-KXP.text" = "Przeglądaj prywatnie za pomocą naszej aplikacji dla systemu Windows"; - /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Ochrona prywatności włączona dla wszystkich witryn"; /* Class = "UICollectionViewController"; title = "Icon"; ObjectID = "jbD-Oy-Cmw"; */ "jbD-Oy-Cmw.title" = "Ikona"; -/* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ -"m23-t6-9cb.text" = "Podziel się opinią"; - -/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ -"mLn-1x-Fl5.title" = "Pozycja paska adresu"; - -/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ -"mqV-pf-NZ1.text" = "Pozycja paska adresu"; - /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Pozwól DuckDuckGo zarządzać wyskakującymi okienkami ze zgodą na używanie plików cookie"; @@ -181,36 +76,9 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Niezabezpieczone witryny"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "O DuckDuckGo"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Do góry"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "Więcej możliwości DuckDuckGo"; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Synchronizacja i kopia zapasowa"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Przeglądaj prywatnie za pomocą aplikacji dla komputerów Mac "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Etykieta"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "Aplikacja DuckDuckGo dla systemu Windows"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Rozmiar tekstu"; -/* Class = "UILabel"; text = "Private Voice Search"; ObjectID = "Swa-O7-n8W"; */ -"Swa-O7-n8W.text" = "Prywatne wyszukiwanie głosowe"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Pokaż klawiaturę"; @@ -220,42 +88,18 @@ /* Class = "UITableViewSection"; headerTitle = "Action"; ObjectID = "U2M-6p-6nl"; */ "U2M-6p-6nl.headerTitle" = "Działanie"; -/* Class = "UILabel"; text = "Autocomplete Suggestions"; ObjectID = "U8i-cQ-5WW"; */ -"U8i-cQ-5WW.text" = "Pokaż sugestie autouzupełniania"; - -/* Class = "UINavigationItem"; title = "Fire Button Animation"; ObjectID = "uns-8w-IwL"; Note = "Fire button animation setting page title"; */ -"uns-8w-IwL.title" = "Animacja przycisku ognia"; - /* Class = "UILabel"; text = "Remove All Fireproof Sites"; ObjectID = "UZx-52-aer"; */ "UZx-52-aer.text" = "Usuń wszystkie zabezpieczone strony"; -/* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "vPz-uO-6gB"; */ -"vPz-uO-6gB.text" = "Globalna Kontrola Prywatności (GPC)"; - /* Class = "UITableViewSection"; footerTitle = "App exit is defined by swiping the app to close it while inactivity is when the app is in the background."; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.footerTitle" = "Aplikacja jest zamknięta, gdy zostanie przesunięta poza ekran w celu jej zamknięcia, natomiast bezczynność aplikacji występuje, gdy aplikacja działa w tle."; /* Class = "UITableViewSection"; headerTitle = "Desired timing"; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.headerTitle" = "Wymagany czas"; -/* Class = "UILabel"; text = "Set as Default Browser"; ObjectID = "xof-5k-PkI"; */ -"xof-5k-PkI.text" = "Ustaw jako domyślną przeglądarkę"; - /* Class = "UINavigationItem"; title = "Fireproof Sites"; ObjectID = "xUX-nF-HOl"; */ "xUX-nF-HOl.title" = "Bezpieczne witryny internetowe"; -/* Class = "UILabel"; text = "Block email trackers and hide your address"; ObjectID = "Y6Y-wA-n6Z"; */ -"Y6Y-wA-n6Z.text" = "Zablokuj skrypty śledzące pocztę e-mail i ukryj swój adres"; - -/* Class = "UILabel"; text = "Keyboard"; ObjectID = "yoZ-jw-Cu3"; */ -"yoZ-jw-Cu3.text" = "Klawiatura"; - -/* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ -"yvj-LL-MiR.text" = "Dodaj aplikację do Docka"; - -/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "Aplikacja DuckDuckGo na komputery Mac"; - /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Nowa karta"; diff --git a/DuckDuckGo/pt.lproj/Localizable.strings b/DuckDuckGo/pt.lproj/Localizable.strings index ad0fb49df3..4324f1a86d 100644 --- a/DuckDuckGo/pt.lproj/Localizable.strings +++ b/DuckDuckGo/pt.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Teclado"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Palavras-passe"; + /* Settings title for the 'More' section */ "settings.more" = "Mais de DuckDuckGo"; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Pré-visualizações ao premir prolongadamente"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Que site está com falhas?"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Código de recuperação copiado para a área de transferência"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Eliminar dados do servidor"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "Todos os dispositivos serão desconectados e os dados sincronizados serão eliminados do servidor."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "Eliminar dados do servidor?"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Remover"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "\"%@\" deixará de poder aceder aos teus dados sincronizados."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "Remover dispositivo?"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Remover"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "Este dispositivo deixará de poder aceder aos teus dados sincronizados."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "Desativar a sincronização?"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "Lamentamos, mas a sincronização e cópia de segurança não estão disponíveis de momento. Tenta novamente mais tarde."; diff --git a/DuckDuckGo/pt.lproj/Settings.strings b/DuckDuckGo/pt.lproj/Settings.strings index affa649c82..1c4761d9bb 100644 --- a/DuckDuckGo/pt.lproj/Settings.strings +++ b/DuckDuckGo/pt.lproj/Settings.strings @@ -1,15 +1,3 @@ -/* Class = "UILabel"; text = "Fireproof Sites"; ObjectID = "0DQ-yq-UuT"; */ -"0DQ-yq-UuT.text" = "Sites com barreira de segurança"; - -/* Class = "UITableViewSection"; footerTitle = "Disable to prevent links from automatically opening in other installed apps."; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.footerTitle" = "Desative para prevenir que ligações sejam abertas automaticamente noutras aplicações instaladas."; - -/* Class = "UITableViewSection"; headerTitle = "Customize"; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.headerTitle" = "Personalizar"; - -/* Class = "UILabel"; text = "Version"; ObjectID = "2ky-s9-1aZ"; */ -"2ky-s9-1aZ.text" = "Versão"; - /* Class = "UINavigationItem"; title = "Keyboard"; ObjectID = "2pp-PM-6rW"; */ "2pp-PM-6rW.title" = "Teclado"; @@ -31,30 +19,9 @@ /* Class = "UILabel"; text = "Clear Tabs and Data"; ObjectID = "9fc-9r-4aA"; */ "9fc-9r-4aA.text" = "Limpar separadores e dados"; -/* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ -"9Ko-0g-T3h.text" = "Tamanho do texto"; - -/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ -"9kt-6R-XiZ.text" = "Título"; - /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Abertura das aplicações"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Abrir links nas aplicações associadas"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Menu de depuração"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Palavras-passe"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Animação"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "Proteção de e-mail"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "Os dados e/ou separadores serão apagados após a reinicialização da aplicação."; @@ -64,114 +31,42 @@ /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ "btj-ri-kRr.title" = "Gerir pop-ups de cookies"; -/* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ -"cKo-er-HNj.text" = "Ícone da aplicação"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "CR5-Al-WIW"; */ -"CR5-Al-WIW.text" = "Rótulo"; - /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Controlo Global de Privacidade (CGP)"; /* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ "CxT-QK-iVn.title" = "Adicionar"; -/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ -"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; - /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Saída da aplicação, inativa durante 15 minutos"; -/* Class = "UILabel"; text = "10.0.1 (Build 10005)"; ObjectID = "d5n-vG-8kF"; */ -"d5n-vG-8kF.text" = "10.0.1 (Compilação 10005)"; - -/* Class = "UILabel"; text = "Application Lock"; ObjectID = "dBZ-yq-FYj"; */ -"dBZ-yq-FYj.text" = "Bloqueio de aplicações"; - -/* Class = "UITableViewSection"; headerTitle = "Appearance"; ObjectID = "dj9-vh-Rig"; */ -"dj9-vh-Rig.headerTitle" = "Aparência"; - -/* Class = "UITableViewSection"; footerTitle = "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening."; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.footerTitle" = "Se o Touch ID, Face ID ou um código de acesso estiverem definidos, ser-lhe-á pedido o desbloqueio da aplicação ao abrir."; - -/* Class = "UITableViewSection"; headerTitle = "Privacy"; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.headerTitle" = "Privacidade"; - /* Class = "UILabel"; text = "Label"; ObjectID = "dud-qo-Ces"; */ "dud-qo-Ces.text" = "Rótulo"; -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Dyd-bm-goj"; */ -"Dyd-bm-goj.title" = "Definições"; - -/* Class = "UILabel"; text = "100%"; ObjectID = "EB8-09-gt2"; */ -"EB8-09-gt2.text" = "100%"; - /* Class = "UILabel"; text = "Label"; ObjectID = "EIq-Ev-nfj"; */ "EIq-Ev-nfj.text" = "Rótulo"; -/* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "ekF-SJ-PAQ"; */ -"ekF-SJ-PAQ.text" = "Limpar os dados automaticamente"; - /* Class = "UILabel"; text = "App Exit, Inactive for 5 Minutes"; ObjectID = "ElX-yE-4PX"; */ "ElX-yE-4PX.text" = "Saída da aplicação, inativa durante 5 minutos"; /* Class = "UINavigationItem"; title = "App Icon"; ObjectID = "eMH-uv-Kms"; */ "eMH-uv-Kms.title" = "Ícone da aplicação"; -/* Class = "UILabel"; text = "Theme"; ObjectID = "f1O-6u-LFY"; */ -"f1O-6u-LFY.text" = "Tema"; - /* Class = "UILabel"; text = "App Exit Only"; ObjectID = "Fal-1Y-o2S"; */ "Fal-1Y-o2S.text" = "Apenas saída da aplicação"; /* Class = "UITableViewController"; title = "Automatically Clear Data"; ObjectID = "fdJ-b1-Des"; */ "fdJ-b1-Des.title" = "Limpar os dados automaticamente"; -/* Class = "UILabel"; text = "Unprotected Sites"; ObjectID = "FHC-1z-Z3v"; */ -"FHC-1z-Z3v.text" = "Sites desprotegidos"; - -/* Class = "UITableViewSection"; headerTitle = "About"; ObjectID = "FpT-1C-xtx"; */ -"FpT-1C-xtx.headerTitle" = "Acerca de"; - /* Class = "UITableViewController"; title = "Global Privacy Control (GPC)"; ObjectID = "fV3-86-QQj"; */ "fV3-86-QQj.title" = "Controlo Global de Privacidade (CGP)"; -/* Class = "UILabel"; text = "Add Widget to Home Screen"; ObjectID = "Fxu-zn-51Z"; */ -"Fxu-zn-51Z.text" = "Adicionar widget ao ecrã inicial"; - -/* Class = "UILabel"; text = "Fire Button Animation"; ObjectID = "gBo-Cu-e2k"; Note = "Fire button animation settings item"; */ -"gBo-Cu-e2k.text" = "Animação do Botão de Fogo"; - -/* Class = "UILabel"; text = "Default"; ObjectID = "Gbx-kl-uOO"; */ -"Gbx-kl-uOO.text" = "Predefinido"; - -/* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Gerir pop-ups de cookies"; - -/* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ -"gS2-mg-l7R.title" = "Tema"; - -/* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ -"HLr-R8-xxF.text" = "Pré-visualizações ao premir prolongadamente"; - -/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ -"hoT-Nu-KXP.text" = "Navegue em privado com a nossa aplicação para Windows"; - /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Proteção de Privacidade ativa para todos os sites"; /* Class = "UICollectionViewController"; title = "Icon"; ObjectID = "jbD-Oy-Cmw"; */ "jbD-Oy-Cmw.title" = "Ícone"; -/* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ -"m23-t6-9cb.text" = "Partilhar comentários"; - -/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ -"mLn-1x-Fl5.title" = "Posição da barra de endereços"; - -/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ -"mqV-pf-NZ1.text" = "Posição da barra de endereços"; - /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Permitir que o DuckDuckGo faça a gestão dos pop-ups de consentimento de cookies"; @@ -181,36 +76,9 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Sites desprotegidos"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "Sobre o DuckDuckGo"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Topo"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "Mais da DuckDuckGo"; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Sincronização e cópia de segurança"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Navegue em privado com a nossa aplicação para Mac "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Rótulo"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "Aplicação DuckDuckGo para Windows"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Tamanho do texto"; -/* Class = "UILabel"; text = "Private Voice Search"; ObjectID = "Swa-O7-n8W"; */ -"Swa-O7-n8W.text" = "Pesquisa por voz privada"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Mostrar teclado em"; @@ -220,42 +88,18 @@ /* Class = "UITableViewSection"; headerTitle = "Action"; ObjectID = "U2M-6p-6nl"; */ "U2M-6p-6nl.headerTitle" = "Ação"; -/* Class = "UILabel"; text = "Autocomplete Suggestions"; ObjectID = "U8i-cQ-5WW"; */ -"U8i-cQ-5WW.text" = "Mostrar sugestões de preenchimento automático"; - -/* Class = "UINavigationItem"; title = "Fire Button Animation"; ObjectID = "uns-8w-IwL"; Note = "Fire button animation setting page title"; */ -"uns-8w-IwL.title" = "Animação do Botão de Fogo"; - /* Class = "UILabel"; text = "Remove All Fireproof Sites"; ObjectID = "UZx-52-aer"; */ "UZx-52-aer.text" = "Remover todos os sites com barreira de segurança"; -/* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "vPz-uO-6gB"; */ -"vPz-uO-6gB.text" = "Controlo Global de Privacidade (CGP)"; - /* Class = "UITableViewSection"; footerTitle = "App exit is defined by swiping the app to close it while inactivity is when the app is in the background."; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.footerTitle" = "A saída da aplicação é definida deslizando a aplicação para a fechar e fica inativa quando a aplicação está em segundo plano."; /* Class = "UITableViewSection"; headerTitle = "Desired timing"; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.headerTitle" = "Tempo desejado"; -/* Class = "UILabel"; text = "Set as Default Browser"; ObjectID = "xof-5k-PkI"; */ -"xof-5k-PkI.text" = "Definir como navegador padrão"; - /* Class = "UINavigationItem"; title = "Fireproof Sites"; ObjectID = "xUX-nF-HOl"; */ "xUX-nF-HOl.title" = "Sites com barreira de segurança"; -/* Class = "UILabel"; text = "Block email trackers and hide your address"; ObjectID = "Y6Y-wA-n6Z"; */ -"Y6Y-wA-n6Z.text" = "Bloqueie rastreadores de e-mail e oculte o seu endereço."; - -/* Class = "UILabel"; text = "Keyboard"; ObjectID = "yoZ-jw-Cu3"; */ -"yoZ-jw-Cu3.text" = "Teclado"; - -/* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ -"yvj-LL-MiR.text" = "Adicionar aplicação à sua dock"; - -/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "Aplicação DuckDuckGo para Mac"; - /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Novo separador"; diff --git a/DuckDuckGo/ro.lproj/Localizable.strings b/DuckDuckGo/ro.lproj/Localizable.strings index 5f740fee41..370c94d16f 100644 --- a/DuckDuckGo/ro.lproj/Localizable.strings +++ b/DuckDuckGo/ro.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Tastatură"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Parole"; + /* Settings title for the 'More' section */ "settings.more" = "Mai multe de la DuckDuckGo"; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Previzualizări prin apăsare lungă"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Ce site este defect?"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Codul de recuperare a fost copiat în clipboard"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Șterge datele serverului"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "Toate dispozitivele vor fi deconectate, iar datele tale sincronizate vor fi șterse de pe server."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "Ștergi datele serverului?"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Elimină"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "„%@” nu va mai putea accesa datele tale sincronizate."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "Elimini dispozitivul?"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Elimină"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "Acest dispozitiv nu va mai putea accesa datele tale sincronizate."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "Oprești sincronizarea?"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "Ne pare rău, dar Sincronizarea și copierea de rezervă nu este disponibilă momentan. Încearcă din nou mai târziu."; diff --git a/DuckDuckGo/ro.lproj/Settings.strings b/DuckDuckGo/ro.lproj/Settings.strings index 84c4985147..7a72b0fcf1 100644 --- a/DuckDuckGo/ro.lproj/Settings.strings +++ b/DuckDuckGo/ro.lproj/Settings.strings @@ -1,15 +1,3 @@ -/* Class = "UILabel"; text = "Fireproof Sites"; ObjectID = "0DQ-yq-UuT"; */ -"0DQ-yq-UuT.text" = "Site-uri cu ștergerea activității și istoricului din browser la ieșire"; - -/* Class = "UITableViewSection"; footerTitle = "Disable to prevent links from automatically opening in other installed apps."; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.footerTitle" = "Dezactivează pentru a preveni deschiderea automată a linkurilor în alte aplicații instalate."; - -/* Class = "UITableViewSection"; headerTitle = "Customize"; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.headerTitle" = "Personalizare"; - -/* Class = "UILabel"; text = "Version"; ObjectID = "2ky-s9-1aZ"; */ -"2ky-s9-1aZ.text" = "Versiune"; - /* Class = "UINavigationItem"; title = "Keyboard"; ObjectID = "2pp-PM-6rW"; */ "2pp-PM-6rW.title" = "Tastatură"; @@ -31,30 +19,9 @@ /* Class = "UILabel"; text = "Clear Tabs and Data"; ObjectID = "9fc-9r-4aA"; */ "9fc-9r-4aA.text" = "Șterge filele și datele"; -/* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ -"9Ko-0g-T3h.text" = "Dimensiune text"; - -/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ -"9kt-6R-XiZ.text" = "Titlu"; - /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Lansare aplicație"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Deschide linkuri în aplicațiile asociate"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Meniul de depanare"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Parole"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Animație"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "Protecția comunicațiilor prin e-mail"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "Datele și/sau filele vor fi șterse la repornirea aplicației."; @@ -64,114 +31,42 @@ /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ "btj-ri-kRr.title" = "Gestionare pop-up-uri pentru module cookie"; -/* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ -"cKo-er-HNj.text" = "Pictograma aplicației"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "CR5-Al-WIW"; */ -"CR5-Al-WIW.text" = "Label"; - /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Global Privacy Control (GPC)"; /* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ "CxT-QK-iVn.title" = "Adăugați"; -/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ -"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; - /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Ieșire din aplicație, inactiv timp de 15 minute"; -/* Class = "UILabel"; text = "10.0.1 (Build 10005)"; ObjectID = "d5n-vG-8kF"; */ -"d5n-vG-8kF.text" = "10.0.1 (Versiune 10005)"; - -/* Class = "UILabel"; text = "Application Lock"; ObjectID = "dBZ-yq-FYj"; */ -"dBZ-yq-FYj.text" = "Blocarea aplicației"; - -/* Class = "UITableViewSection"; headerTitle = "Appearance"; ObjectID = "dj9-vh-Rig"; */ -"dj9-vh-Rig.headerTitle" = "Aspect"; - -/* Class = "UITableViewSection"; footerTitle = "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening."; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.footerTitle" = "Dacă este setat Touch ID, Face ID sau o parolă de sistem, ți se va solicita să deblochezi aplicația la deschidere."; - -/* Class = "UITableViewSection"; headerTitle = "Privacy"; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.headerTitle" = "Confidențialitate"; - /* Class = "UILabel"; text = "Label"; ObjectID = "dud-qo-Ces"; */ "dud-qo-Ces.text" = "Label"; -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Dyd-bm-goj"; */ -"Dyd-bm-goj.title" = "Setări"; - -/* Class = "UILabel"; text = "100%"; ObjectID = "EB8-09-gt2"; */ -"EB8-09-gt2.text" = "100%"; - /* Class = "UILabel"; text = "Label"; ObjectID = "EIq-Ev-nfj"; */ "EIq-Ev-nfj.text" = "Label"; -/* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "ekF-SJ-PAQ"; */ -"ekF-SJ-PAQ.text" = "Șterge automat datele"; - /* Class = "UILabel"; text = "App Exit, Inactive for 5 Minutes"; ObjectID = "ElX-yE-4PX"; */ "ElX-yE-4PX.text" = "Ieșire din aplicație, inactiv timp de 5 minute"; /* Class = "UINavigationItem"; title = "App Icon"; ObjectID = "eMH-uv-Kms"; */ "eMH-uv-Kms.title" = "Pictograma aplicației"; -/* Class = "UILabel"; text = "Theme"; ObjectID = "f1O-6u-LFY"; */ -"f1O-6u-LFY.text" = "Temă"; - /* Class = "UILabel"; text = "App Exit Only"; ObjectID = "Fal-1Y-o2S"; */ "Fal-1Y-o2S.text" = "Doar ieșire din aplicație"; /* Class = "UITableViewController"; title = "Automatically Clear Data"; ObjectID = "fdJ-b1-Des"; */ "fdJ-b1-Des.title" = "Șterge automat datele"; -/* Class = "UILabel"; text = "Unprotected Sites"; ObjectID = "FHC-1z-Z3v"; */ -"FHC-1z-Z3v.text" = "Site-uri neprotejate"; - -/* Class = "UITableViewSection"; headerTitle = "About"; ObjectID = "FpT-1C-xtx"; */ -"FpT-1C-xtx.headerTitle" = "Despre"; - /* Class = "UITableViewController"; title = "Global Privacy Control (GPC)"; ObjectID = "fV3-86-QQj"; */ "fV3-86-QQj.title" = "Global Privacy Control (GPC)"; -/* Class = "UILabel"; text = "Add Widget to Home Screen"; ObjectID = "Fxu-zn-51Z"; */ -"Fxu-zn-51Z.text" = "Adaugă un widget la ecranul de întâmpinare"; - -/* Class = "UILabel"; text = "Fire Button Animation"; ObjectID = "gBo-Cu-e2k"; Note = "Fire button animation settings item"; */ -"gBo-Cu-e2k.text" = "Animație buton Foc"; - -/* Class = "UILabel"; text = "Default"; ObjectID = "Gbx-kl-uOO"; */ -"Gbx-kl-uOO.text" = "Implicită"; - -/* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Gestionare pop-up-uri pentru module cookie"; - -/* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ -"gS2-mg-l7R.title" = "Temă"; - -/* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ -"HLr-R8-xxF.text" = "Previzualizări prin apăsare lungă"; - -/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ -"hoT-Nu-KXP.text" = "Navighează în mod privat cu aplicația noastră pentru Windows"; - /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Protecția confidențialității este activată pentru toate site-urile"; /* Class = "UICollectionViewController"; title = "Icon"; ObjectID = "jbD-Oy-Cmw"; */ "jbD-Oy-Cmw.title" = "Pictogramă"; -/* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ -"m23-t6-9cb.text" = "Partajează feedback"; - -/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ -"mLn-1x-Fl5.title" = "Poziția barei de adrese"; - -/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ -"mqV-pf-NZ1.text" = "Poziția barei de adrese"; - /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Lasă DuckDuckGo să gestioneze ferestrele pop-up de solicitare a consimțământului cu privire la modulele cookie"; @@ -181,36 +76,9 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Site-uri neprotejate"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "Despre DuckDuckGo"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Sus"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "Mai multe de la DuckDuckGo"; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Sincronizare și copiere de rezervă"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Răsfoiește în mod privat, cu aplicația noastră pentru Mac "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Label"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "Aplicația DuckDuckGo pentru Windows"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Dimensiune text"; -/* Class = "UILabel"; text = "Private Voice Search"; ObjectID = "Swa-O7-n8W"; */ -"Swa-O7-n8W.text" = "Căutare vocală privată"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Afișează tastatura la"; @@ -220,42 +88,18 @@ /* Class = "UITableViewSection"; headerTitle = "Action"; ObjectID = "U2M-6p-6nl"; */ "U2M-6p-6nl.headerTitle" = "Acțiune"; -/* Class = "UILabel"; text = "Autocomplete Suggestions"; ObjectID = "U8i-cQ-5WW"; */ -"U8i-cQ-5WW.text" = "Afișează sugestiile de completare automată"; - -/* Class = "UINavigationItem"; title = "Fire Button Animation"; ObjectID = "uns-8w-IwL"; Note = "Fire button animation setting page title"; */ -"uns-8w-IwL.title" = "Animație buton Foc"; - /* Class = "UILabel"; text = "Remove All Fireproof Sites"; ObjectID = "UZx-52-aer"; */ "UZx-52-aer.text" = "Elimină toate site-urile la care la ieșire se șterge activitatea și istoricul din browser"; -/* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "vPz-uO-6gB"; */ -"vPz-uO-6gB.text" = "Global Privacy Control (GPC)"; - /* Class = "UITableViewSection"; footerTitle = "App exit is defined by swiping the app to close it while inactivity is when the app is in the background."; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.footerTitle" = "Ieșirea aplicației este definită prin glisarea aplicației pentru a o închide în timp ce inactivitatea este atunci când aplicația este în fundal."; /* Class = "UITableViewSection"; headerTitle = "Desired timing"; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.headerTitle" = "Calendarul dorit"; -/* Class = "UILabel"; text = "Set as Default Browser"; ObjectID = "xof-5k-PkI"; */ -"xof-5k-PkI.text" = "Setare ca browser implicit"; - /* Class = "UINavigationItem"; title = "Fireproof Sites"; ObjectID = "xUX-nF-HOl"; */ "xUX-nF-HOl.title" = "Site-uri cu ștergerea activității și istoricului din browser la ieșire"; -/* Class = "UILabel"; text = "Block email trackers and hide your address"; ObjectID = "Y6Y-wA-n6Z"; */ -"Y6Y-wA-n6Z.text" = "Blochează tehnologiile de urmărire prin e-mail și ascunde-ți adresa"; - -/* Class = "UILabel"; text = "Keyboard"; ObjectID = "yoZ-jw-Cu3"; */ -"yoZ-jw-Cu3.text" = "Tastatură"; - -/* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ -"yvj-LL-MiR.text" = "Adaugă aplicația la secțiunea ta fixă"; - -/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "Aplicația DuckDuckGo pentru Mac"; - /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Filă nouă"; diff --git a/DuckDuckGo/ru.lproj/Localizable.strings b/DuckDuckGo/ru.lproj/Localizable.strings index 5dd4826081..d63d876074 100644 --- a/DuckDuckGo/ru.lproj/Localizable.strings +++ b/DuckDuckGo/ru.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Клавиатура"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Пароли"; + /* Settings title for the 'More' section */ "settings.more" = "DuckDuckGo также предлагает..."; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Предпросмотр долгим нажатием"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "На каком сайте возникает проблема?"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Код восстановления скопирован в буфер обмена"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Удалить данные с сервера"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "Привязка устройств будет отключена, а синхронизированные данные — удалены с сервера."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "Удалить данные с сервера?"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Удалить"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "«%@» утратит доступ к синхронизированным данным."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "Удалить устройство?"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Удалить"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "Это устройство утратит доступ к вашим синхронизированным данным."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "Отключить синхронизацию?"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "Синхронизация и резервное копирование сейчас недоступны. Повторите попытку позже."; diff --git a/DuckDuckGo/ru.lproj/Settings.strings b/DuckDuckGo/ru.lproj/Settings.strings index 1db03c4184..fa13ed39ac 100644 --- a/DuckDuckGo/ru.lproj/Settings.strings +++ b/DuckDuckGo/ru.lproj/Settings.strings @@ -1,15 +1,3 @@ -/* Class = "UILabel"; text = "Fireproof Sites"; ObjectID = "0DQ-yq-UuT"; */ -"0DQ-yq-UuT.text" = "Огнеупорные сайты"; - -/* Class = "UITableViewSection"; footerTitle = "Disable to prevent links from automatically opening in other installed apps."; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.footerTitle" = "Отключение этой функции не позволит ссылкам автоматически открываться в других приложениях."; - -/* Class = "UITableViewSection"; headerTitle = "Customize"; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.headerTitle" = "Собственная настройка"; - -/* Class = "UILabel"; text = "Version"; ObjectID = "2ky-s9-1aZ"; */ -"2ky-s9-1aZ.text" = "Версия"; - /* Class = "UINavigationItem"; title = "Keyboard"; ObjectID = "2pp-PM-6rW"; */ "2pp-PM-6rW.title" = "Клавиатура"; @@ -31,30 +19,9 @@ /* Class = "UILabel"; text = "Clear Tabs and Data"; ObjectID = "9fc-9r-4aA"; */ "9fc-9r-4aA.text" = "Сбросить вкладки и данные"; -/* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ -"9Ko-0g-T3h.text" = "Размер текста"; - -/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ -"9kt-6R-XiZ.text" = "Название"; - /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "При запуске приложения"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Открывать ссылки в связанных приложениях"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Меню отладки"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Пароли"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Анимация"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "Защита электронной почты"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "Данные и/или вкладки будут сброшены при следующем запуске приложения."; @@ -64,114 +31,42 @@ /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ "btj-ri-kRr.title" = "Управление окнами выбора куки-файлов"; -/* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ -"cKo-er-HNj.text" = "Значок приложения"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "CR5-Al-WIW"; */ -"CR5-Al-WIW.text" = "Метка"; - /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Глобальный контроль конфиденциальности (GPC)"; /* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ "CxT-QK-iVn.title" = "Добавить"; -/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ -"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; - /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "После выхода из приложения, через 15 минут бездействия"; -/* Class = "UILabel"; text = "10.0.1 (Build 10005)"; ObjectID = "d5n-vG-8kF"; */ -"d5n-vG-8kF.text" = "10.0.1 (сборка 10005)"; - -/* Class = "UILabel"; text = "Application Lock"; ObjectID = "dBZ-yq-FYj"; */ -"dBZ-yq-FYj.text" = "Блокировка"; - -/* Class = "UITableViewSection"; headerTitle = "Appearance"; ObjectID = "dj9-vh-Rig"; */ -"dj9-vh-Rig.headerTitle" = "Внешний вид"; - -/* Class = "UITableViewSection"; footerTitle = "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening."; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.footerTitle" = "Если система защищена технологией Touch ID или Face ID либо кодом доступа, при запуске вам придется разблокировать приложение."; - -/* Class = "UITableViewSection"; headerTitle = "Privacy"; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.headerTitle" = "Настройки конфиденциальности"; - /* Class = "UILabel"; text = "Label"; ObjectID = "dud-qo-Ces"; */ "dud-qo-Ces.text" = "Метка"; -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Dyd-bm-goj"; */ -"Dyd-bm-goj.title" = "Настройки"; - -/* Class = "UILabel"; text = "100%"; ObjectID = "EB8-09-gt2"; */ -"EB8-09-gt2.text" = "100 %"; - /* Class = "UILabel"; text = "Label"; ObjectID = "EIq-Ev-nfj"; */ "EIq-Ev-nfj.text" = "Метка"; -/* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "ekF-SJ-PAQ"; */ -"ekF-SJ-PAQ.text" = "Автоудаление данных"; - /* Class = "UILabel"; text = "App Exit, Inactive for 5 Minutes"; ObjectID = "ElX-yE-4PX"; */ "ElX-yE-4PX.text" = "После выхода из приложения, через 5 минут бездействия"; /* Class = "UINavigationItem"; title = "App Icon"; ObjectID = "eMH-uv-Kms"; */ "eMH-uv-Kms.title" = "Значок приложения"; -/* Class = "UILabel"; text = "Theme"; ObjectID = "f1O-6u-LFY"; */ -"f1O-6u-LFY.text" = "Тема"; - /* Class = "UILabel"; text = "App Exit Only"; ObjectID = "Fal-1Y-o2S"; */ "Fal-1Y-o2S.text" = "Только после выхода из приложения"; /* Class = "UITableViewController"; title = "Automatically Clear Data"; ObjectID = "fdJ-b1-Des"; */ "fdJ-b1-Des.title" = "Автоудаление данных"; -/* Class = "UILabel"; text = "Unprotected Sites"; ObjectID = "FHC-1z-Z3v"; */ -"FHC-1z-Z3v.text" = "Незащищенные сайты"; - -/* Class = "UITableViewSection"; headerTitle = "About"; ObjectID = "FpT-1C-xtx"; */ -"FpT-1C-xtx.headerTitle" = "О нас"; - /* Class = "UITableViewController"; title = "Global Privacy Control (GPC)"; ObjectID = "fV3-86-QQj"; */ "fV3-86-QQj.title" = "Глобальный контроль конфиденциальности (GPC)"; -/* Class = "UILabel"; text = "Add Widget to Home Screen"; ObjectID = "Fxu-zn-51Z"; */ -"Fxu-zn-51Z.text" = "Добавьте виджет на домашний экран"; - -/* Class = "UILabel"; text = "Fire Button Animation"; ObjectID = "gBo-Cu-e2k"; Note = "Fire button animation settings item"; */ -"gBo-Cu-e2k.text" = "Aнимация кнопки «Огонь»"; - -/* Class = "UILabel"; text = "Default"; ObjectID = "Gbx-kl-uOO"; */ -"Gbx-kl-uOO.text" = "По умолчанию"; - -/* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Управление окнами выбора куки-файлов"; - -/* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ -"gS2-mg-l7R.title" = "Тема"; - -/* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ -"HLr-R8-xxF.text" = "Предпросмотр долгим нажатием"; - -/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ -"hoT-Nu-KXP.text" = "Полная конфиденциальность в Интернете, благодаря нашему приложению для Windows"; - /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Защита конфиденциальности включена на всех сайтах"; /* Class = "UICollectionViewController"; title = "Icon"; ObjectID = "jbD-Oy-Cmw"; */ "jbD-Oy-Cmw.title" = "Значок"; -/* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ -"m23-t6-9cb.text" = "Оставьте нам отзыв"; - -/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ -"mLn-1x-Fl5.title" = "Положение адресной строки"; - -/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ -"mqV-pf-NZ1.text" = "Положение адресной строки"; - /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Разрешить DuckDuckGo управлять окнами выбора куки-файлов"; @@ -181,36 +76,9 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Незащищенные сайты"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "Несколько слов о DuckDuckGo"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Вверх"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "DuckDuckGo также предлагает..."; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Синхронизация и резервное копирование"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Интернет без трекеров с нашим приложением для Mac "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Метка"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "Приложение DuckDuckGo для Windows"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Размер текста"; -/* Class = "UILabel"; text = "Private Voice Search"; ObjectID = "Swa-O7-n8W"; */ -"Swa-O7-n8W.text" = "Конфиденциальный голосовой поиск"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Показывать клавиатуру"; @@ -220,42 +88,18 @@ /* Class = "UITableViewSection"; headerTitle = "Action"; ObjectID = "U2M-6p-6nl"; */ "U2M-6p-6nl.headerTitle" = "Действие"; -/* Class = "UILabel"; text = "Autocomplete Suggestions"; ObjectID = "U8i-cQ-5WW"; */ -"U8i-cQ-5WW.text" = "Показать предложения автозаполнения"; - -/* Class = "UINavigationItem"; title = "Fire Button Animation"; ObjectID = "uns-8w-IwL"; Note = "Fire button animation setting page title"; */ -"uns-8w-IwL.title" = "Анимация кнопки «Огонь»"; - /* Class = "UILabel"; text = "Remove All Fireproof Sites"; ObjectID = "UZx-52-aer"; */ "UZx-52-aer.text" = "Удалить все огнеупорные сайты"; -/* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "vPz-uO-6gB"; */ -"vPz-uO-6gB.text" = "Глобальный контроль конфиденциальности (GPC)"; - /* Class = "UITableViewSection"; footerTitle = "App exit is defined by swiping the app to close it while inactivity is when the app is in the background."; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.footerTitle" = "Под выходом из приложения подразумевается закрытие свайпом, а под бездействием — работа в фоновом режиме."; /* Class = "UITableViewSection"; headerTitle = "Desired timing"; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.headerTitle" = "Предпочитаемое время"; -/* Class = "UILabel"; text = "Set as Default Browser"; ObjectID = "xof-5k-PkI"; */ -"xof-5k-PkI.text" = "Сделать браузером по умолчанию"; - /* Class = "UINavigationItem"; title = "Fireproof Sites"; ObjectID = "xUX-nF-HOl"; */ "xUX-nF-HOl.title" = "Огнеупорные сайты"; -/* Class = "UILabel"; text = "Block email trackers and hide your address"; ObjectID = "Y6Y-wA-n6Z"; */ -"Y6Y-wA-n6Z.text" = "Блокировка почтовых трекеров и скрытие адреса"; - -/* Class = "UILabel"; text = "Keyboard"; ObjectID = "yoZ-jw-Cu3"; */ -"yoZ-jw-Cu3.text" = "Клавиатура"; - -/* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ -"yvj-LL-MiR.text" = "Добавьте приложение на док-панель"; - -/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "Приложение DuckDuckGo для Mac"; - /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Новая вкладка"; diff --git a/DuckDuckGo/sk.lproj/Localizable.strings b/DuckDuckGo/sk.lproj/Localizable.strings index d57cf782c6..09f96b80ca 100644 --- a/DuckDuckGo/sk.lproj/Localizable.strings +++ b/DuckDuckGo/sk.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Klávesnica"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Heslo"; + /* Settings title for the 'More' section */ "settings.more" = "Viac od DuckDuckGo"; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Náhľady po dlhodobom stlačení"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Ktorá webová stránka je nefunkčná?"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Kód obnovenia bol skopírovaný do schránky"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Zmazanie údajov servera"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "Všetky zariadenia budú odpojené a vaše synchronizované údaje budú zmazané zo servera."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "Zmazať údaje servera?"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Odstrániť"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "\"%@\" už nebude mať prístup k synchronizovaným údajom."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "Odstrániť zariadenie?"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Odstrániť"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "Toto zariadenie už nebude mať prístup k synchronizovaným údajom."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "Chcete vypnúť synchronizáciu?"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "Ľutujeme, ale synchronizácia a zálohovanie momentálne nie sú k dispozícii. Prosím, skúste to neskôr znova."; diff --git a/DuckDuckGo/sk.lproj/Settings.strings b/DuckDuckGo/sk.lproj/Settings.strings index 6be76e6798..625a14ec00 100644 --- a/DuckDuckGo/sk.lproj/Settings.strings +++ b/DuckDuckGo/sk.lproj/Settings.strings @@ -1,15 +1,3 @@ -/* Class = "UILabel"; text = "Fireproof Sites"; ObjectID = "0DQ-yq-UuT"; */ -"0DQ-yq-UuT.text" = "Zabezpečené stránky"; - -/* Class = "UITableViewSection"; footerTitle = "Disable to prevent links from automatically opening in other installed apps."; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.footerTitle" = "Zakážte a zabráňte tak automatickému otváraniu odkazov v iných nainštalovaných aplikáciách."; - -/* Class = "UITableViewSection"; headerTitle = "Customize"; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.headerTitle" = "Prispôsobiť"; - -/* Class = "UILabel"; text = "Version"; ObjectID = "2ky-s9-1aZ"; */ -"2ky-s9-1aZ.text" = "Verzia"; - /* Class = "UINavigationItem"; title = "Keyboard"; ObjectID = "2pp-PM-6rW"; */ "2pp-PM-6rW.title" = "Klávesnica"; @@ -31,30 +19,9 @@ /* Class = "UILabel"; text = "Clear Tabs and Data"; ObjectID = "9fc-9r-4aA"; */ "9fc-9r-4aA.text" = "Vymazať karty a údaje"; -/* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ -"9Ko-0g-T3h.text" = "Veľkosť textu"; - -/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ -"9kt-6R-XiZ.text" = "Názov"; - /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Spustenie aplikácie"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Otvárať odkazy v pridružených aplikáciách"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Ponuka ladenia"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Heslo"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Animácia"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "Ochrana e-mailu"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "Údaje a/alebo karty sa vymažú po reštartovaní aplikácie."; @@ -64,114 +31,42 @@ /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ "btj-ri-kRr.title" = "Správa kontextových okien súborov cookie"; -/* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ -"cKo-er-HNj.text" = "Ikona aplikácie"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "CR5-Al-WIW"; */ -"CR5-Al-WIW.text" = "Označenie"; - /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Globálna kontrola súkromia (GPC)"; /* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ "CxT-QK-iVn.title" = "Pridať"; -/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ -"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; - /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Ukončiť 15 minút neaktívnu aplikáciu"; -/* Class = "UILabel"; text = "10.0.1 (Build 10005)"; ObjectID = "d5n-vG-8kF"; */ -"d5n-vG-8kF.text" = "10.0.1 (Build 10005)"; - -/* Class = "UILabel"; text = "Application Lock"; ObjectID = "dBZ-yq-FYj"; */ -"dBZ-yq-FYj.text" = "Zámok aplikácie"; - -/* Class = "UITableViewSection"; headerTitle = "Appearance"; ObjectID = "dj9-vh-Rig"; */ -"dj9-vh-Rig.headerTitle" = "Vzhľad"; - -/* Class = "UITableViewSection"; footerTitle = "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening."; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.footerTitle" = "Ak je nastavená funkcia Touch ID, Face ID alebo systémový prístupový kód, pri otvorení aplikácie sa zobrazí výzva na odomknutie aplikácie."; - -/* Class = "UITableViewSection"; headerTitle = "Privacy"; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.headerTitle" = "Súkromie"; - /* Class = "UILabel"; text = "Label"; ObjectID = "dud-qo-Ces"; */ "dud-qo-Ces.text" = "Označenie"; -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Dyd-bm-goj"; */ -"Dyd-bm-goj.title" = "Nastavenia"; - -/* Class = "UILabel"; text = "100%"; ObjectID = "EB8-09-gt2"; */ -"EB8-09-gt2.text" = "100 %"; - /* Class = "UILabel"; text = "Label"; ObjectID = "EIq-Ev-nfj"; */ "EIq-Ev-nfj.text" = "Označenie"; -/* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "ekF-SJ-PAQ"; */ -"ekF-SJ-PAQ.text" = "Automaticky vymazať údaje"; - /* Class = "UILabel"; text = "App Exit, Inactive for 5 Minutes"; ObjectID = "ElX-yE-4PX"; */ "ElX-yE-4PX.text" = "Ukončiť 5 minút neaktívnu aplikáciu"; /* Class = "UINavigationItem"; title = "App Icon"; ObjectID = "eMH-uv-Kms"; */ "eMH-uv-Kms.title" = "Ikona aplikácie"; -/* Class = "UILabel"; text = "Theme"; ObjectID = "f1O-6u-LFY"; */ -"f1O-6u-LFY.text" = "Motív"; - /* Class = "UILabel"; text = "App Exit Only"; ObjectID = "Fal-1Y-o2S"; */ "Fal-1Y-o2S.text" = "Iba ukončiť aplikáciu"; /* Class = "UITableViewController"; title = "Automatically Clear Data"; ObjectID = "fdJ-b1-Des"; */ "fdJ-b1-Des.title" = "Automaticky vymazať údaje"; -/* Class = "UILabel"; text = "Unprotected Sites"; ObjectID = "FHC-1z-Z3v"; */ -"FHC-1z-Z3v.text" = "Nezabezpečené webové stránky"; - -/* Class = "UITableViewSection"; headerTitle = "About"; ObjectID = "FpT-1C-xtx"; */ -"FpT-1C-xtx.headerTitle" = "O nás"; - /* Class = "UITableViewController"; title = "Global Privacy Control (GPC)"; ObjectID = "fV3-86-QQj"; */ "fV3-86-QQj.title" = "Globálna kontrola súkromia (GPC)"; -/* Class = "UILabel"; text = "Add Widget to Home Screen"; ObjectID = "Fxu-zn-51Z"; */ -"Fxu-zn-51Z.text" = "Pridať miniaplikáciu na domovskú obrazovku"; - -/* Class = "UILabel"; text = "Fire Button Animation"; ObjectID = "gBo-Cu-e2k"; Note = "Fire button animation settings item"; */ -"gBo-Cu-e2k.text" = "Animácia tlačidla ohňa"; - -/* Class = "UILabel"; text = "Default"; ObjectID = "Gbx-kl-uOO"; */ -"Gbx-kl-uOO.text" = "Štandardný"; - -/* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Správa kontextových okien súborov cookie"; - -/* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ -"gS2-mg-l7R.title" = "Motív"; - -/* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ -"HLr-R8-xxF.text" = "Náhľady po dlhodobom stlačení"; - -/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ -"hoT-Nu-KXP.text" = "Prehliadajte súkromne s našou aplikáciou pre Windows"; - /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Ochrana súkromia je zapnutá pre všetky webové stránky"; /* Class = "UICollectionViewController"; title = "Icon"; ObjectID = "jbD-Oy-Cmw"; */ "jbD-Oy-Cmw.title" = "Ikona"; -/* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ -"m23-t6-9cb.text" = "Zdieľať spätnú väzbu"; - -/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ -"mLn-1x-Fl5.title" = "Poloha riadku s adresou"; - -/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ -"mqV-pf-NZ1.text" = "Poloha riadku s adresou"; - /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Nech DuckDuckGo spravuje kontextové okná týkajúce sa súhlasu so súbormi cookie"; @@ -181,36 +76,9 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Nezabezpečené webové stránky"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "O službe DuckDuckGo"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Hore"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "Viac od DuckDuckGo"; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Synchronizácia a zálohovanie"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Prezerajte si súkromne stránky s našou aplikáciou pre Mac "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Označenie"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "Aplikácia DuckDuckGo pre Windows"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Veľkosť textu"; -/* Class = "UILabel"; text = "Private Voice Search"; ObjectID = "Swa-O7-n8W"; */ -"Swa-O7-n8W.text" = "Súkromné hlasové vyhľadávanie"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Zobraziť klávesnicu v"; @@ -220,42 +88,18 @@ /* Class = "UITableViewSection"; headerTitle = "Action"; ObjectID = "U2M-6p-6nl"; */ "U2M-6p-6nl.headerTitle" = "Aktivita"; -/* Class = "UILabel"; text = "Autocomplete Suggestions"; ObjectID = "U8i-cQ-5WW"; */ -"U8i-cQ-5WW.text" = "Zobraziť návrhy automatického dopĺňania"; - -/* Class = "UINavigationItem"; title = "Fire Button Animation"; ObjectID = "uns-8w-IwL"; Note = "Fire button animation setting page title"; */ -"uns-8w-IwL.title" = "Animácia tlačidla ohňa"; - /* Class = "UILabel"; text = "Remove All Fireproof Sites"; ObjectID = "UZx-52-aer"; */ "UZx-52-aer.text" = "Odstrániť všetky zabezpečené stránky"; -/* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "vPz-uO-6gB"; */ -"vPz-uO-6gB.text" = "Globálna kontrola súkromia (GPC)"; - /* Class = "UITableViewSection"; footerTitle = "App exit is defined by swiping the app to close it while inactivity is when the app is in the background."; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.footerTitle" = "Uzatvorenie aplikácie je rozpoznané potiahnutím aplikácie na jej uzatvorenie, zatiaľ čo nečinnosť nastane, keď je aplikácia na pozadí."; /* Class = "UITableViewSection"; headerTitle = "Desired timing"; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.headerTitle" = "Požadované načasovanie"; -/* Class = "UILabel"; text = "Set as Default Browser"; ObjectID = "xof-5k-PkI"; */ -"xof-5k-PkI.text" = "Nastaviť ako predvolený prehliadač"; - /* Class = "UINavigationItem"; title = "Fireproof Sites"; ObjectID = "xUX-nF-HOl"; */ "xUX-nF-HOl.title" = "Zabezpečené stránky"; -/* Class = "UILabel"; text = "Block email trackers and hide your address"; ObjectID = "Y6Y-wA-n6Z"; */ -"Y6Y-wA-n6Z.text" = "Zablokujte nástroje na sledovanie e‑mailov a skryte vašu adresu"; - -/* Class = "UILabel"; text = "Keyboard"; ObjectID = "yoZ-jw-Cu3"; */ -"yoZ-jw-Cu3.text" = "Klávesnica"; - -/* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ -"yvj-LL-MiR.text" = "Pridať aplikáciu do Docku"; - -/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "Aplikácia DuckDuckGo pre Mac"; - /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Nová karta"; diff --git a/DuckDuckGo/sl.lproj/Localizable.strings b/DuckDuckGo/sl.lproj/Localizable.strings index df7e4632ff..8b11995d70 100644 --- a/DuckDuckGo/sl.lproj/Localizable.strings +++ b/DuckDuckGo/sl.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Tipkovnica"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Gesla"; + /* Settings title for the 'More' section */ "settings.more" = "Več od iskalnika DuckDuckGo"; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Pregledi z dolgim pritiskom"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Katera spletna stran je poškodovana?"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Obnovitvena koda je bila kopirana v odložišče"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Izbriši podatke strežnika"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "Prekinjena bo povezava z vsemi napravami, sinhronizirani podatki pa bodo izbrisani iz strežnika."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "Želite izbrisati podatke strežnika?"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Odstrani"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "Aplikacija »%@« ne bo več mogla dostopati do sinhroniziranih podatkov."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "Želite odstraniti napravo?"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Odstrani"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "Ta naprava ne bo več mogla dostopati do vaših sinhroniziranih podatkov."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "Želite izklopiti sinhronizacijo?"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "Sinhronizacija in varnostno kopiranje žal trenutno nista na voljo. Poskusite znova pozneje."; diff --git a/DuckDuckGo/sl.lproj/Settings.strings b/DuckDuckGo/sl.lproj/Settings.strings index ef107b32dd..c4ef486b97 100644 --- a/DuckDuckGo/sl.lproj/Settings.strings +++ b/DuckDuckGo/sl.lproj/Settings.strings @@ -1,15 +1,3 @@ -/* Class = "UILabel"; text = "Fireproof Sites"; ObjectID = "0DQ-yq-UuT"; */ -"0DQ-yq-UuT.text" = "Spletne strani s požarno zaščito"; - -/* Class = "UITableViewSection"; footerTitle = "Disable to prevent links from automatically opening in other installed apps."; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.footerTitle" = "Onemogoči, da preprečiš samodejno odpiranje povezav v drugih nameščenih aplikacijah."; - -/* Class = "UITableViewSection"; headerTitle = "Customize"; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.headerTitle" = "Prilagodi"; - -/* Class = "UILabel"; text = "Version"; ObjectID = "2ky-s9-1aZ"; */ -"2ky-s9-1aZ.text" = "Različica"; - /* Class = "UINavigationItem"; title = "Keyboard"; ObjectID = "2pp-PM-6rW"; */ "2pp-PM-6rW.title" = "Tipkovnica"; @@ -31,30 +19,9 @@ /* Class = "UILabel"; text = "Clear Tabs and Data"; ObjectID = "9fc-9r-4aA"; */ "9fc-9r-4aA.text" = "Počisti zavihke in podatke"; -/* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ -"9Ko-0g-T3h.text" = "Velikost besedila"; - -/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ -"9kt-6R-XiZ.text" = "Naslov"; - /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Zagon aplikacije"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Odpri povezave v povezanih aplikacijah"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Meni za odpravljanje napak"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Gesla"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Animacija"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "Zaščita e-pošte"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "Podatki in/ali zavihki bodo izbrisani, ko boste aplikacijo ponovno zagnali."; @@ -64,114 +31,42 @@ /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ "btj-ri-kRr.title" = "Upravljanje pojavnih oken za piškotke"; -/* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ -"cKo-er-HNj.text" = "Ikona aplikacije"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "CR5-Al-WIW"; */ -"CR5-Al-WIW.text" = "Oznaka"; - /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Globalni nadzor zasebnosti (GPC)"; /* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ "CxT-QK-iVn.title" = "Dodaj"; -/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ -"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; - /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Izhod iz aplikacije, neaktivnost 15 minut"; -/* Class = "UILabel"; text = "10.0.1 (Build 10005)"; ObjectID = "d5n-vG-8kF"; */ -"d5n-vG-8kF.text" = "10.0.1 (različica 10005)"; - -/* Class = "UILabel"; text = "Application Lock"; ObjectID = "dBZ-yq-FYj"; */ -"dBZ-yq-FYj.text" = "Zaklepanje aplikacije"; - -/* Class = "UITableViewSection"; headerTitle = "Appearance"; ObjectID = "dj9-vh-Rig"; */ -"dj9-vh-Rig.headerTitle" = "Izgled"; - -/* Class = "UITableViewSection"; footerTitle = "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening."; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.footerTitle" = "Če nastavite prepoznavanje z dotikom, prepoznavanje z obrazom ali sistemsko geslo, boste ob odpiranju morali odkleniti aplikacijo."; - -/* Class = "UITableViewSection"; headerTitle = "Privacy"; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.headerTitle" = "Zasebnost"; - /* Class = "UILabel"; text = "Label"; ObjectID = "dud-qo-Ces"; */ "dud-qo-Ces.text" = "Oznaka"; -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Dyd-bm-goj"; */ -"Dyd-bm-goj.title" = "Nastavitve"; - -/* Class = "UILabel"; text = "100%"; ObjectID = "EB8-09-gt2"; */ -"EB8-09-gt2.text" = "100 %"; - /* Class = "UILabel"; text = "Label"; ObjectID = "EIq-Ev-nfj"; */ "EIq-Ev-nfj.text" = "Oznaka"; -/* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "ekF-SJ-PAQ"; */ -"ekF-SJ-PAQ.text" = "Samodejno počisti podatke"; - /* Class = "UILabel"; text = "App Exit, Inactive for 5 Minutes"; ObjectID = "ElX-yE-4PX"; */ "ElX-yE-4PX.text" = "Izhod iz aplikacije, neaktivnost 5 minut"; /* Class = "UINavigationItem"; title = "App Icon"; ObjectID = "eMH-uv-Kms"; */ "eMH-uv-Kms.title" = "Ikona aplikacije"; -/* Class = "UILabel"; text = "Theme"; ObjectID = "f1O-6u-LFY"; */ -"f1O-6u-LFY.text" = "Tema"; - /* Class = "UILabel"; text = "App Exit Only"; ObjectID = "Fal-1Y-o2S"; */ "Fal-1Y-o2S.text" = "Samo izhod iz aplikacije"; /* Class = "UITableViewController"; title = "Automatically Clear Data"; ObjectID = "fdJ-b1-Des"; */ "fdJ-b1-Des.title" = "Samodejno počisti podatke"; -/* Class = "UILabel"; text = "Unprotected Sites"; ObjectID = "FHC-1z-Z3v"; */ -"FHC-1z-Z3v.text" = "Nezaščitene strani"; - -/* Class = "UITableViewSection"; headerTitle = "About"; ObjectID = "FpT-1C-xtx"; */ -"FpT-1C-xtx.headerTitle" = "Več o"; - /* Class = "UITableViewController"; title = "Global Privacy Control (GPC)"; ObjectID = "fV3-86-QQj"; */ "fV3-86-QQj.title" = "Globalni nadzor zasebnosti (GPC)"; -/* Class = "UILabel"; text = "Add Widget to Home Screen"; ObjectID = "Fxu-zn-51Z"; */ -"Fxu-zn-51Z.text" = "Dodajte pripomoček na domači zaslon"; - -/* Class = "UILabel"; text = "Fire Button Animation"; ObjectID = "gBo-Cu-e2k"; Note = "Fire button animation settings item"; */ -"gBo-Cu-e2k.text" = "Animacija za gumb ogenj"; - -/* Class = "UILabel"; text = "Default"; ObjectID = "Gbx-kl-uOO"; */ -"Gbx-kl-uOO.text" = "Privzeto"; - -/* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Upravljanje pojavnih oken za piškotke"; - -/* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ -"gS2-mg-l7R.title" = "Tema"; - -/* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ -"HLr-R8-xxF.text" = "Pregledi z dolgim pritiskom"; - -/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ -"hoT-Nu-KXP.text" = "Brskajte zasebno z našo aplikacijo za sistem Windows"; - /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Zaščita zasebnosti omogočena za vsa spletna mesta"; /* Class = "UICollectionViewController"; title = "Icon"; ObjectID = "jbD-Oy-Cmw"; */ "jbD-Oy-Cmw.title" = "Ikona"; -/* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ -"m23-t6-9cb.text" = "Deli povratne informacije"; - -/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ -"mLn-1x-Fl5.title" = "Položaj naslovne vrstice"; - -/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ -"mqV-pf-NZ1.text" = "Položaj naslovne vrstice"; - /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Naj DuckDuckGo upravlja pojavna okna s soglasjem za piškotke"; @@ -181,36 +76,9 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Nezaščitene strani"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "O DuckDuckGo"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Vrh"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "Več od iskalnika DuckDuckGo"; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Sinhronizacija in varnostno kopiranje"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Brskajte zasebno z našo aplikacijo za računalnike Mac "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Oznaka"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "Aplikacija DuckDuckGo za sistem Windows"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Velikost besedila"; -/* Class = "UILabel"; text = "Private Voice Search"; ObjectID = "Swa-O7-n8W"; */ -"Swa-O7-n8W.text" = "Zasebno glasovno iskanje"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Pokaži tipkovnico v/pri"; @@ -220,42 +88,18 @@ /* Class = "UITableViewSection"; headerTitle = "Action"; ObjectID = "U2M-6p-6nl"; */ "U2M-6p-6nl.headerTitle" = "Ukrep"; -/* Class = "UILabel"; text = "Autocomplete Suggestions"; ObjectID = "U8i-cQ-5WW"; */ -"U8i-cQ-5WW.text" = "Pokaži predloge za samodokončanje"; - -/* Class = "UINavigationItem"; title = "Fire Button Animation"; ObjectID = "uns-8w-IwL"; Note = "Fire button animation setting page title"; */ -"uns-8w-IwL.title" = "Animacija za gumb ogenj"; - /* Class = "UILabel"; text = "Remove All Fireproof Sites"; ObjectID = "UZx-52-aer"; */ "UZx-52-aer.text" = "Odstrani vse spletne strani s požarno zaščito"; -/* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "vPz-uO-6gB"; */ -"vPz-uO-6gB.text" = "Globalni nadzor zasebnosti (GPC)"; - /* Class = "UITableViewSection"; footerTitle = "App exit is defined by swiping the app to close it while inactivity is when the app is in the background."; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.footerTitle" = "Izhod iz aplikacije je določen s tem, da povlečeš aplikacijo, da jo med nedejavnostjo zapreš, ko se aplikacija izvaja v ozadju."; /* Class = "UITableViewSection"; headerTitle = "Desired timing"; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.headerTitle" = "Želen čas"; -/* Class = "UILabel"; text = "Set as Default Browser"; ObjectID = "xof-5k-PkI"; */ -"xof-5k-PkI.text" = "Nastavite za privzeti brskalnik"; - /* Class = "UINavigationItem"; title = "Fireproof Sites"; ObjectID = "xUX-nF-HOl"; */ "xUX-nF-HOl.title" = "Spletne strani s požarno zaščito"; -/* Class = "UILabel"; text = "Block email trackers and hide your address"; ObjectID = "Y6Y-wA-n6Z"; */ -"Y6Y-wA-n6Z.text" = "Blokirajte sledilnike e-pošte in skrijte svoj naslov"; - -/* Class = "UILabel"; text = "Keyboard"; ObjectID = "yoZ-jw-Cu3"; */ -"yoZ-jw-Cu3.text" = "Tipkovnica"; - -/* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ -"yvj-LL-MiR.text" = "Dodajte aplikacijo na svoj domači zaslon"; - -/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "Aplikacija DuckDuckGo za računalnike Mac"; - /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Nov zavihek"; diff --git a/DuckDuckGo/sv.lproj/Localizable.strings b/DuckDuckGo/sv.lproj/Localizable.strings index 809b15e3c3..626ec73894 100644 --- a/DuckDuckGo/sv.lproj/Localizable.strings +++ b/DuckDuckGo/sv.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Tangentbord"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Lösenord"; + /* Settings title for the 'More' section */ "settings.more" = "Mer från DuckDuckGo"; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Förhandsvisning vid nedhållning"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Vilken webbplats är skadad?"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Återställningskoden har kopierats till urklipp"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Radera serverdata"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "Alla enheter kopplas bort och dina synkroniserade data raderas från servern."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "Radera serverdata?"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Ta bort"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "”%@” kommer inte längre att få tillgång till dina synkroniserade data."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "Vill du ta bort enheten?"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Ta bort"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "Denna enhet kommer inte längre att få tillgång till dina synkroniserade data."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "Vill du inaktivera synkronisering?"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "Tyvärr är synkronisering och säkerhetskopiering inte tillgängliga för närvarande. Försök igen senare."; diff --git a/DuckDuckGo/sv.lproj/Settings.strings b/DuckDuckGo/sv.lproj/Settings.strings index bfb4ae44ed..9d5a7cf207 100644 --- a/DuckDuckGo/sv.lproj/Settings.strings +++ b/DuckDuckGo/sv.lproj/Settings.strings @@ -1,15 +1,3 @@ -/* Class = "UILabel"; text = "Fireproof Sites"; ObjectID = "0DQ-yq-UuT"; */ -"0DQ-yq-UuT.text" = "Brandsäkra webbplatser"; - -/* Class = "UITableViewSection"; footerTitle = "Disable to prevent links from automatically opening in other installed apps."; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.footerTitle" = "Inaktivera för att förhindra att länkar automatiskt öppnas i andra installerade appar."; - -/* Class = "UITableViewSection"; headerTitle = "Customize"; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.headerTitle" = "Anpassa"; - -/* Class = "UILabel"; text = "Version"; ObjectID = "2ky-s9-1aZ"; */ -"2ky-s9-1aZ.text" = "Version"; - /* Class = "UINavigationItem"; title = "Keyboard"; ObjectID = "2pp-PM-6rW"; */ "2pp-PM-6rW.title" = "Tangentbord"; @@ -31,30 +19,9 @@ /* Class = "UILabel"; text = "Clear Tabs and Data"; ObjectID = "9fc-9r-4aA"; */ "9fc-9r-4aA.text" = "Rensa flikar och data"; -/* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ -"9Ko-0g-T3h.text" = "Textstorlek"; - -/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ -"9kt-6R-XiZ.text" = "Rubrik"; - /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "App-start"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Öppna länkar i associerade appar"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Felsökningsmeny"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Lösenord"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Animation"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "E-postskydd"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "Data och/eller flikar kommer att rensas vid omstart av appen."; @@ -64,114 +31,42 @@ /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ "btj-ri-kRr.title" = "Hantera popup-fönster för cookies"; -/* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ -"cKo-er-HNj.text" = "App-ikon"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "CR5-Al-WIW"; */ -"CR5-Al-WIW.text" = "Etikett"; - /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Global Privacy Control (GPC)"; /* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ "CxT-QK-iVn.title" = "Lägg till"; -/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ -"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; - /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Avsluta app, inaktiv i 15 minuter"; -/* Class = "UILabel"; text = "10.0.1 (Build 10005)"; ObjectID = "d5n-vG-8kF"; */ -"d5n-vG-8kF.text" = "10.0.1 (Version 10005)"; - -/* Class = "UILabel"; text = "Application Lock"; ObjectID = "dBZ-yq-FYj"; */ -"dBZ-yq-FYj.text" = "App-lås"; - -/* Class = "UITableViewSection"; headerTitle = "Appearance"; ObjectID = "dj9-vh-Rig"; */ -"dj9-vh-Rig.headerTitle" = "Utseende"; - -/* Class = "UITableViewSection"; footerTitle = "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening."; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.footerTitle" = "Om Touch ID, Face ID eller ett systemlösenord har konfigurerats ombes du låsa upp appen när du öppnar."; - -/* Class = "UITableViewSection"; headerTitle = "Privacy"; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.headerTitle" = "Sekretess"; - /* Class = "UILabel"; text = "Label"; ObjectID = "dud-qo-Ces"; */ "dud-qo-Ces.text" = "Etikett"; -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Dyd-bm-goj"; */ -"Dyd-bm-goj.title" = "Inställningar"; - -/* Class = "UILabel"; text = "100%"; ObjectID = "EB8-09-gt2"; */ -"EB8-09-gt2.text" = "100 %"; - /* Class = "UILabel"; text = "Label"; ObjectID = "EIq-Ev-nfj"; */ "EIq-Ev-nfj.text" = "Etikett"; -/* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "ekF-SJ-PAQ"; */ -"ekF-SJ-PAQ.text" = "Rensa data automatiskt"; - /* Class = "UILabel"; text = "App Exit, Inactive for 5 Minutes"; ObjectID = "ElX-yE-4PX"; */ "ElX-yE-4PX.text" = "Avsluta app, inaktiv i 5 minuter"; /* Class = "UINavigationItem"; title = "App Icon"; ObjectID = "eMH-uv-Kms"; */ "eMH-uv-Kms.title" = "App-ikon"; -/* Class = "UILabel"; text = "Theme"; ObjectID = "f1O-6u-LFY"; */ -"f1O-6u-LFY.text" = "Tema"; - /* Class = "UILabel"; text = "App Exit Only"; ObjectID = "Fal-1Y-o2S"; */ "Fal-1Y-o2S.text" = "Lämna endast appen"; /* Class = "UITableViewController"; title = "Automatically Clear Data"; ObjectID = "fdJ-b1-Des"; */ "fdJ-b1-Des.title" = "Rensa data automatiskt"; -/* Class = "UILabel"; text = "Unprotected Sites"; ObjectID = "FHC-1z-Z3v"; */ -"FHC-1z-Z3v.text" = "Oskyddade webbplatser"; - -/* Class = "UITableViewSection"; headerTitle = "About"; ObjectID = "FpT-1C-xtx"; */ -"FpT-1C-xtx.headerTitle" = "Om"; - /* Class = "UITableViewController"; title = "Global Privacy Control (GPC)"; ObjectID = "fV3-86-QQj"; */ "fV3-86-QQj.title" = "Global Privacy Control (GPC)"; -/* Class = "UILabel"; text = "Add Widget to Home Screen"; ObjectID = "Fxu-zn-51Z"; */ -"Fxu-zn-51Z.text" = "Lägg till widget på startsidan"; - -/* Class = "UILabel"; text = "Fire Button Animation"; ObjectID = "gBo-Cu-e2k"; Note = "Fire button animation settings item"; */ -"gBo-Cu-e2k.text" = "Animation för brännarknapp"; - -/* Class = "UILabel"; text = "Default"; ObjectID = "Gbx-kl-uOO"; */ -"Gbx-kl-uOO.text" = "Standard"; - -/* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Hantera popup-fönster för cookies"; - -/* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ -"gS2-mg-l7R.title" = "Tema"; - -/* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ -"HLr-R8-xxF.text" = "Förhandsvisning vid nedhållning"; - -/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ -"hoT-Nu-KXP.text" = "Surfa privat med vår app för Windows"; - /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Integritetsskydd aktiverat för alla webbplatser"; /* Class = "UICollectionViewController"; title = "Icon"; ObjectID = "jbD-Oy-Cmw"; */ "jbD-Oy-Cmw.title" = "Ikon"; -/* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ -"m23-t6-9cb.text" = "Berätta vad du tycker"; - -/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ -"mLn-1x-Fl5.title" = "Adressfältsläge"; - -/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ -"mqV-pf-NZ1.text" = "Adressfältsläge"; - /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Låt DuckDuckGo hantera popup-fönster för godkännande av cookies"; @@ -181,36 +76,9 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Oskyddade webbplatser"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "Om DuckDuckGo"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Topp"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "Mer från DuckDuckGo"; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Synkronisering och säkerhetskopiering"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Surfa privat med vår app för Mac "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Etikett"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "DuckDuckGo-app för Windows"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Textstorlek"; -/* Class = "UILabel"; text = "Private Voice Search"; ObjectID = "Swa-O7-n8W"; */ -"Swa-O7-n8W.text" = "Privat röstsökning"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Visa tangentbord på"; @@ -220,42 +88,18 @@ /* Class = "UITableViewSection"; headerTitle = "Action"; ObjectID = "U2M-6p-6nl"; */ "U2M-6p-6nl.headerTitle" = "Åtgärd"; -/* Class = "UILabel"; text = "Autocomplete Suggestions"; ObjectID = "U8i-cQ-5WW"; */ -"U8i-cQ-5WW.text" = "Visa Autoslutför förslag"; - -/* Class = "UINavigationItem"; title = "Fire Button Animation"; ObjectID = "uns-8w-IwL"; Note = "Fire button animation setting page title"; */ -"uns-8w-IwL.title" = "Animation för brännarknapp"; - /* Class = "UILabel"; text = "Remove All Fireproof Sites"; ObjectID = "UZx-52-aer"; */ "UZx-52-aer.text" = "Ta bort alla brandsäkra webbplatser"; -/* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "vPz-uO-6gB"; */ -"vPz-uO-6gB.text" = "Global Privacy Control (GPC)"; - /* Class = "UITableViewSection"; footerTitle = "App exit is defined by swiping the app to close it while inactivity is when the app is in the background."; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.footerTitle" = "Avslutning av app är då appen sveps för att stängas. Inaktivitet är då appen körs i bakgrunden."; /* Class = "UITableViewSection"; headerTitle = "Desired timing"; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.headerTitle" = "Önskad tidpunkt"; -/* Class = "UILabel"; text = "Set as Default Browser"; ObjectID = "xof-5k-PkI"; */ -"xof-5k-PkI.text" = "Ställ in som standardwebbläsare"; - /* Class = "UINavigationItem"; title = "Fireproof Sites"; ObjectID = "xUX-nF-HOl"; */ "xUX-nF-HOl.title" = "Brandsäkra webbplatser"; -/* Class = "UILabel"; text = "Block email trackers and hide your address"; ObjectID = "Y6Y-wA-n6Z"; */ -"Y6Y-wA-n6Z.text" = "Blockera e-postspårare och dölj din adress"; - -/* Class = "UILabel"; text = "Keyboard"; ObjectID = "yoZ-jw-Cu3"; */ -"yoZ-jw-Cu3.text" = "Tangentbord"; - -/* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ -"yvj-LL-MiR.text" = "Lägg till app i din Dock"; - -/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "DuckDuckGo-app för Mac"; - /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Ny flik"; diff --git a/DuckDuckGo/tr.lproj/Localizable.strings b/DuckDuckGo/tr.lproj/Localizable.strings index 1ca6c73d19..2e53912f04 100644 --- a/DuckDuckGo/tr.lproj/Localizable.strings +++ b/DuckDuckGo/tr.lproj/Localizable.strings @@ -1843,9 +1843,15 @@ /* Settings screen cell for Keyboard */ "settings.keyboard" = "Klavye"; +/* Settings screen cell text for passwords */ +"settings.logins" = "Şifreler"; + /* Settings title for the 'More' section */ "settings.more" = "DuckDuckGo'dan daha fazlası"; +/* Product name for the subscription bundle */ +"settings.ppro" = "Privacy Pro"; + /* Settings screen cell for long press previews */ "settings.previews" = "Uzun Basma Önizlemeleri"; @@ -1891,9 +1897,36 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Hangi web sitesi hatalı?"; +/* Message confirming that recovery code was copied to clipboard */ +"sync.code.copied" = "Kurtarma kodu panoya kopyalandı"; + +/* Caption for a button to delete Sync server data */ +"sync.delete.all.confirm.action" = "Sunucu Verilerini Sil"; + +/* Message for the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.message" = "Tüm cihazların bağlantısı kesilecek ve senkronize edilen verileriniz sunucudan silinecektir."; + +/* Title of the dialog to confirm deleting Sync server data */ +"sync.delete.all.confirm.title" = "Sunucu Verileri Silinsin mi?"; + +/* Caption for a button to remove device from Sync */ +"sync.remove-device.action" = "Kaldır"; + /* No comment provided by engineer. */ "sync.remove-device.message" = "\"%@\" artık senkronize edilmiş verilerinize erişemeyecektir."; +/* Title of the dialog to remove device from Sync */ +"sync.remove-device.title" = "Cihaz Kaldırılsın mı?"; + +/* Caption for a button to remove current device from Sync */ +"sync.turn.off.confirm.action" = "Kaldır"; + +/* Message for the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.message" = "Bu Cihaz artık senkronize edilen verilerinize erişemeyecektir."; + +/* Title of the dialog to confirm turning off Sync */ +"sync.turn.off.confirm.title" = "Senkronizasyon kapatılsın mı?"; + /* Data syncing unavailable warning message */ "sync.warning.data.syncing.disabled" = "Üzgünüz, ancak Senkronizasyon ve Yedekleme şu anda kullanılamıyor. Lütfen daha sonra tekrar deneyin."; diff --git a/DuckDuckGo/tr.lproj/Settings.strings b/DuckDuckGo/tr.lproj/Settings.strings index 88f37adc38..cef3e23ead 100644 --- a/DuckDuckGo/tr.lproj/Settings.strings +++ b/DuckDuckGo/tr.lproj/Settings.strings @@ -1,15 +1,3 @@ -/* Class = "UILabel"; text = "Fireproof Sites"; ObjectID = "0DQ-yq-UuT"; */ -"0DQ-yq-UuT.text" = "Korumalı Siteler"; - -/* Class = "UITableViewSection"; footerTitle = "Disable to prevent links from automatically opening in other installed apps."; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.footerTitle" = "Bağlantıların yüklenmiş olan diğer uygulamalarda otomatik olarak açılmasını önlemek için devre dışı bırakın."; - -/* Class = "UITableViewSection"; headerTitle = "Customize"; ObjectID = "0gU-rZ-pRc"; */ -"0gU-rZ-pRc.headerTitle" = "Özelleştir"; - -/* Class = "UILabel"; text = "Version"; ObjectID = "2ky-s9-1aZ"; */ -"2ky-s9-1aZ.text" = "Versiyon"; - /* Class = "UINavigationItem"; title = "Keyboard"; ObjectID = "2pp-PM-6rW"; */ "2pp-PM-6rW.title" = "Klavye"; @@ -31,30 +19,9 @@ /* Class = "UILabel"; text = "Clear Tabs and Data"; ObjectID = "9fc-9r-4aA"; */ "9fc-9r-4aA.text" = "Sekmeleri ve Verileri Temizle"; -/* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ -"9Ko-0g-T3h.text" = "Metin Boyutu"; - -/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ -"9kt-6R-XiZ.text" = "Title"; - /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Uygulamayı Başlatma"; -/* Class = "UILabel"; text = "Open Links in Associated Apps"; ObjectID = "a1T-ui-4Nw"; */ -"a1T-ui-4Nw.text" = "Bağlantıları İlişkili Uygulamalarda Aç"; - -/* Class = "UILabel"; text = "Debug Menu"; ObjectID = "A9G-5I-RSn"; */ -"A9G-5I-RSn.text" = "Hata Ayıklama Menüsü"; - -/* Class = "UILabel"; text = "Passwords"; ObjectID = "And-cQ-SEu"; */ -"And-cQ-SEu.text" = "Şifreler"; - -/* Class = "UILabel"; text = "Animation"; ObjectID = "AtR-nS-Gun"; */ -"AtR-nS-Gun.text" = "Animasyon"; - -/* Class = "UILabel"; text = "Email Protection"; ObjectID = "azf-Nc-kvW"; */ -"azf-Nc-kvW.text" = "E-posta Koruması"; - /* Class = "UITableViewSection"; footerTitle = "Data and/or tabs will be cleared upon restart of the app."; ObjectID = "BGs-JL-4ib"; */ "BGs-JL-4ib.footerTitle" = "Uygulama yeniden başlatıldığında veriler ve/veya sekmeler temizlenecektir."; @@ -64,114 +31,42 @@ /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ "btj-ri-kRr.title" = "Çerez Açılır Pencerelerini Yönetin"; -/* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ -"cKo-er-HNj.text" = "Uygulama Simgesi"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "CR5-Al-WIW"; */ -"CR5-Al-WIW.text" = "Label"; - /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Küresel Gizlilik Kontrolü (GPC)"; /* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ "CxT-QK-iVn.title" = "Ekle"; -/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ -"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; - /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Uygulamadan Çık, 15 Dakika Boyunca İnaktif"; -/* Class = "UILabel"; text = "10.0.1 (Build 10005)"; ObjectID = "d5n-vG-8kF"; */ -"d5n-vG-8kF.text" = "10.0.1 (Build 10005)"; - -/* Class = "UILabel"; text = "Application Lock"; ObjectID = "dBZ-yq-FYj"; */ -"dBZ-yq-FYj.text" = "Uygulama Kilidi"; - -/* Class = "UITableViewSection"; headerTitle = "Appearance"; ObjectID = "dj9-vh-Rig"; */ -"dj9-vh-Rig.headerTitle" = "Görünüm"; - -/* Class = "UITableViewSection"; footerTitle = "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening."; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.footerTitle" = "Touch ID, Face ID veya sistem parolası belirlenmişse uygulamayı açarken kilidini açmanız istenir."; - -/* Class = "UITableViewSection"; headerTitle = "Privacy"; ObjectID = "dOj-jn-mSN"; */ -"dOj-jn-mSN.headerTitle" = "Gizlilik"; - /* Class = "UILabel"; text = "Label"; ObjectID = "dud-qo-Ces"; */ "dud-qo-Ces.text" = "Label"; -/* Class = "UINavigationItem"; title = "Settings"; ObjectID = "Dyd-bm-goj"; */ -"Dyd-bm-goj.title" = "Ayarlar"; - -/* Class = "UILabel"; text = "100%"; ObjectID = "EB8-09-gt2"; */ -"EB8-09-gt2.text" = "%100"; - /* Class = "UILabel"; text = "Label"; ObjectID = "EIq-Ev-nfj"; */ "EIq-Ev-nfj.text" = "Label"; -/* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "ekF-SJ-PAQ"; */ -"ekF-SJ-PAQ.text" = "Verileri Otomatik Olarak Temizle"; - /* Class = "UILabel"; text = "App Exit, Inactive for 5 Minutes"; ObjectID = "ElX-yE-4PX"; */ "ElX-yE-4PX.text" = "Uygulamadan Çık, 5 Dakika Boyunca İnaktif"; /* Class = "UINavigationItem"; title = "App Icon"; ObjectID = "eMH-uv-Kms"; */ "eMH-uv-Kms.title" = "Uygulama Simgesi"; -/* Class = "UILabel"; text = "Theme"; ObjectID = "f1O-6u-LFY"; */ -"f1O-6u-LFY.text" = "Tema"; - /* Class = "UILabel"; text = "App Exit Only"; ObjectID = "Fal-1Y-o2S"; */ "Fal-1Y-o2S.text" = "Yalnızca Uygulamadan Çıkma"; /* Class = "UITableViewController"; title = "Automatically Clear Data"; ObjectID = "fdJ-b1-Des"; */ "fdJ-b1-Des.title" = "Verileri Otomatik Olarak Temizle"; -/* Class = "UILabel"; text = "Unprotected Sites"; ObjectID = "FHC-1z-Z3v"; */ -"FHC-1z-Z3v.text" = "Korumasız Siteler"; - -/* Class = "UITableViewSection"; headerTitle = "About"; ObjectID = "FpT-1C-xtx"; */ -"FpT-1C-xtx.headerTitle" = "Hakkında"; - /* Class = "UITableViewController"; title = "Global Privacy Control (GPC)"; ObjectID = "fV3-86-QQj"; */ "fV3-86-QQj.title" = "Küresel Gizlilik Kontrolü (GPC)"; -/* Class = "UILabel"; text = "Add Widget to Home Screen"; ObjectID = "Fxu-zn-51Z"; */ -"Fxu-zn-51Z.text" = "Widget'ı Ana Ekrana Ekle"; - -/* Class = "UILabel"; text = "Fire Button Animation"; ObjectID = "gBo-Cu-e2k"; Note = "Fire button animation settings item"; */ -"gBo-Cu-e2k.text" = "Yangın Düğmesi Animasyonu"; - -/* Class = "UILabel"; text = "Default"; ObjectID = "Gbx-kl-uOO"; */ -"Gbx-kl-uOO.text" = "Varsayılan"; - -/* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Çerez Açılır Pencerelerini Yönetin"; - -/* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ -"gS2-mg-l7R.title" = "Tema"; - -/* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ -"HLr-R8-xxF.text" = "Uzun Basma Önizlemeleri"; - -/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ -"hoT-Nu-KXP.text" = "Windows uygulamamızla gizliliğinizi koruyarak gezinin"; - /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Gizlilik Koruması tüm siteler için etkin"; /* Class = "UICollectionViewController"; title = "Icon"; ObjectID = "jbD-Oy-Cmw"; */ "jbD-Oy-Cmw.title" = "Icon"; -/* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ -"m23-t6-9cb.text" = "Geri Bildirim Paylaş"; - -/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ -"mLn-1x-Fl5.title" = "Adres Çubuğu Konumu"; - -/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ -"mqV-pf-NZ1.text" = "Adres Çubuğu Konumu"; - /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "DuckDuckGo'nun çerez izni açılır pencerelerini yönetmesine izin verin"; @@ -181,36 +76,9 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Korumasız Siteler"; -/* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ -"oM7-1o-9oY.text" = "DuckDuckGo Hakkında"; - -/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ -"opn-JO-idF.text" = "Top"; - -/* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ -"OxE-MQ-uJk.headerTitle" = "DuckDuckGo'dan Daha Fazlası"; - -/* Class = "UILabel"; text = "Sync & Backup"; ObjectID = "oXN-ez-gct"; */ -"oXN-ez-gct.text" = "Senkronizasyon ve Yedekleme"; - -/* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ -"P0F-ts-ekd.text" = "Mac için uygulamamızla internette gezinirken gizliliğinizi koruyun "; - -/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ -"qah-gb-udB.text" = "Network Protection"; - -/* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ -"qeN-SV-zy7.text" = "Label"; - -/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ -"RQ8-H1-Ez1.text" = "DuckDuckGo Windows Uygulaması"; - /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Metin Boyutu"; -/* Class = "UILabel"; text = "Private Voice Search"; ObjectID = "Swa-O7-n8W"; */ -"Swa-O7-n8W.text" = "Özel Sesli Arama"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Şu durumda klavyeyi göster:"; @@ -220,42 +88,18 @@ /* Class = "UITableViewSection"; headerTitle = "Action"; ObjectID = "U2M-6p-6nl"; */ "U2M-6p-6nl.headerTitle" = "İşlem"; -/* Class = "UILabel"; text = "Autocomplete Suggestions"; ObjectID = "U8i-cQ-5WW"; */ -"U8i-cQ-5WW.text" = "Otomatik Tamamlama Önerileri"; - -/* Class = "UINavigationItem"; title = "Fire Button Animation"; ObjectID = "uns-8w-IwL"; Note = "Fire button animation setting page title"; */ -"uns-8w-IwL.title" = "Yangın Düğmesi Animasyonu"; - /* Class = "UILabel"; text = "Remove All Fireproof Sites"; ObjectID = "UZx-52-aer"; */ "UZx-52-aer.text" = "Tüm Korumalı Web Sitelerini Kaldır"; -/* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "vPz-uO-6gB"; */ -"vPz-uO-6gB.text" = "Küresel Gizlilik Kontrolü (GPC)"; - /* Class = "UITableViewSection"; footerTitle = "App exit is defined by swiping the app to close it while inactivity is when the app is in the background."; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.footerTitle" = "Uygulama çıkışı, uygulama arka plandayken uzun süre etkin olmaması durumunda uygulamayı kapatmak için ekranı kaydırmak şeklinde tanımlanır."; /* Class = "UITableViewSection"; headerTitle = "Desired timing"; ObjectID = "vSJ-YJ-bBs"; Note = "Timing setting for automatic data clearing function"; */ "vSJ-YJ-bBs.headerTitle" = "İstenen zamanlama"; -/* Class = "UILabel"; text = "Set as Default Browser"; ObjectID = "xof-5k-PkI"; */ -"xof-5k-PkI.text" = "Varsayılan Tarayıcı olarak ayarla"; - /* Class = "UINavigationItem"; title = "Fireproof Sites"; ObjectID = "xUX-nF-HOl"; */ "xUX-nF-HOl.title" = "Korumalı Siteler"; -/* Class = "UILabel"; text = "Block email trackers and hide your address"; ObjectID = "Y6Y-wA-n6Z"; */ -"Y6Y-wA-n6Z.text" = "E-posta izleyicileri engelleyin ve adresinizi gizleyin"; - -/* Class = "UILabel"; text = "Keyboard"; ObjectID = "yoZ-jw-Cu3"; */ -"yoZ-jw-Cu3.text" = "Klavye"; - -/* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ -"yvj-LL-MiR.text" = "Uygulamayı Dock'a Ekle"; - -/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "DuckDuckGo Mac Uygulaması"; - /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Yeni Sekme"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/bg.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/bg.lproj/Localizable.strings index f94451517f..bd324af65a 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/bg.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/bg.lproj/Localizable.strings @@ -28,6 +28,9 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "Отмени"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "Включване на Синхронизиране и архивиране"; + /* Connect With Server Sheet - Description Part 1 */ "connect.with.server.sheet.description.part1" = "Създава криптирано резервно копие на Вашите отметки и пароли на защитения сървър на DuckDuckGo, което може да бъде синхронизирано с други Ваши устройства."; @@ -52,6 +55,9 @@ /* No comment provided by engineer. */ "Dev environment" = "Среда за разработване"; +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "Данните Ви са синхронизирани!"; + /* Standard Buttons - Done Button */ "done.button" = "Готово"; @@ -226,6 +232,9 @@ /* Title of the warning message */ "sync.warning.sync.unavailable" = "Функцията за синхронизиране и архивиране не е достъпна"; +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Синхронизиране с друго устройство"; + /* Footer message for syncing with another device */ "sync.with.another.device.footer" = "Данните Ви са изцяло криптирани и DuckDuckGo няма достъп до ключа за криптиране."; @@ -253,6 +262,9 @@ /* Options - Unify Favorites Instruction */ "unified.favorites.instruction" = "Използвайте едни и същи любими отметки на всички Ваши устройства. Оставете изключено, ако имате различни предпочитания за мобилни устройства и настолни компютри."; +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Обединяване на любимите на всички устройства"; + /* View Text Code menu item */ "view.text.code.menu.item" = "Преглед на текстов код"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/cs.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/cs.lproj/Localizable.strings index 19bc89ec36..73cb877ae6 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/cs.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/cs.lproj/Localizable.strings @@ -28,6 +28,9 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "Zrušit"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "Zapnout synchronizaci a zálohování"; + /* Connect With Server Sheet - Description Part 1 */ "connect.with.server.sheet.description.part1" = "Na zabezpečeném serveru DuckDuckGo se vytvoří šifrovaná záloha záložek a hesel, kterou můžeš synchronizovat s ostatními zařízeními."; @@ -52,6 +55,9 @@ /* No comment provided by engineer. */ "Dev environment" = "Vývojářské prostředí"; +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "Tvoje data už se zase synchronizují!"; + /* Standard Buttons - Done Button */ "done.button" = "Hotovo"; @@ -226,6 +232,9 @@ /* Title of the warning message */ "sync.warning.sync.unavailable" = "Synchronizace a zálohování není k dispozici"; +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Synchronizovat s jiným zařízením"; + /* Footer message for syncing with another device */ "sync.with.another.device.footer" = "U tvých dat se uplatňuje end-to-end šifrování a DuckDuckGo nemá přístup k šifrovacímu klíči."; @@ -253,6 +262,9 @@ /* Options - Unify Favorites Instruction */ "unified.favorites.instruction" = "Používej na všech svých zařízeních stejné oblíbené záložky. Nech volbu vypnutou, pokud chceš mít záložky na mobilu a na počítači oddělené."; +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Sjednocení oblíbených položek napříč zařízeními"; + /* View Text Code menu item */ "view.text.code.menu.item" = "Zobrazit textový kód"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/da.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/da.lproj/Localizable.strings index 8ce4f30d96..82b5752c17 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/da.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/da.lproj/Localizable.strings @@ -28,6 +28,9 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "Annullér"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "Slå synkronisering og sikkerhedskopiering til"; + /* Connect With Server Sheet - Description Part 1 */ "connect.with.server.sheet.description.part1" = "Dette opretter en krypteret sikkerhedskopi af dine bogmærker og adgangskoder på DuckDuckGos sikre server, som kan synkroniseres med dine andre enheder."; @@ -52,6 +55,9 @@ /* No comment provided by engineer. */ "Dev environment" = "Udviklermiljø"; +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "Dine data er synkroniserede!"; + /* Standard Buttons - Done Button */ "done.button" = "Færdig"; @@ -226,6 +232,9 @@ /* Title of the warning message */ "sync.warning.sync.unavailable" = "Synkronisering og sikkerhedskopiering er ikke tilgængelig"; +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Synkroniser med en anden enhed"; + /* Footer message for syncing with another device */ "sync.with.another.device.footer" = "Dine data er fuldt krypterede, og DuckDuckGo har ikke adgang til krypteringsnøglen."; @@ -253,6 +262,9 @@ /* Options - Unify Favorites Instruction */ "unified.favorites.instruction" = "Brug de samme favoritbogmærker på alle dine enheder. Slå fra for at holde mobil- og computerfavoritter adskilt."; +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Foren favoritter på tværs af enheder"; + /* View Text Code menu item */ "view.text.code.menu.item" = "Vis tekstkode"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/de.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/de.lproj/Localizable.strings index 262d0fd621..82540a6071 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/de.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/de.lproj/Localizable.strings @@ -28,6 +28,9 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "Abbrechen"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "„Synchronisieren und sichern“ aktivieren"; + /* Connect With Server Sheet - Description Part 1 */ "connect.with.server.sheet.description.part1" = "Dadurch wird ein verschlüsseltes Backup deiner Lesezeichen und Passwörter auf dem sicheren Server von DuckDuckGo erstellt, der mit deinen anderen Geräten synchronisiert werden kann."; @@ -52,6 +55,9 @@ /* No comment provided by engineer. */ "Dev environment" = "Entwicklungsumgebung"; +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "Deine Daten sind synchronisiert!"; + /* Standard Buttons - Done Button */ "done.button" = "Fertig"; @@ -226,6 +232,9 @@ /* Title of the warning message */ "sync.warning.sync.unavailable" = "Synchronisieren und sichern ist nicht verfügbar"; +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Mit anderem Gerät synchronisieren"; + /* Footer message for syncing with another device */ "sync.with.another.device.footer" = "Deine Daten sind Ende-zu-Ende-verschlüsselt. DuckDuckGo hat keinen Zugriff auf den Verschlüsselungsschlüssel."; @@ -253,6 +262,9 @@ /* Options - Unify Favorites Instruction */ "unified.favorites.instruction" = "Verwende auf allen deinen Geräten dieselben Lesezeichenfavoriten. Lass diese Option deaktiviert, um deine Favoriten auf deinem Handy und deinem Desktop getrennt zu halten."; +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Favoriten geräteübergreifend vereinheitlichen"; + /* View Text Code menu item */ "view.text.code.menu.item" = "Textcode anzeigen"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/el.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/el.lproj/Localizable.strings index 4de539d94e..aebfbd4eca 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/el.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/el.lproj/Localizable.strings @@ -28,6 +28,9 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "Ακύρωση"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "Ενεργοποίηση συγχρονισμού και δημιουργίας αντιγράφων ασφαλείας"; + /* Connect With Server Sheet - Description Part 1 */ "connect.with.server.sheet.description.part1" = "Αυτό δημιουργεί ένα κρυπτογραφημένο αντίγραφο ασφάλειας των σελιδοδεικτών και των κωδικών πρόσβασής σας στον ασφαλή διακομιστή του DuckDuckGo, το οποίο μπορεί να συγχρονιστεί με τις άλλες συσκευές σας."; @@ -52,6 +55,9 @@ /* No comment provided by engineer. */ "Dev environment" = "Περιβάλλον ανάπτυξης"; +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "Τα δεδομένα σας είναι συγχρονισμένα!"; + /* Standard Buttons - Done Button */ "done.button" = "Ολοκληρώθηκε"; @@ -226,6 +232,9 @@ /* Title of the warning message */ "sync.warning.sync.unavailable" = "Ο συγχρονισμός και η δημιουργία αντιγράφων ασφάλειας δεν είναι διαθέσιμος"; +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Συγχρονισμός με άλλη συσκευή"; + /* Footer message for syncing with another device */ "sync.with.another.device.footer" = "Τα δεδομένα σας είναι κρυπτογραφημένα από άκρο σε άκρο και το DuckDuckGo δεν έχει πρόσβαση στο κλειδί κρυπτογράφησης."; @@ -253,6 +262,9 @@ /* Options - Unify Favorites Instruction */ "unified.favorites.instruction" = "Χρησιμοποιήστε τους ίδιους αγαπημένους σελιδοδείκτες σε όλες τις συσκευές σας. Διακόψτε τη διαδικασία για να διατηρήσετε τα Αγαπημένα σας για κινητά και υπολογιστές ξεχωριστά."; +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Ενοποίηση αγαπημένων σε όλες τις συσκευές"; + /* View Text Code menu item */ "view.text.code.menu.item" = "Προβολή κωδικού κειμένου"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/es.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/es.lproj/Localizable.strings index 7b378b2142..e44afe0149 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/es.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/es.lproj/Localizable.strings @@ -28,6 +28,9 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "Cancelar"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "Activar sincronización y copia de seguridad"; + /* Connect With Server Sheet - Description Part 1 */ "connect.with.server.sheet.description.part1" = "Esto crea una copia de seguridad encriptada de tus marcadores y contraseñas en el servidor seguro de DuckDuckGo que se puede sincronizar con tus otros dispositivos."; @@ -52,6 +55,9 @@ /* No comment provided by engineer. */ "Dev environment" = "Entorno de desarrollo"; +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "¡Tus datos están sincronizados!"; + /* Standard Buttons - Done Button */ "done.button" = "Hecho"; @@ -226,6 +232,9 @@ /* Title of the warning message */ "sync.warning.sync.unavailable" = "La sincronización y copia de seguridad no está disponible"; +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Sincronizar con otro dispositivo"; + /* Footer message for syncing with another device */ "sync.with.another.device.footer" = "Tus datos están encriptados de extremo a extremo y DuckDuckGo no tiene acceso a la clave de encriptación."; @@ -253,6 +262,9 @@ /* Options - Unify Favorites Instruction */ "unified.favorites.instruction" = "Usa los mismos marcadores favoritos en todos tus dispositivos. Deja de tener diferentes favoritos en tus dispositivos móviles y de escritorio."; +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Unifica tus favoritos en todos los dispositivos"; + /* View Text Code menu item */ "view.text.code.menu.item" = "Ver código de texto"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/et.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/et.lproj/Localizable.strings index 009fb3c73d..38f610d4f6 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/et.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/et.lproj/Localizable.strings @@ -28,6 +28,9 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "Tühista"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "Lülita sünkroonimine ja varundamine sisse"; + /* Connect With Server Sheet - Description Part 1 */ "connect.with.server.sheet.description.part1" = "See loob DuckDuckGo turvalises serveris krüptitud varukoopia sinu järjehoidjatest ja paroolidest, mida saab sünkroonida sinu teiste seadmetega."; @@ -52,6 +55,9 @@ /* No comment provided by engineer. */ "Dev environment" = "Arenduskeskkond"; +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "Sinu andmed on sünkroonitud!"; + /* Standard Buttons - Done Button */ "done.button" = "Valmis"; @@ -226,6 +232,9 @@ /* Title of the warning message */ "sync.warning.sync.unavailable" = "Sünkroonimine ja varundamine pole saadaval"; +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Sünkrooni teise seadmega"; + /* Footer message for syncing with another device */ "sync.with.another.device.footer" = "Sinu andmed on otsast lõpuni krüptitud ja DuckDuckGo ei pääse krüpteerimisvõtmele ligi."; @@ -253,6 +262,9 @@ /* Options - Unify Favorites Instruction */ "unified.favorites.instruction" = "Kasuta kõigis oma seadmetes samu lemmikjärjehoidjaid. Kui soovid mobiili ja arvuti lemmikud lahus hoida, siis ära seda sisse lülita."; +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Lemmikute sünkroonimine kõigis seadmetes"; + /* View Text Code menu item */ "view.text.code.menu.item" = "Kuva tekstikood"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/fi.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/fi.lproj/Localizable.strings index 2b0b6c3e8c..49bed1e7f8 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/fi.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/fi.lproj/Localizable.strings @@ -28,6 +28,9 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "Peruuta"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "Ota käyttöön synkronointi ja varmuuskopiointi"; + /* Connect With Server Sheet - Description Part 1 */ "connect.with.server.sheet.description.part1" = "Tämä luo salatun varmuuskopion kirjanmerkeistäsi ja salasanoistasi DuckDuckGon suojatulle palvelimelle. Voit synkronoida varmuuskopion muiden laitteittesi kanssa."; @@ -52,6 +55,9 @@ /* No comment provided by engineer. */ "Dev environment" = "Dev-ympäristö"; +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "Tietosi on synkronoitu!"; + /* Standard Buttons - Done Button */ "done.button" = "Valmis"; @@ -226,6 +232,9 @@ /* Title of the warning message */ "sync.warning.sync.unavailable" = "Synkronointi ja varmuuskopiointi ei ole käytettävissä"; +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Synkronoi toisen laitteen kanssa"; + /* Footer message for syncing with another device */ "sync.with.another.device.footer" = "Tietosi on salattu päästä päähän, eikä DuckDuckGolla ole hallussaan salausavainta."; @@ -253,6 +262,9 @@ /* Options - Unify Favorites Instruction */ "unified.favorites.instruction" = "Käytä samoja suosikkikirjanmerkkejä kaikilla laitteillasi. Jätä pois, jos haluat pitää mobiili- ja työpöytäsuosikit erillään."; +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Yhdistä suosikit eri laitteilla"; + /* View Text Code menu item */ "view.text.code.menu.item" = "Näytä tekstikoodi"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/fr.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/fr.lproj/Localizable.strings index 786f127f21..8ef0018e51 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/fr.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/fr.lproj/Localizable.strings @@ -28,6 +28,9 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "Annuler"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "Activer Synchronisation et sauvegarde"; + /* Connect With Server Sheet - Description Part 1 */ "connect.with.server.sheet.description.part1" = "Cela crée une sauvegarde cryptée de vos signets et mots de passe sur le serveur sécurisé de DuckDuckGo, qui peut être synchronisée avec vos autres appareils."; @@ -52,6 +55,9 @@ /* No comment provided by engineer. */ "Dev environment" = "Environnement de développement"; +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "Vos données sont synchronisées !"; + /* Standard Buttons - Done Button */ "done.button" = "Terminé"; @@ -226,6 +232,9 @@ /* Title of the warning message */ "sync.warning.sync.unavailable" = "Synchronisation et sauvegarde n'est pas disponible"; +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Synchroniser avec un autre appareil"; + /* Footer message for syncing with another device */ "sync.with.another.device.footer" = "Vos données sont cryptées de bout en bout et DuckDuckGo n'a pas accès à la clé de chiffrement."; @@ -253,6 +262,9 @@ /* Options - Unify Favorites Instruction */ "unified.favorites.instruction" = "Utilisez les mêmes signets favoris sur tous vos appareils. Laissez désactivé pour séparer les favoris sur mobile et ceux sur ordinateur de bureau."; +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Unifier les favoris sur tous les appareils"; + /* View Text Code menu item */ "view.text.code.menu.item" = "Afficher le code texte"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/hr.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/hr.lproj/Localizable.strings index 18575d84d2..f84cfd7dec 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/hr.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/hr.lproj/Localizable.strings @@ -28,6 +28,9 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "Otkaži"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "Uključi sinkronizaciju i sigurnosno kopiranje"; + /* Connect With Server Sheet - Description Part 1 */ "connect.with.server.sheet.description.part1" = "To stvara šifriranu sigurnosnu kopiju tvojih oznaka i lozinki na sigurnom poslužitelju DuckDuckGo, koja se može sinkronizirati s drugim uređajima."; @@ -52,6 +55,9 @@ /* No comment provided by engineer. */ "Dev environment" = "Dev okruženje"; +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "Tvoji su podaci sinkronizirani!"; + /* Standard Buttons - Done Button */ "done.button" = "Gotovo"; @@ -226,6 +232,9 @@ /* Title of the warning message */ "sync.warning.sync.unavailable" = "Sinkronizacija i sigurnosno kopiranje nisu dostupni"; +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Sinkronizacija s drugim uređajem"; + /* Footer message for syncing with another device */ "sync.with.another.device.footer" = "Tvoji su podaci šifrirani od početka do kraja, a DuckDuckGo nema pristup ključu za šifriranje."; @@ -253,6 +262,9 @@ /* Options - Unify Favorites Instruction */ "unified.favorites.instruction" = "Koristi iste omiljene oznake na svim svojim uređajima. Isključi za odvajanje favorita za mobilne uređaje i stolna računala."; +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Objedini favorite na svim uređajima"; + /* View Text Code menu item */ "view.text.code.menu.item" = "Prikaz tekstne šifre"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/hu.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/hu.lproj/Localizable.strings index 0c5550a902..b3eb15be6c 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/hu.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/hu.lproj/Localizable.strings @@ -28,6 +28,9 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "Mégsem"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "Szinkronizálás és biztonsági mentés bekapcsolása"; + /* Connect With Server Sheet - Description Part 1 */ "connect.with.server.sheet.description.part1" = "Ez a DuckDuckGo biztonságos szerverén egy titkosított biztonsági másolatot készít a könyvjelzőidről és jelszavaidról, amelyet más eszközökkel szinkronizálhatsz."; @@ -52,6 +55,9 @@ /* No comment provided by engineer. */ "Dev environment" = "Fejlesztői környezet"; +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "Az adatszinkronizálás megtörtént."; + /* Standard Buttons - Done Button */ "done.button" = "Kész"; @@ -226,6 +232,9 @@ /* Title of the warning message */ "sync.warning.sync.unavailable" = "Szinkronizálás és biztonsági mentés nem érhető el"; +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Szinkronizálás másik eszközzel"; + /* Footer message for syncing with another device */ "sync.with.another.device.footer" = "Az adataid végponttól végpontig titkosítva vannak, a DuckDuckGo pedig nem fér hozzá a titkosítási kulcshoz."; @@ -253,6 +262,9 @@ /* Options - Unify Favorites Instruction */ "unified.favorites.instruction" = "Használd ugyanazokat a kedvenc könyvjelzőket minden eszközön. Hagyd kikapcsolva a mobil és asztali kedvencek különválasztásához."; +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Kedvencek egyesítése az eszközökön"; + /* View Text Code menu item */ "view.text.code.menu.item" = "Szöveges kód megtekintése"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/it.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/it.lproj/Localizable.strings index 624f498e36..2b1353a56d 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/it.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/it.lproj/Localizable.strings @@ -28,6 +28,9 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "Annulla"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "Attiva Sincronizzazione e backup"; + /* Connect With Server Sheet - Description Part 1 */ "connect.with.server.sheet.description.part1" = "La funzione crea un backup criptato dei tuoi segnalibri e delle tue password sul server sicuro di DuckDuckGo, in modo che possa essere sincronizzato con gli altri dispositivi."; @@ -52,6 +55,9 @@ /* No comment provided by engineer. */ "Dev environment" = "Ambiente di sviluppo"; +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "I tuoi dati sono sincronizzati!"; + /* Standard Buttons - Done Button */ "done.button" = "Fatto"; @@ -226,6 +232,9 @@ /* Title of the warning message */ "sync.warning.sync.unavailable" = "Sincronizzazione e backup non disponibile"; +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Sincronizza con un altro dispositivo"; + /* Footer message for syncing with another device */ "sync.with.another.device.footer" = "I tuoi dati sono crittografati end-to-end e DuckDuckGo non ha accesso alla chiave di crittografia."; @@ -253,6 +262,9 @@ /* Options - Unify Favorites Instruction */ "unified.favorites.instruction" = "Usa gli stessi segnalibri preferiti su tutti i tuoi dispositivi. Lascia l'opzione disattivata per tenere separati i preferiti per dispositivi mobili e desktop."; +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Unifica i preferiti su tutti i dispositivi"; + /* View Text Code menu item */ "view.text.code.menu.item" = "Voce di menu Visualizza codice SMS"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/lt.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/lt.lproj/Localizable.strings index 845f8530a9..3f8d4e06d0 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/lt.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/lt.lproj/Localizable.strings @@ -28,6 +28,9 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "Atšaukti"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "Įjunkite sinchronizavimą ir atsarginės kopijos kūrimą"; + /* Connect With Server Sheet - Description Part 1 */ "connect.with.server.sheet.description.part1" = "Taip „DuckDuckGo“ saugiame serveryje sukuriama užšifruota atsarginė žymių ir slaptažodžių kopija, kurią galima sinchronizuoti su kitais įrenginiais."; @@ -52,6 +55,9 @@ /* No comment provided by engineer. */ "Dev environment" = "Kūrėjo aplinka"; +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "Jūsų duomenys sinchronizuoti!"; + /* Standard Buttons - Done Button */ "done.button" = "Atlikta"; @@ -226,6 +232,9 @@ /* Title of the warning message */ "sync.warning.sync.unavailable" = "Sinchronizavimas ir atsarginės kopijos kūrimas nepasiekiami"; +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Sinchronizuoti su kitu įrenginiu"; + /* Footer message for syncing with another device */ "sync.with.another.device.footer" = "Jūsų duomenys šifruojami ištisiniu būdu, o „DuckDuckGo“ neturi prieigos prie šifravimo rakto."; @@ -253,6 +262,9 @@ /* Options - Unify Favorites Instruction */ "unified.favorites.instruction" = "Visuose įrenginiuose naudokite tas pačias mėgstamiausias žymes. Palikite išjungtą, kad mobiliojo ir stacionaraus įrenginių parankiniai būtų atskirti."; +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Suvienodinti adresynus visuose įrenginiuose"; + /* View Text Code menu item */ "view.text.code.menu.item" = "Peržiūrėti teksto kodą"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/lv.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/lv.lproj/Localizable.strings index 1593d80f38..65a5800526 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/lv.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/lv.lproj/Localizable.strings @@ -28,6 +28,9 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "Atcelt"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "Ieslēgt sinhronizāciju un dublēšanu"; + /* Connect With Server Sheet - Description Part 1 */ "connect.with.server.sheet.description.part1" = "Tādējādi DuckDuckGo drošajā serverī tiek izveidota šifrēta grāmatzīmju un paroļu dublējumkopija, ko var sinhronizēt ar citām ierīcēm."; @@ -52,6 +55,9 @@ /* No comment provided by engineer. */ "Dev environment" = "Izstrādes vide"; +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "Tavi dati ir sinhronizēti!"; + /* Standard Buttons - Done Button */ "done.button" = "Gatavs"; @@ -226,6 +232,9 @@ /* Title of the warning message */ "sync.warning.sync.unavailable" = "Sinhronizācija un dublēšana nav pieejama"; +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Sinhronizēt ar citu ierīci"; + /* Footer message for syncing with another device */ "sync.with.another.device.footer" = "Tavi dati tiek šifrēti no gala līdz galam, un DuckDuckGo nav piekļuves šifrēšanas atslēgai."; @@ -253,6 +262,9 @@ /* Options - Unify Favorites Instruction */ "unified.favorites.instruction" = "Izmanto vienas grāmatzīmes visās savās ierīcēs. Atstāj opciju izslēgtu, lai nodalītu mobilās un galddatora izlases."; +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Izlases apvienošana visās ierīcēs"; + /* View Text Code menu item */ "view.text.code.menu.item" = "Skatīt teksta kodu"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/nb.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/nb.lproj/Localizable.strings index 37f0fba483..f6cafc9052 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/nb.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/nb.lproj/Localizable.strings @@ -28,6 +28,9 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "Avbryt"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "Slå på Synkronisering og sikkerhetskopiering"; + /* Connect With Server Sheet - Description Part 1 */ "connect.with.server.sheet.description.part1" = "Dette oppretter en kryptert sikkerhetskopi av bokmerkene og passordene dine på DuckDuckGos sikre server, som kan synkroniseres med dine andre enheter."; @@ -52,6 +55,9 @@ /* No comment provided by engineer. */ "Dev environment" = "Utviklermiljø"; +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "Dataene dine er synkronisert!"; + /* Standard Buttons - Done Button */ "done.button" = "Ferdig"; @@ -226,6 +232,9 @@ /* Title of the warning message */ "sync.warning.sync.unavailable" = "Synkronisering og sikkerhetskopiering er utilgjengelig"; +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Synkroniser med en annen enhet"; + /* Footer message for syncing with another device */ "sync.with.another.device.footer" = "Dataene dine er ende-til-ende-kryptert og DuckDuckGo har ikke tilgang til krypteringsnøkkelen."; @@ -253,6 +262,9 @@ /* Options - Unify Favorites Instruction */ "unified.favorites.instruction" = "Bruk de samme favorittbokmerkene på alle enhetene dine. La dette valget være avslått hvis du vil holde mobil- og skrivebordsfavoritter adskilt."; +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Foren favoritter på tvers av enheter"; + /* View Text Code menu item */ "view.text.code.menu.item" = "Vis tekstkode"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/nl.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/nl.lproj/Localizable.strings index fe8432e900..b475c09771 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/nl.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/nl.lproj/Localizable.strings @@ -28,6 +28,9 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "Annuleren"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "'Synchronisatie en back-up' inschakelen"; + /* Connect With Server Sheet - Description Part 1 */ "connect.with.server.sheet.description.part1" = "Hiermee maak je een versleutelde back-up van je bladwijzers en wachtwoorden op de beveiligde server van DuckDuckGo. Deze back-up kan met je andere apparaten worden gesynchroniseerd."; @@ -52,6 +55,9 @@ /* No comment provided by engineer. */ "Dev environment" = "Ontwikkelomgeving"; +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "Je gegevens zijn gesynchroniseerd!"; + /* Standard Buttons - Done Button */ "done.button" = "Klaar"; @@ -226,6 +232,9 @@ /* Title of the warning message */ "sync.warning.sync.unavailable" = "'Synchronisatie en back-up' is niet beschikbaar"; +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Synchroniseren met een ander apparaat"; + /* Footer message for syncing with another device */ "sync.with.another.device.footer" = "Je gegevens zijn volledig versleuteld en DuckDuckGo heeft geen toegang tot de versleutelingscode."; @@ -253,6 +262,9 @@ /* Options - Unify Favorites Instruction */ "unified.favorites.instruction" = "Gebruik dezelfde favoriete bladwijzers op al je apparaten. Schakel deze optie uit als je je favoriete bladwijzers niet wilt synchroniseren tussen je mobiele apparaat en pc."; +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Beschik over dezelfde favorieten op verschillende apparaten"; + /* View Text Code menu item */ "view.text.code.menu.item" = "Tekstcode weergeven"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/pl.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/pl.lproj/Localizable.strings index bd945abe6a..52ce200641 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/pl.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/pl.lproj/Localizable.strings @@ -28,6 +28,9 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "Anuluj"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "Włącz synchronizację i kopię zapasową"; + /* Connect With Server Sheet - Description Part 1 */ "connect.with.server.sheet.description.part1" = "Tworzy na bezpiecznym serwerze DuckDuckGo zaszyfrowaną kopię zapasową zakładek i haseł, którą można zsynchronizować z innymi urządzeniami."; @@ -52,6 +55,9 @@ /* No comment provided by engineer. */ "Dev environment" = "Środowisko programisty"; +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "Twoje dane są synchronizowane!"; + /* Standard Buttons - Done Button */ "done.button" = "Gotowe"; @@ -226,6 +232,9 @@ /* Title of the warning message */ "sync.warning.sync.unavailable" = "Synchronizacja i kopia zapasowa jest niedostępna"; +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Synchronizuj z innym urządzeniem"; + /* Footer message for syncing with another device */ "sync.with.another.device.footer" = "Twoje dane są kompleksowo szyfrowane, a DuckDuckGo nie ma dostępu do klucza szyfrowania."; @@ -253,6 +262,9 @@ /* Options - Unify Favorites Instruction */ "unified.favorites.instruction" = "Korzystaj z tych samych ulubionych zakładek na wszystkich swoich urządzeniach. Wyłącz, aby oddzielić ulubione elementy na urządzenia mobilne i stacjonarne."; +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Połącz ulubione elementy na urządzeniach"; + /* View Text Code menu item */ "view.text.code.menu.item" = "Wyświetl kod tekstowy"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/pt.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/pt.lproj/Localizable.strings index 1bba6587e6..8490c08eb1 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/pt.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/pt.lproj/Localizable.strings @@ -28,6 +28,9 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "Cancelar"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "Ativar a sincronização e cópia de segurança"; + /* Connect With Server Sheet - Description Part 1 */ "connect.with.server.sheet.description.part1" = "Cria uma cópia de segurança encriptada dos teus marcadores e palavras-passe no servidor seguro do DuckDuckGo, que pode ser sincronizada com os teus restantes dispositivos."; @@ -52,6 +55,9 @@ /* No comment provided by engineer. */ "Dev environment" = "Ambiente de desenvolvimento"; +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "Os teus dados estão sincronizados!"; + /* Standard Buttons - Done Button */ "done.button" = "Feito"; @@ -226,6 +232,9 @@ /* Title of the warning message */ "sync.warning.sync.unavailable" = "A sincronização e a cópia de segurança não estão disponíveis"; +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Sincronizar com outro dispositivo"; + /* Footer message for syncing with another device */ "sync.with.another.device.footer" = "Os teus dados são encriptados de ponta a ponta e o DuckDuckGo não tem acesso à chave de encriptação."; @@ -253,6 +262,9 @@ /* Options - Unify Favorites Instruction */ "unified.favorites.instruction" = "Usa os mesmos marcadores favoritos em todos os teus dispositivos. Deixa desativado para manter separados os favoritos em dispositivos móveis e computadores."; +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Unifica os favoritos entre dispositivos"; + /* View Text Code menu item */ "view.text.code.menu.item" = "Ver código de texto"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/ro.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/ro.lproj/Localizable.strings index 0f2903e050..67a504166d 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/ro.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/ro.lproj/Localizable.strings @@ -28,6 +28,9 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "Renunță"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "Activează Sincronizare și backup"; + /* Connect With Server Sheet - Description Part 1 */ "connect.with.server.sheet.description.part1" = "Aceasta creează o copie de rezervă criptată a marcajelor și parolelor tale pe serverul securizat DuckDuckGo, care poate fi sincronizată cu celelalte dispozitive ale tale."; @@ -52,6 +55,9 @@ /* No comment provided by engineer. */ "Dev environment" = "Mediu de dezvoltare"; +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "Datele tale sunt sincronizate!"; + /* Standard Buttons - Done Button */ "done.button" = "Terminat"; @@ -226,6 +232,9 @@ /* Title of the warning message */ "sync.warning.sync.unavailable" = "Sincronizare și backup nu este disponibil"; +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Sincronizează cu un alt dispozitiv"; + /* Footer message for syncing with another device */ "sync.with.another.device.footer" = "Datele tale sunt criptate integral, iar DuckDuckGo nu are acces la cheia de criptare."; @@ -253,6 +262,9 @@ /* Options - Unify Favorites Instruction */ "unified.favorites.instruction" = "Utilizează aceleași marcaje favorite pe toate dispozitivele tale. Lasă dezactivat pentru a păstra favoritele pentru mobil și desktop separate."; +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Unifică favoritele pe toate dispozitivele"; + /* View Text Code menu item */ "view.text.code.menu.item" = "Vezi codul text"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/ru.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/ru.lproj/Localizable.strings index 98c62e6ed3..ce89310fab 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/ru.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/ru.lproj/Localizable.strings @@ -28,6 +28,9 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "Отменить"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "Включить синхронизацию и копирование"; + /* Connect With Server Sheet - Description Part 1 */ "connect.with.server.sheet.description.part1" = "DuckDuckGo создаст зашифрованную резервную копию ваших закладок и паролей на защищенном сервере. Копию можно синхронизировать с другими устройствами."; @@ -52,6 +55,9 @@ /* No comment provided by engineer. */ "Dev environment" = "Среда разработки"; +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "Данные синхронизированы!"; + /* Standard Buttons - Done Button */ "done.button" = "Готово"; @@ -226,6 +232,9 @@ /* Title of the warning message */ "sync.warning.sync.unavailable" = "Функция «Синхронизация и резервное копирование» недоступна"; +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Синхронизировать с другим устройством"; + /* Footer message for syncing with another device */ "sync.with.another.device.footer" = "Ваши данные защищены сквозным шифрованием. У DuckDuckGo нет доступа к ключу шифрования."; @@ -253,6 +262,9 @@ /* Options - Unify Favorites Instruction */ "unified.favorites.instruction" = "Использовать одинаковые закладки на всех устройствах. Не включайте, чтобы использовать разное избранное в мобильной и настольной версиях браузера."; +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Одно и то же избранное на разных устройствах"; + /* View Text Code menu item */ "view.text.code.menu.item" = "Показать текстовый код"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/sk.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/sk.lproj/Localizable.strings index 64f429bb8b..b4fe82a9e7 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/sk.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/sk.lproj/Localizable.strings @@ -28,6 +28,9 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "Zrušiť"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "Zapnite synchronizáciu a zálohovanie"; + /* Connect With Server Sheet - Description Part 1 */ "connect.with.server.sheet.description.part1" = "Vytvorí sa tak šifrovaná záloha vašich záložiek a hesiel na zabezpečenom serveri DuckDuckGo, ktorú môžete synchronizovať s ostatnými zariadeniami."; @@ -52,6 +55,9 @@ /* No comment provided by engineer. */ "Dev environment" = "Vývojárske prostredie"; +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "Vaše údaje sú synchronizované!"; + /* Standard Buttons - Done Button */ "done.button" = "Hotovo"; @@ -226,6 +232,9 @@ /* Title of the warning message */ "sync.warning.sync.unavailable" = "Synchronizácia a zálohovanie nie je k dispozícii"; +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Synchronizácia s iným zariadením"; + /* Footer message for syncing with another device */ "sync.with.another.device.footer" = "Vaše údaje sú šifrované na oboch koncoch (end-to-end) a DuckDuckGo nemá prístup k šifrovaciemu kľúču."; @@ -253,6 +262,9 @@ /* Options - Unify Favorites Instruction */ "unified.favorites.instruction" = "Používajte rovnaké obľúbené záložky vo všetkých svojich zariadeniach. Vypnite, aby obľúbené položky z mobilných zariadení a počítačov zostali oddelené."; +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Zjednotenie obľúbených položiek medzi zariadeniami"; + /* View Text Code menu item */ "view.text.code.menu.item" = "Zobraziť textový kód"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/sl.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/sl.lproj/Localizable.strings index ce86722617..9c8549f278 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/sl.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/sl.lproj/Localizable.strings @@ -28,6 +28,9 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "Prekliči"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "Vklopite sinhronizacijo in varnostno kopiranje"; + /* Connect With Server Sheet - Description Part 1 */ "connect.with.server.sheet.description.part1" = "S tem ustvarite šifrirano varnostno kopijo zaznamkov in gesel v varnem strežniku DuckDuckGo, ki jo lahko sinhronizirate z drugimi napravami."; @@ -52,6 +55,9 @@ /* No comment provided by engineer. */ "Dev environment" = "Razvojno okolje"; +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "Vaši podatki so sinhronizirani!"; + /* Standard Buttons - Done Button */ "done.button" = "Končano"; @@ -226,6 +232,9 @@ /* Title of the warning message */ "sync.warning.sync.unavailable" = "Sinhronizacija in varnostno kopiranje nista na voljo"; +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Sinhronizacija z drugo napravo"; + /* Footer message for syncing with another device */ "sync.with.another.device.footer" = "Vaši podatki so celovito šifrirani in DuckDuckGo nima dostopa do šifrirnega ključa."; @@ -253,6 +262,9 @@ /* Options - Unify Favorites Instruction */ "unified.favorites.instruction" = "Uporabljajte iste priljubljene zaznamke v vseh svojih napravah. Če želite ločiti priljubljene zaznamke za mobilne naprave in namizne računalnike, pustite izklopljeno."; +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Poenotite priljubljene v vseh napravah"; + /* View Text Code menu item */ "view.text.code.menu.item" = "Prikaži besedilno kodo"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/sv.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/sv.lproj/Localizable.strings index 03157d722f..69a1f6de0f 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/sv.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/sv.lproj/Localizable.strings @@ -28,6 +28,9 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "Avbryt"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "Aktivera synkronisering och säkerhetskopiering"; + /* Connect With Server Sheet - Description Part 1 */ "connect.with.server.sheet.description.part1" = "Detta skapar en krypterad säkerhetskopia av dina bokmärken och lösenord på DuckDuckGos säkra server, som kan synkroniseras med dina andra enheter."; @@ -52,6 +55,9 @@ /* No comment provided by engineer. */ "Dev environment" = "Utvecklingsmiljö"; +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "Dina data har synkroniserats!"; + /* Standard Buttons - Done Button */ "done.button" = "Klart"; @@ -226,6 +232,9 @@ /* Title of the warning message */ "sync.warning.sync.unavailable" = "Synkronisering och säkerhetskopiering är inte tillgängligt"; +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Synkronisera med en annan enhet"; + /* Footer message for syncing with another device */ "sync.with.another.device.footer" = "Dina data krypteras under hela vägen och DuckDuckGo har inte tillgång till krypteringsnyckeln."; @@ -253,6 +262,9 @@ /* Options - Unify Favorites Instruction */ "unified.favorites.instruction" = "Använd samma favoritbokmärken på alla dina enheter. Använd inte funktionen om du vill skilja på favoriter mellan mobil och dator."; +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Samla alla favoriter på olika enheter"; + /* View Text Code menu item */ "view.text.code.menu.item" = "Visa textkod"; diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/tr.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/tr.lproj/Localizable.strings index be8f9f9f01..d6f1ed16d0 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/tr.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/tr.lproj/Localizable.strings @@ -2,25 +2,25 @@ "back.button" = "Geri"; /* Sync Paused Errors - Bookmarks Limit Exceeded Action */ -"bookmarks.limit.exceeded.action" = "Manage Bookmarks"; +"bookmarks.limit.exceeded.action" = "Yer İmlerini Yönet"; /* Sync Paused Errors - Bookmarks Limit Exceeded Description */ -"bookmarks.limit.exceeded.description" = "Bookmark limit exceeded. Delete some to resume syncing."; +"bookmarks.limit.exceeded.description" = "Yer imi sınırı aşıldı. Senkronizasyona devam etmek için bazılarını silin."; /* Camera View - Go to Settings Button */ "camera.go.to.settings.button" = "Ayarlar'a gidin"; /* Camera View - Unavailable Title */ -"camera.is.unavailable.title" = "Camera is Unavailable"; +"camera.is.unavailable.title" = "Kamera Kullanılamıyor"; /* Camera View - Permission Instructions */ -"camera.permission.instructions" = "Please go to your device's settings and grant permission for this app to access your camera."; +"camera.permission.instructions" = "Lütfen cihazınızın ayarlarına gidin ve bu uygulamanın kameranıza erişmesine izin verin."; /* Camera View - Permission Required */ -"camera.permission.required" = "Camera Permission is Required"; +"camera.permission.required" = "Kamera İzni Gereklidir"; /* Camera View - Point Camera Indication */ -"camera.point.camera.indication" = "Point camera at QR code to sync"; +"camera.point.camera.indication" = "Senkronize etmek için kamerayı QR koduna doğrultun"; /* No comment provided by engineer. */ "Cancel" = "İptal"; @@ -28,29 +28,35 @@ /* Standard Buttons - Cancel Button */ "cancel.button" = "İptal"; +/* Connect With Server Sheet - Button */ +"connect.with.server.sheet.button" = "Senkronizasyon ve Yedekleme'yi açın"; + /* Connect With Server Sheet - Description Part 1 */ -"connect.with.server.sheet.description.part1" = "This creates an encrypted backup of your bookmarks and passwords on DuckDuckGo’s secure server, which can be synced with your other devices."; +"connect.with.server.sheet.description.part1" = "Bu, DuckDuckGo'nun güvenli sunucusunda yer imlerinizin ve şifrelerinizi diğer cihazlarınızla senkronize edilebilen şifrelenmiş bir yedeğini oluşturur."; /* Connect With Server Sheet - Description Part 2 */ -"connect.with.server.sheet.description.part2" = "The encryption key is only stored on your device, DuckDuckGo cannot access it."; +"connect.with.server.sheet.description.part2" = "Şifreleme anahtarı sadece cihazınızda saklanır, DuckDuckGo buna erişemez."; /* Connect With Server Sheet - Footer */ -"connect.with.server.sheet.footer" = "You can sync with your other devices later."; +"connect.with.server.sheet.footer" = "Daha sonra diğer cihazlarınızla senkronize edebilirsiniz."; /* Connect With Server Sheet - Title */ -"connect.with.server.sheet.title" = "Sync and Back Up This Device"; +"connect.with.server.sheet.title" = "Bu Cihazı Senkronize Et ve Yedekle"; /* Sync Paused Errors - Credentials Limit Exceeded Action */ -"credentials.limit.exceeded.action" = "Manage Logins"; +"credentials.limit.exceeded.action" = "Girişleri Yönet"; /* Sync Paused Errors - Credentials Limit Exceeded Description */ -"credentials.limit.exceeded.description" = "Logins limit exceeded. Delete some to resume syncing."; +"credentials.limit.exceeded.description" = "Giriş sınırı aşıldı. Senkronizasyona devam etmek için bazılarını silin."; /* Delete Server Data - Button */ -"delete.server.data" = "Turn Off and Delete Server Data..."; +"delete.server.data" = "Sunucu Verilerini Kapat ve Sil..."; /* No comment provided by engineer. */ -"Dev environment" = "Dev environment"; +"Dev environment" = "Geliştirme ortamı"; + +/* Device SyncedSheet - Title */ +"device.synced.sheet.title" = "Verileriniz Senkronize Edildi!"; /* Standard Buttons - Done Button */ "done.button" = "Bitti"; @@ -59,37 +65,37 @@ "edit.device.header" = "Cihaz Adı"; /* Edit Device View - Title */ -"edit.device.title" = "Edit %@"; +"edit.device.title" = "Düzenle %@"; /* Fetch Favicons Onboarding - Button Title */ -"fetch.favicons.onboarding.button.title" = "Keep Bookmarks Icons Updated"; +"fetch.favicons.onboarding.button.title" = "Yer İmi Simgelerini Güncel Tut"; /* Fetch Favicons Onboarding - Message */ -"fetch.favicons.onboarding.message" = "Do you want this device to automatically download icons for any new bookmarks synced from your other devices? This will expose the download to your network any time a bookmark is synced."; +"fetch.favicons.onboarding.message" = "Bu cihazın diğer cihazlarınızdan senkronize edilen yeni yer imleri için simgeleri otomatik olarak indirmesini istiyor musunuz? Bu, bir yer imi her senkronize edildiğinde indirmeyi ağınıza gösterecektir."; /* Fetch Favicons Onboarding - Title */ -"fetch.favicons.onboarding.title" = "Download Missing Icons?"; +"fetch.favicons.onboarding.title" = "Kayıp Simgeler İndirilsin mi?"; /* Options - Fetch Favicons Description */ -"fetch.favicons.option.caption" = "Automatically download icons for your synced bookmarks. Icon downloads are exposed to your network."; +"fetch.favicons.option.caption" = "Senkronize edilmiş yer imleriniz için simgeleri otomatik olarak indirin. İndirilen simgeler ağınıza açık olur."; /* Options - Fetch Favicons Title */ -"fetch.favicons.option.title" = "Auto-Download Icons"; +"fetch.favicons.option.title" = "Simgeleri Otomatik İndir"; /* No comment provided by engineer. */ -"Keep Development" = "Keep Development"; +"Keep Development" = "Geliştirmeye Devam Et"; /* Manually Enter Code View - Instruction with sync menu path and view text code menu item inserted */ -"manually.enter.code.instruction.attributed" = "Go to %1$@ and select %2$@ in the DuckDuckGo App on another synced device and paste the code here to sync this device."; +"manually.enter.code.instruction.attributed" = "Senkronize edilmiş başka bir cihazdaki DuckDuckGo Uygulamasında %1$@ ögesine gidin ve %2$@ ögesini seçin ve bu cihazı senkronize etmek için kodu buraya yapıştırın."; /* Manually Enter Code View - Title */ -"manually.enter.code.title" = "Manually Enter Code"; +"manually.enter.code.title" = "Kodu Manuel Olarak Yaz"; /* Manually Enter Code View - Validating Code Action */ -"manually.enter.code.validating.code.action" = "Validating code"; +"manually.enter.code.validating.code.action" = "Kod doğrulanıyor"; /* Manually Enter Code View - Validating Code Failed Action */ -"manually.enter.code.validating.code.failed.action" = "Invalid code."; +"manually.enter.code.validating.code.failed.action" = "Geçersiz kod."; /* Standard Buttons - Next Button */ "next.button" = "Sonraki"; @@ -101,115 +107,115 @@ "options.section.header" = "Seçenekler"; /* Section header for other syncing options */ -"other.options.section.header" = "Other Options"; +"other.options.section.header" = "Diğer Seçenekler"; /* Standard Buttons - Paste Button */ -"paste.button" = "Paste"; +"paste.button" = "Yapıştır"; /* Description of rollout banner */ -"preferences.sync.rollout-banner.description" = "Sync & Backup is rolling out gradually and may not be available yet within DuckDuckGo on your other devices."; +"preferences.sync.rollout-banner.description" = "Senkronizasyon ve Yedekleme kademeli olarak kullanıma sunulmaktadır ve diğer cihazlarınızda DuckDuckGo'da henüz mevcut olmayabilir."; /* Preparing To Sync Sheet - Description */ -"preparing.to.sync.sheet.description" = "Your bookmarks and passwords are being prepared to sync. This should only take a moment."; +"preparing.to.sync.sheet.description" = "Yer imleriniz ve şifreler senkronize edilmek üzere hazırlanıyor. Bu sadece bir dakikanızı alacaktır."; /* Preparing To Sync Sheet - Footer */ -"preparing.to.sync.sheet.footer" = "Connecting…"; +"preparing.to.sync.sheet.footer" = "Bağlantı kuruluyor..."; /* Preparing To Sync Sheet - Title */ -"preparing.to.sync.sheet.title" = "Setting Up Sync and Backup"; +"preparing.to.sync.sheet.title" = "Senkronizasyon ve Yedeklemeyi Ayarlama"; /* Link label for recovering synced data */ -"recover.synced.data.link" = "Recover Synced Data"; +"recover.synced.data.link" = "Senkronize Edilen Verileri Kurtar"; /* Recover Synced Data Sheet - Button */ "recover.synced.data.sheet.button" = "Başlayın"; /* Recover Synced Data Sheet - Description */ -"recover.synced.data.sheet.description" = "To restore your synced data, you'll need the Recovery Code you saved when you first set up Sync. This code may have been saved as a PDF on the device you originally used to set up Sync."; +"recover.synced.data.sheet.description" = "Senkronize edilmiş verilerinizi geri yüklemek için Senkronizasyonu ilk kez ayarlarken belirlediğiniz Kurtarma Koduna ihtiyacınız olacaktır. Bu kod, Senkronizasyonu yapmak üzere kullandığınız cihaza PDF olarak kaydedilmiş olabilir."; /* Recover Synced Data Sheet - Title */ -"recover.synced.data.sheet.title" = "Recover Synced Data"; +"recover.synced.data.sheet.title" = "Senkronize Edilen Verileri Kurtar"; /* Remove Device View - Button */ -"remove.device.button" = "Remove Device"; +"remove.device.button" = "Cihazı Kaldır"; /* Remove Device View - Message */ "remove.device.message" = "\"%@\" artık senkronize edilmiş verilerinize erişemeyecektir."; /* Remove Device View - Title */ -"remove.device.title" = "Remove Device?"; +"remove.device.title" = "Cihaz Kaldırılsın mı?"; /* Save Recovery Code Sheet - Copy Code Toast */ -"save.recovery.code.code.copied.button" = "Recovery code copied to clipboard"; +"save.recovery.code.code.copied.button" = "Kurtarma kodu panoya kopyalandı"; /* Save Recovery Code Sheet - Copy Code Button */ -"save.recovery.code.copy.code.button" = "Copy Code"; +"save.recovery.code.copy.code.button" = "Kodu Kopyala"; /* Save Recovery Code Sheet - Save as PDF Button */ -"save.recovery.code.save.as.pdf.button" = "Save as PDF"; +"save.recovery.code.save.as.pdf.button" = "PDF olarak kaydet"; /* Save Recovery Code Sheet - Description */ -"save.recovery.code.sheet.description" = "If you lose access to your devices, you will need this code to recover your synced data."; +"save.recovery.code.sheet.description" = "Cihazlarınıza erişiminizi kaybederseniz, senkronize edilmiş verilerinizi kurtarmak için bu koda ihtiyacınız olacaktır."; /* Save Recovery Code Sheet - Footer */ -"save.recovery.code.sheet.footer" = "Anyone with access to this code can access your synced data, so please keep it in a safe place."; +"save.recovery.code.sheet.footer" = "Bu koda erişimi olan herkes senkronize edilmiş verilerinize erişebileceği için lütfen bu kodu güvenli bir yerde saklayın."; /* Save Recovery Code Sheet - Title */ -"save.recovery.code.sheet.title" = "Save Recovery Code"; +"save.recovery.code.sheet.title" = "Kurtarma Kodunu Kaydet"; /* Save RecoveryPDF - Button */ -"save.recovery.pdf.button" = "Save Recovery PDF"; +"save.recovery.pdf.button" = "Kurtarma PDF'ini Kaydet"; /* Save RecoveryPDF - Footer */ -"save.recovery.pdf.footer" = "If you lose your device, you will need this recovery code to restore your synced data."; +"save.recovery.pdf.footer" = "Cihazınızı kaybederseniz senkronize edilmiş verilerinizi geri yüklemek için bu kurtarma koduna ihtiyacınız olacaktır."; /* Scan Or Enter Code To Recover Synced Data View - Enter Code Link */ -"scan.code.to.recover.synced.data.enter.code.link" = "Enter Text Code Manually"; +"scan.code.to.recover.synced.data.enter.code.link" = "Metin Kodunu Manuel Olarak Yaz"; /* Scan Or Enter Code To Recover Synced Data View - Explanation */ -"scan.code.to.recover.synced.data.explanation" = "Scan the QR code on your Recovery PDF, or another synced device, to recover your data."; +"scan.code.to.recover.synced.data.explanation" = "Verilerinizi kurtarmak için Kurtarma PDF'ndeki veya senkronize edilmiş başka bir cihazdaki QR kodunu tarayın."; /* Scan Or Enter Code To Recover Synced Data View - Footer */ -"scan.code.to.recover.synced.data.footer" = "Can’t Scan?"; +"scan.code.to.recover.synced.data.footer" = "Tarama yapamıyor musunuz?"; /* Scan Or Enter Code To Recover Synced Data View - Title */ -"scan.code.to.recover.synced.data.title" = "Recover Synced Data"; +"scan.code.to.recover.synced.data.title" = "Senkronize Edilen Verileri Kurtar"; /* Scan or See Code View - Footer */ -"scan.or.see.code.footer" = "Can’t Scan?"; +"scan.or.see.code.footer" = "Tarama yapamıyor musunuz?"; /* Scan or See Code View - Instruction */ -"scan.or.see.code.instruction" = "Go to Settings › Sync & Backup in the DuckDuckGo Browser on another device and select ”Sync with Another Device.”"; +"scan.or.see.code.instruction" = "Başka bir cihazdaki DuckDuckGo Tarayıcısında Ayarlar › Senkronizasyon ve Yedekleme bölümüne gidin ve \"Başka Bir Cihazla Senkronize Et\" seçeneğini seçin."; /* Scan or See Code View - Instruction with syncMenuPath */ -"scan.or.see.code.instruction.attributed" = "Go to %@ in the DuckDuckGo Browser on another device and select ”Sync with Another Device.”."; +"scan.or.see.code.instruction.attributed" = "Başka bir cihazdaki DuckDuckGo Tarayıcısında %@ bölümüne gidin ve \"Başka Bir Cihazla Senkronize Et\" seçeneğini seçin."; /* Scan or See Code View - Manually Enter Code Link */ -"scan.or.see.code.manually.enter.code.link" = "Manually Enter Code"; +"scan.or.see.code.manually.enter.code.link" = "Kodu Manuel Olarak Yaz"; /* Scan or See Code View - Scan Code Instructions Body */ -"scan.or.see.code.scan.code.instructions.body" = "Scan this code with another device to sync."; +"scan.or.see.code.scan.code.instructions.body" = "Senkronize etmek için bu kodu başka bir cihazla tarayın."; /* Scan or See Code View - Scan Code Instructions Title */ -"scan.or.see.code.scan.code.instructions.title" = "Mobile-to-Mobile?"; +"scan.or.see.code.scan.code.instructions.title" = "Mobilden Mobile mi?"; /* Scan or See Code View - Share Code Link */ -"scan.or.see.code.share.code.link" = "Share Text Code?"; +"scan.or.see.code.share.code.link" = "Metin Kodu Paylaşılsın mı?"; /* Scan or See Code View - Title */ -"scan.or.see.code.title" = "Scan QR Code"; +"scan.or.see.code.title" = "QR Kodunu Tara"; /* No comment provided by engineer. */ -"Switch to Production" = "Switch to Production"; +"Switch to Production" = "Üretime Geç"; /* Link label for syncing and backing up the device */ -"sync.and.backup.this.device.link" = "Sync and Back Up This Device"; +"sync.and.backup.this.device.link" = "Bu Cihazı Senkronize Et ve Yedekle"; /* Sync Paused Errors - Title */ -"sync.limit.exceeded.title" = "Sync Paused"; +"sync.limit.exceeded.title" = "Senkronizasyon Duraklatıldı"; /* Sync Menu Path */ -"sync.menu.path" = "Settings > Sync & Backup > Sync With Another Device"; +"sync.menu.path" = "Ayarlar > Senkronizasyon ve Yedekleme > Başka Bir Cihazla Senkronize Et"; /* Sync & Backup Title */ "sync.title" = "Senkronizasyon ve Yedekleme"; @@ -224,38 +230,44 @@ "sync.warning.sync.paused" = "Senkronizasyon ve Yedekleme Duraklatıldı"; /* Title of the warning message */ -"sync.warning.sync.unavailable" = "Sync & Backup is Unavailable"; +"sync.warning.sync.unavailable" = "Senkronizasyon ve Yedekleme Kullanılamıyor"; + +/* Button label for syncing with another device */ +"sync.with.another.device.button" = "Başka Bir Cihazla Senkronize Et"; /* Footer message for syncing with another device */ -"sync.with.another.device.footer" = "Your data is end-to-end encrypted, and DuckDuckGo does not have access to the encryption key."; +"sync.with.another.device.footer" = "Verileriniz uçtan uca şifrelenir ve DuckDuckGo'nun şifreleme anahtarına erişimi yoktur."; /* Message for syncing with another device */ -"sync.with.another.device.message" = "Securely sync bookmarks and passwords between your devices."; +"sync.with.another.device.message" = "Yer imlerini ve parolaları cihazlarınız arasında güvenli bir şekilde senkronize edin."; /* Title for syncing with another device */ -"sync.with.another.device.title" = "Begin Syncing"; +"sync.with.another.device.title" = "Senkronizasyonu Başlat"; /* Synced Devices - Section Header */ -"synced.devices.section.header" = "Synced Devices"; +"synced.devices.section.header" = "Senkronize Edilmiş Cihazlar"; /* Synced Devices - Sync with Another Device Label */ -"synced.devices.sync.with.another.device.label" = "Sync with Another Device"; +"synced.devices.sync.with.another.device.label" = "Başka Bir Cihazla Senkronize Et"; /* Synced Devices - This Device Label */ -"synced.devices.this.device.label" = "This Device"; +"synced.devices.this.device.label" = "Bu Cihaz"; /* Turn Sync Off - Button */ -"turn.sync.off" = "Turn Off Sync & Backup..."; +"turn.sync.off" = "Senkronizasyon ve Yedeklemeyi Kapat..."; /* Turn Sync Off - Section Header */ -"turn.sync.off.section.header" = "Sync Enabled"; +"turn.sync.off.section.header" = "Senkronizasyon Etkin"; /* Options - Unify Favorites Instruction */ -"unified.favorites.instruction" = "Use the same favorite bookmarks on all your devices. Leave off to keep mobile and desktop favorites separate."; +"unified.favorites.instruction" = "Tüm cihazlarınızda aynı favori yer imlerini kullanın. Mobil ve masaüstü favorilerini ayrı tutmak için kapalı bırakın."; + +/* Options - Unify Favorites Title */ +"unified.favorites.title" = "Favorileri Cihazlar Arasında Birleştirin"; /* View Text Code menu item */ -"view.text.code.menu.item" = "View Text Code"; +"view.text.code.menu.item" = "Metin Kodunu Görüntüle"; /* No comment provided by engineer. */ -"You're using Sync Development environment" = "You're using Sync Development environment"; +"You're using Sync Development environment" = "Senkronizasyon Geliştirme ortamını kullanıyorsunuz"; From b723911ef29b98ad197690f89a5cb47735891bca Mon Sep 17 00:00:00 2001 From: Daniel Bernal Date: Thu, 18 Jan 2024 18:09:43 +0100 Subject: [PATCH 10/70] Restore Subscriptions (#2354) Task/Issue URL: https://app.asana.com/0/72649045549333/1205054784245717/f Description: Implements subscription restoration via Apple ID --- DuckDuckGo.xcodeproj/project.pbxproj | 8 + .../Platform-Apple-16.imageset/Contents.json | 15 ++ .../Platform-Apple-16.svg | 10 + ...scriptionPagesUseSubscriptionFeature.swift | 2 + .../ViewModel/SubscriptionFlowViewModel.swift | 9 +- .../SubscriptionRestoreViewModel.swift | 68 ++++++ .../Subscription/Views/HeadlessWebView.swift | 9 +- .../Views/PurchaseInProgressView.swift | 2 +- .../Views/SubscriptionFlowView.swift | 19 +- .../Views/SubscriptionRestoreView.swift | 217 ++++++++++++++++++ .../Views/SubscriptionSettingsView.swift | 1 + DuckDuckGo/UserText.swift | 29 ++- DuckDuckGo/en.lproj/Localizable.strings | 65 +++++- 13 files changed, 425 insertions(+), 29 deletions(-) create mode 100644 DuckDuckGo/Assets.xcassets/Platform-Apple-16.imageset/Contents.json create mode 100644 DuckDuckGo/Assets.xcassets/Platform-Apple-16.imageset/Platform-Apple-16.svg create mode 100644 DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift create mode 100644 DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 3acee7f6cb..d282cb5bbb 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -778,6 +778,8 @@ D664C7CC2B289AA200CBFA76 /* SubscriptionPagesUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664C7B32B289AA000CBFA76 /* SubscriptionPagesUserScript.swift */; }; D664C7CE2B289AA200CBFA76 /* SubscriptionPagesUseSubscriptionFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664C7B52B289AA000CBFA76 /* SubscriptionPagesUseSubscriptionFeature.swift */; }; D664C7DD2B28A02800CBFA76 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D664C7DC2B28A02800CBFA76 /* StoreKit.framework */; }; + D68DF81C2B58302E0023DBEA /* SubscriptionRestoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68DF81B2B58302E0023DBEA /* SubscriptionRestoreView.swift */; }; + D68DF81E2B5830380023DBEA /* SubscriptionRestoreViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68DF81D2B5830380023DBEA /* SubscriptionRestoreViewModel.swift */; }; D69FBF762B28BE3600B505F1 /* SettingsSubscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69FBF752B28BE3600B505F1 /* SettingsSubscriptionView.swift */; }; D6D12C9F2B291CA90054390C /* URL+Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C8B2B291CA90054390C /* URL+Subscription.swift */; }; D6D12CA02B291CA90054390C /* SubscriptionPurchaseEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C8C2B291CA90054390C /* SubscriptionPurchaseEnvironment.swift */; }; @@ -2419,6 +2421,8 @@ D664C7B32B289AA000CBFA76 /* SubscriptionPagesUserScript.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionPagesUserScript.swift; sourceTree = ""; }; D664C7B52B289AA000CBFA76 /* SubscriptionPagesUseSubscriptionFeature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionPagesUseSubscriptionFeature.swift; sourceTree = ""; }; D664C7DC2B28A02800CBFA76 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; + D68DF81B2B58302E0023DBEA /* SubscriptionRestoreView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionRestoreView.swift; sourceTree = ""; }; + D68DF81D2B5830380023DBEA /* SubscriptionRestoreViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionRestoreViewModel.swift; sourceTree = ""; }; D69FBF752B28BE3600B505F1 /* SettingsSubscriptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionView.swift; sourceTree = ""; }; D6D12C8B2B291CA90054390C /* URL+Subscription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URL+Subscription.swift"; sourceTree = ""; }; D6D12C8C2B291CA90054390C /* SubscriptionPurchaseEnvironment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionPurchaseEnvironment.swift; sourceTree = ""; }; @@ -4525,6 +4529,7 @@ children = ( D664C7942B289AA000CBFA76 /* SubscriptionFlowViewModel.swift */, D652498D2B515A6A0056B0DE /* SubscriptionSettingsViewModel.swift */, + D68DF81D2B5830380023DBEA /* SubscriptionRestoreViewModel.swift */, ); path = ViewModel; sourceTree = ""; @@ -4544,6 +4549,7 @@ D664C7AD2B289AA000CBFA76 /* PurchaseInProgressView.swift */, D664C7AE2B289AA000CBFA76 /* SubscriptionFlowView.swift */, D6F93E3D2B50A8A0004C268D /* SubscriptionSettingsView.swift */, + D68DF81B2B58302E0023DBEA /* SubscriptionRestoreView.swift */, ); path = Views; sourceTree = ""; @@ -6502,6 +6508,7 @@ F1DE78581E5CAE350058895A /* TabViewGridCell.swift in Sources */, 984D035824ACCC6F0066CFB8 /* TabViewListCell.swift in Sources */, B6BA95C328891E33004ABA20 /* BrowsingMenuAnimator.swift in Sources */, + D68DF81C2B58302E0023DBEA /* SubscriptionRestoreView.swift in Sources */, D664C7CE2B289AA200CBFA76 /* SubscriptionPagesUseSubscriptionFeature.swift in Sources */, EE9D68DC2AE16AE100B55EF4 /* NotificationsAuthorizationController.swift in Sources */, AA3D854923DA1DFB00788410 /* AppIcon.swift in Sources */, @@ -6624,6 +6631,7 @@ 98999D5922FDA41500CBBE1B /* BasicAuthenticationAlert.swift in Sources */, C13B32D22A0E750700A59236 /* AutofillSettingStatus.swift in Sources */, D6D12CA52B291CAA0054390C /* AppStorePurchaseFlow.swift in Sources */, + D68DF81E2B5830380023DBEA /* SubscriptionRestoreViewModel.swift in Sources */, F4F6DFB426E6B63700ED7E12 /* BookmarkFolderCell.swift in Sources */, D6F93E3E2B50A8A0004C268D /* SubscriptionSettingsView.swift in Sources */, 851B12CC22369931004781BC /* AtbAndVariantCleanup.swift in Sources */, diff --git a/DuckDuckGo/Assets.xcassets/Platform-Apple-16.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/Platform-Apple-16.imageset/Contents.json new file mode 100644 index 0000000000..16a3bd837a --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/Platform-Apple-16.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Platform-Apple-16.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/DuckDuckGo/Assets.xcassets/Platform-Apple-16.imageset/Platform-Apple-16.svg b/DuckDuckGo/Assets.xcassets/Platform-Apple-16.imageset/Platform-Apple-16.svg new file mode 100644 index 0000000000..4e50b8f0d1 --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/Platform-Apple-16.imageset/Platform-Apple-16.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift index 04f6d26d95..ba40e5890e 100644 --- a/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift +++ b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift @@ -66,6 +66,7 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec @Published var transactionStatus: TransactionStatus = .idle @Published var hasActiveSubscription = false @Published var purchaseError: AppStorePurchaseFlow.Error? + @Published var activateSubscription: Bool = false var broker: UserScriptMessageBroker? @@ -221,6 +222,7 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec } func activateSubscription(params: Any, original: WKScriptMessage) async throws -> Encodable? { + activateSubscription = true return nil } diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift index 440b4060de..6fee092c1c 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift @@ -39,6 +39,7 @@ final class SubscriptionFlowViewModel: ObservableObject { @Published var hasActiveSubscription = false @Published var transactionStatus: SubscriptionPagesUseSubscriptionFeature.TransactionStatus = .idle @Published var shouldReloadWebview = false + @Published var activatingSubscription = false init(userScript: SubscriptionPagesUserScript = SubscriptionPagesUserScript(), subFeature: SubscriptionPagesUseSubscriptionFeature = SubscriptionPagesUseSubscriptionFeature(), @@ -66,10 +67,13 @@ final class SubscriptionFlowViewModel: ObservableObject { } .store(in: &cancellables) - subFeature.$hasActiveSubscription + subFeature.$activateSubscription .receive(on: DispatchQueue.main) .sink { [weak self] value in - self?.hasActiveSubscription = value + if value { + self?.subFeature.activateSubscription = false + self?.activatingSubscription = true + } } .store(in: &cancellables) } @@ -81,6 +85,7 @@ final class SubscriptionFlowViewModel: ObservableObject { func initializeViewData() async { await self.setupTransactionObserver() + await MainActor.run { shouldReloadWebview = true } } func restoreAppstoreTransaction() { diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift new file mode 100644 index 0000000000..8b76e2b9d3 --- /dev/null +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift @@ -0,0 +1,68 @@ +// +// SubscriptionRestoreViewModel.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 UserScript +import Combine +import Core + +#if SUBSCRIPTION +@available(iOS 15.0, *) +final class SubscriptionRestoreViewModel: ObservableObject { + + let userScript: SubscriptionPagesUserScript + let subFeature: SubscriptionPagesUseSubscriptionFeature + let purchaseManager: PurchaseManager + + enum SubscriptionActivationResult { + case unknown, activated, notFound, error + } + + @Published var transactionStatus: SubscriptionPagesUseSubscriptionFeature.TransactionStatus = .idle + @Published var activationResult: SubscriptionActivationResult = .unknown + + init(userScript: SubscriptionPagesUserScript = SubscriptionPagesUserScript(), + subFeature: SubscriptionPagesUseSubscriptionFeature = SubscriptionPagesUseSubscriptionFeature(), + purchaseManager: PurchaseManager = PurchaseManager.shared) { + self.userScript = userScript + self.subFeature = subFeature + self.purchaseManager = purchaseManager + } + + @MainActor + private func setTransactionStatus(_ status: SubscriptionPagesUseSubscriptionFeature.TransactionStatus) { + self.transactionStatus = status + } + + @MainActor + func restoreAppstoreTransaction() { + Task { + transactionStatus = .restoring + activationResult = .unknown + if await subFeature.restoreAccountFromAppStorePurchase() { + activationResult = .activated + } else { + activationResult = .notFound + } + transactionStatus = .idle + } + } + +} +#endif diff --git a/DuckDuckGo/Subscription/Views/HeadlessWebView.swift b/DuckDuckGo/Subscription/Views/HeadlessWebView.swift index 44fd4c026b..2b3fafaeee 100644 --- a/DuckDuckGo/Subscription/Views/HeadlessWebView.swift +++ b/DuckDuckGo/Subscription/Views/HeadlessWebView.swift @@ -55,7 +55,14 @@ struct HeadlessWebview: UIViewRepresentable { func updateUIView(_ uiView: WKWebView, context: Context) { if shouldReload { - uiView.reload() + reloadView(uiView: uiView) + } + } + + @MainActor + func reloadView(uiView: WKWebView) { + uiView.reload() + DispatchQueue.main.async { shouldReload = false } } diff --git a/DuckDuckGo/Subscription/Views/PurchaseInProgressView.swift b/DuckDuckGo/Subscription/Views/PurchaseInProgressView.swift index 8b4aed988e..2793d9c043 100644 --- a/DuckDuckGo/Subscription/Views/PurchaseInProgressView.swift +++ b/DuckDuckGo/Subscription/Views/PurchaseInProgressView.swift @@ -28,7 +28,7 @@ struct PurchaseInProgressView: View { static let cornerRadius = 12.0 static let shadowRadius = 10.0 static let lightShadowColor = Color.gray50 - static let darkShadowColor = Color.black + static let darkShadowColor = Color.gray95 static let spinnerScale = 2.0 static let internalZStackWidth = 220.0 static let horizontalPadding = 20.0 diff --git a/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift b/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift index 07d7571e0a..3649d27492 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift @@ -25,6 +25,7 @@ import Foundation struct SubscriptionFlowView: View { @ObservedObject var viewModel: SubscriptionFlowViewModel + @State private var isAlertVisible = false private func getTransactionStatus() -> String { switch viewModel.transactionStatus { @@ -33,7 +34,7 @@ struct SubscriptionFlowView: View { case .purchasing: return UserText.subscriptionPurchasingTitle case .restoring: - return UserText.subscriptionPestoringTitle + return UserText.subscriptionRestoringTitle case .idle: return "" } @@ -50,15 +51,21 @@ struct SubscriptionFlowView: View { if viewModel.transactionStatus != .idle { PurchaseInProgressView(status: getTransactionStatus()) } + + // Activation View + NavigationLink(destination: SubscriptionRestoreView(viewModel: SubscriptionRestoreViewModel()), + isActive: $viewModel.activatingSubscription) { + EmptyView() + } } .onChange(of: viewModel.shouldReloadWebview) { shouldReload in if shouldReload { viewModel.shouldReloadWebview = false } } - .onChange(of: viewModel.shouldReloadWebview) { shouldReload in - if shouldReload { - viewModel.shouldReloadWebview = false + .onChange(of: viewModel.hasActiveSubscription) { result in + if result { + isAlertVisible = true } } .onAppear(perform: { @@ -68,13 +75,13 @@ struct SubscriptionFlowView: View { .navigationBarBackButtonHidden(viewModel.transactionStatus != .idle) // Active subscription found Alert - .alert(isPresented: $viewModel.hasActiveSubscription) { + .alert(isPresented: $isAlertVisible) { Alert( title: Text(UserText.subscriptionFoundTitle), message: Text(UserText.subscriptionFoundText), primaryButton: .cancel(Text(UserText.subscriptionFoundCancel)) { }, - secondaryButton: .default(Text(UserText.subscriptionFoundCancel)) { + secondaryButton: .default(Text(UserText.subscriptionFoundRestore)) { viewModel.restoreAppstoreTransaction() } ) diff --git a/DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift b/DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift new file mode 100644 index 0000000000..618223a245 --- /dev/null +++ b/DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift @@ -0,0 +1,217 @@ +// +// SubscriptionRestoreView.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 +import DesignResourcesKit + +#if SUBSCRIPTION +@available(iOS 15.0, *) +struct SubscriptionRestoreView: View { + + @Environment(\.dismiss) var dismiss + @ObservedObject var viewModel: SubscriptionRestoreViewModel + @State private var expandedItemId: Int = 0 + @State private var isAlertVisible = false + + private enum Constants { + static let heroImage = "SyncTurnOnSyncHero" + static let appleIDIcon = "Platform-Apple-16" + static let emailIcon = "Email-16" + static let headerLineSpacing = 10.0 + static let openIndicator = "chevron.up" + static let closedIndicator = "chevron.down" + static let buttonCornerRadius = 8.0 + static let buttonInsets = EdgeInsets(top: 10.0, leading: 16.0, bottom: 10.0, trailing: 16.0) + static let cellLineSpacing = 12.0 + static let cellPadding = 4.0 + static let headerPadding = EdgeInsets(top: 16.0, leading: 16.0, bottom: 0, trailing: 16.0) + } + + var body: some View { + ZStack { + VStack { + headerView + listView + } + .background(Color(designSystemColor: .container)) + .navigationTitle(UserText.subscriptionActivate) + .navigationBarBackButtonHidden(viewModel.transactionStatus != .idle) + .applyInsetGroupedListStyle() + .alert(isPresented: $isAlertVisible) { getAlert() } + .onChange(of: viewModel.activationResult) { result in + if result != .unknown { + isAlertVisible = true + } + } + + if viewModel.transactionStatus != .idle { + PurchaseInProgressView(status: getTransactionStatus()) + } + } + + + } + + private var listItems: [ListItem] { + [ + .init(id: 0, + content: getCellTitle(icon: Constants.appleIDIcon, + text: UserText.subscriptionActivateAppleID), + expandedContent: getCellContent(description: UserText.subscriptionActivateAppleIDDescription, + buttonText: UserText.subscriptionRestoreAppleID, + buttonAction: viewModel.restoreAppstoreTransaction)), + .init(id: 1, + content: getCellTitle(icon: Constants.emailIcon, + text: UserText.subscriptionActivateEmail), + expandedContent: getCellContent(description: UserText.subscriptionActivateEmailDescription, + buttonText: UserText.subscriptionRestoreEmail, + buttonAction: {})) + ] + } + + private func getCellTitle(icon: String, text: String) -> AnyView { + AnyView( + HStack { + Image(icon) + Text(text) + .daxBodyRegular() + .foregroundColor(Color(designSystemColor: .textPrimary)) + } + ) + } + + private func getCellContent(description: String, buttonText: String, buttonAction: @escaping () -> Void) -> AnyView { + AnyView( + VStack(alignment: .leading) { + Text(description) + .daxSubheadRegular() + .foregroundColor(Color(designSystemColor: .textSecondary)) + getCellButton(buttonText: buttonText, action: buttonAction) + } + ) + } + + private func getCellButton(buttonText: String, action: @escaping () -> Void) -> AnyView { + AnyView( + Button(action: action, label: { + Text(buttonText) + .daxButton() + .padding(Constants.buttonInsets) + .foregroundColor(.white) + .overlay( + RoundedRectangle(cornerRadius: Constants.buttonCornerRadius) + .stroke(Color.clear, lineWidth: 1) + ) + }) + .background(Color(designSystemColor: .accent)) + .cornerRadius(Constants.buttonCornerRadius) + .padding(.top, Constants.cellPadding) + ) + } + + private func getTransactionStatus() -> String { + switch viewModel.transactionStatus { + case .polling: + return UserText.subscriptionCompletingPurchaseTitle + case .purchasing: + return UserText.subscriptionPurchasingTitle + case .restoring: + return UserText.subscriptionRestoringTitle + case .idle: + return "" + } + } + + private var headerView: some View { + VStack(spacing: Constants.headerLineSpacing) { + Image(Constants.heroImage) + Text(UserText.subscriptionActivateTitle) + .daxHeadline() + .multilineTextAlignment(.center) + .foregroundColor(Color(designSystemColor: .textPrimary)) + Text(UserText.subscriptionActivateDescription) + .daxFootnoteRegular() + .foregroundColor(Color(designSystemColor: .textSecondary)) + .multilineTextAlignment(.center) + }.padding(Constants.headerPadding) + } + + private var listView: some View { + List { + Section { + ForEach(Array(zip(listItems.indices, listItems)), id: \.1.id) { _, item in + VStack(alignment: .leading, spacing: Constants.cellLineSpacing) { + HStack { + item.content + Spacer() + Image(systemName: expandedItemId == item.id ? Constants.openIndicator : Constants.closedIndicator) + .foregroundColor(Color(designSystemColor: .textPrimary)) + } + .contentShape(Rectangle()) + .onTapGesture { + expandedItemId = expandedItemId == item.id ? 0 : item.id + } + if expandedItemId == item.id { + item.expandedContent + } + }.padding(Constants.cellPadding) + } + } + } + } + + private func getAlert() -> Alert { + switch viewModel.activationResult { + case .activated: + return Alert(title: Text(UserText.subscriptionRestoreSuccessfulTitle), + message: Text(UserText.subscriptionRestoreSuccessfulMessage), + dismissButton: .default(Text(UserText.subscriptionRestoreSuccessfulButton)) { + dismiss() + } + ) + case .notFound: + return Alert(title: Text(UserText.subscriptionRestoreNotFoundTitle), + message: Text(UserText.subscriptionRestoreNotFoundMessage), + primaryButton: .default(Text(UserText.subscriptionRestoreNotFoundPlans), + action: { + dismiss() + }), + secondaryButton: .cancel()) + case .error: + return Alert(title: Text("Error"), message: Text("An error occurred during activation.")) + default: + return Alert(title: Text("Unknown"), message: Text("An unknown error occurred.")) + } + } + + struct ListItem { + let id: Int + let content: AnyView + let expandedContent: AnyView + } +} + +@available(iOS 15.0, *) +struct SubscriptionRestoreView_Previews: PreviewProvider { + static var previews: some View { + SubscriptionRestoreView(viewModel: SubscriptionRestoreViewModel()) + } +} +#endif diff --git a/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift b/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift index 769041e705..c10bd87f44 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift @@ -80,6 +80,7 @@ struct SubscriptionSettingsView: View { } } .navigationTitle(UserText.settingsPProManageSubscription) + .applyInsetGroupedListStyle() // Remove subscription .alert(isPresented: $viewModel.shouldDisplayRemovalNotice) { diff --git a/DuckDuckGo/UserText.swift b/DuckDuckGo/UserText.swift index d289dc1220..6e6f3bb0a0 100644 --- a/DuckDuckGo/UserText.swift +++ b/DuckDuckGo/UserText.swift @@ -1001,12 +1001,11 @@ But if you *do* want a peek under the hood, you can find more information about public static let settingsVersion = NSLocalizedString("settings.version", value: "Version", comment: "Settings cell for Version") public static let settingsFeedback = NSLocalizedString("settings.feedback", value: "Share Feedback", comment: "Settings cell for Feedback") - // Privacy Pro Subscriptions + // Subscriptions static let subscriptionPurchasingTitle = NSLocalizedString("subscription.progress.view.purchasing.subscription", value: "Purchase in progress...", comment: "Progress view title when starting the purchase") - static let subscriptionPestoringTitle = NSLocalizedString("subscription.progress.view.restoring.subscription", value: "Restoring subscription...", comment: "Progress view title when restoring past subscription purchase") + static let subscriptionRestoringTitle = NSLocalizedString("subscription.progress.view.restoring.subscription", value: "Restoring subscription...", comment: "Progress view title when restoring past subscription purchase") static let subscriptionCompletingPurchaseTitle = NSLocalizedString("subscription.progress.view.completing.purchase", value: "Completing purchase...", comment: "Progress view title when completing the purchase") - static func subscriptionInfo(expiration: String) -> String { let localized = NSLocalizedString("subscription.subscription.active.caption", value: "Your Privacy Pro subscription renews on %@", comment: "Subscription Expiration Data") return String(format: localized, expiration) @@ -1023,9 +1022,23 @@ But if you *do* want a peek under the hood, you can find more information about public static let subscriptionRemoveFromDeviceConfirmText = NSLocalizedString("subscription.remove.from.device.text", value: "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices.", comment: "Remove from device confirmation dialog text") public static let subscriptionRemove = NSLocalizedString("subscription.remove.subscription", value: "Remove Subscription", comment: "Remove subscription button text") public static let subscriptionRemoveCancel = NSLocalizedString("subscription.remove.subscription.cancel", value: "Cancel", comment: "Remove subscription cancel button text") - public static let subscriptionFoundTitle = NSLocalizedString("subscription.subscription.found.tite", value: "Subscription Found", comment: "Title for the existing subscription dialog") - public static let subscriptionFoundText = NSLocalizedString("subscription.subscription.found.text", value: "We found a subscription associated with this Apple ID.", comment: "Message for the existing subscription dialog") - public static let subscriptionFoundCancel = NSLocalizedString("subscription.subscription.found.cancel", value: "Cancel", comment: "Cancel action for the existing subscription dialog") - public static let subscriptionFoundRestore = NSLocalizedString("subscription.subscription.found.restore", value: "Restore", comment: "Restore action for the existing subscription dialog") - + public static let subscriptionFoundTitle = NSLocalizedString("subscription.found.tite", value: "Subscription Found", comment: "Title for the existing subscription dialog") + public static let subscriptionFoundText = NSLocalizedString("subscription.found.text", value: "We found a subscription associated with this Apple ID.", comment: "Message for the existing subscription dialog") + public static let subscriptionFoundCancel = NSLocalizedString("subscription.found.cancel", value: "Cancel", comment: "Cancel action for the existing subscription dialog") + public static let subscriptionFoundRestore = NSLocalizedString("subscription.found.restore", value: "Restore", comment: "Restore action for the existing subscription dialog") + public static let subscriptionActivateTitle = NSLocalizedString("subscription.activate.title", value: "Activate your subscription on this device", comment: "Subscription Activation Title") + public static let subscriptionActivateDescription = NSLocalizedString("subscription.activate.description", value: "Access your Privacy Pro subscription on this device via Apple ID or an email address.", comment: "Subscription Activation Info") + public static let subscriptionActivate = NSLocalizedString("subscription.activate", value: "Activate Subscription.", comment: "Subscription Activation Window Title") + public static let subscriptionActivateAppleID = NSLocalizedString("subscription.activate.appleid", value: "Apple ID", comment: "Apple ID option for activation") + public static let subscriptionActivateAppleIDDescription = NSLocalizedString("subscription.activate.appleid.description", value: "Restore your purchase to activate your subscription on this device.", comment: "Description for Apple ID activation") + public static let subscriptionRestoreAppleID = NSLocalizedString("subscription.activate.restore.apple", value: "Restore", comment: "Restore button title for AppleID") + public static let subscriptionActivateEmail = NSLocalizedString("subscription.activate.email", value: "Email", comment: "Email option for activation") + public static let subscriptionActivateEmailDescription = NSLocalizedString("subscription.activate.appleid.description", value: "Use your email to activate your subscription on this device.", comment: "Description for Email activation") + public static let subscriptionRestoreEmail = NSLocalizedString("subscription.activate.restore.email", value: "Enter Email", comment: "Restore button title for Email") + public static let subscriptionRestoreNotFoundTitle = NSLocalizedString("subscription.notFound.alert.title", value: "Subscription Not Found", comment: "Alert title for not found subscription") + public static let subscriptionRestoreNotFoundMessage = NSLocalizedString("subscription.notFound.alert.message", value: "The subscription associated with this Apple ID is no longer active.", comment: "Alert content for not found subscription") + public static let subscriptionRestoreNotFoundPlans = NSLocalizedString("subscription.notFound.view.plans", value: "View Plans", comment: "View plans button text") + public static let subscriptionRestoreSuccessfulTitle = NSLocalizedString("subscription.restore.success.alert.title", value: "You’re all set.", comment: "Alert title for restored purchase") + public static let subscriptionRestoreSuccessfulMessage = NSLocalizedString("subscription.restore.success.alert.message", value: "Your purchases have been restored.", comment: "Alert message for restored purchase") + public static let subscriptionRestoreSuccessfulButton = NSLocalizedString("subscription.restore.success.alert.button", value: "OK", comment: "Alert button text for restored purchase alert") } diff --git a/DuckDuckGo/en.lproj/Localizable.strings b/DuckDuckGo/en.lproj/Localizable.strings index 8b6c074d19..721303ff0f 100644 --- a/DuckDuckGo/en.lproj/Localizable.strings +++ b/DuckDuckGo/en.lproj/Localizable.strings @@ -1914,6 +1914,31 @@ But if you *do* want a peek under the hood, you can find more information about /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Which website is broken?"; +/* Subscription Activation Window Title */ +"subscription.activate" = "Activate Subscription."; + +/* Apple ID option for activation */ +"subscription.activate.appleid" = "Apple ID"; + +/* Description for Apple ID activation + Description for Email activation */ +"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; + +/* Subscription Activation Info */ +"subscription.activate.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; + +/* Email option for activation */ +"subscription.activate.email" = "Email"; + +/* Restore button title for AppleID */ +"subscription.activate.restore.apple" = "Restore"; + +/* Restore button title for Email */ +"subscription.activate.restore.email" = "Enter Email"; + +/* Subscription Activation Title */ +"subscription.activate.title" = "Activate your subscription on this device"; + /* Add to another device button */ "subscription.add.device" = "Add to Another Device"; @@ -1926,6 +1951,18 @@ But if you *do* want a peek under the hood, you can find more information about /* FAQ Description */ "subscription.faq.description" = "Visit our Privacy Pro help pages for answers to frequently asked questions"; +/* Cancel action for the existing subscription dialog */ +"subscription.found.cancel" = "Cancel"; + +/* Restore action for the existing subscription dialog */ +"subscription.found.restore" = "Restore"; + +/* Message for the existing subscription dialog */ +"subscription.found.text" = "We found a subscription associated with this Apple ID."; + +/* Title for the existing subscription dialog */ +"subscription.found.tite" = "Subscription Found"; + /* Help and support Section header */ "subscription.help" = "Help and support"; @@ -1935,6 +1972,15 @@ But if you *do* want a peek under the hood, you can find more information about /* Manage Plan header */ "subscription.manage.plan" = "Manage Plan"; +/* Alert content for not found subscription */ +"subscription.notFound.alert.message" = "The subscription associated with this Apple ID is no longer active."; + +/* Alert title for not found subscription */ +"subscription.notFound.alert.title" = "Subscription Not Found"; + +/* View plans button text */ +"subscription.notFound.view.plans" = "View Plans"; + /* Progress view title when completing the purchase */ "subscription.progress.view.completing.purchase" = "Completing purchase..."; @@ -1959,20 +2005,17 @@ But if you *do* want a peek under the hood, you can find more information about /* Remove subscription cancel button text */ "subscription.remove.subscription.cancel" = "Cancel"; -/* Subscription Expiration Data */ -"subscription.subscription.active.caption" = "Your Privacy Pro subscription renews on %@"; - -/* Cancel action for the existing subscription dialog */ -"subscription.subscription.found.cancel" = "Cancel"; +/* Alert button text for restored purchase alert */ +"subscription.restore.success.alert.button" = "OK"; -/* Restore action for the existing subscription dialog */ -"subscription.subscription.found.restore" = "Restore"; +/* Alert message for restored purchase */ +"subscription.restore.success.alert.message" = "Your purchases have been restored."; -/* Message for the existing subscription dialog */ -"subscription.subscription.found.text" = "We found a subscription associated with this Apple ID."; +/* Alert title for restored purchase */ +"subscription.restore.success.alert.title" = "You’re all set."; -/* Title for the existing subscription dialog */ -"subscription.subscription.found.tite" = "Subscription Found"; +/* Subscription Expiration Data */ +"subscription.subscription.active.caption" = "Your Privacy Pro subscription renews on %@"; /* Message confirming that recovery code was copied to clipboard */ "sync.code.copied" = "Recovery code copied to clipboard"; From e13c35177a3c437e7082d890cbb075902967e940 Mon Sep 17 00:00:00 2001 From: Sabrina Tardio <44158575+SabrinaTardio@users.noreply.github.com> Date: Thu, 18 Jan 2024 18:19:58 +0100 Subject: [PATCH 11/70] add pixels for sync flows (#2332) Task/Issue URL: https://app.asana.com/0/1204186595873227/1206309044542058/f Description: Adds debugs Pixels for errors encountered during sync setup flow and sync enabled flow. --- Core/PixelEvent.swift | 14 ++++++++++++ DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 ++-- ...cSettingsViewController+SyncDelegate.swift | 22 +++++++++++++------ DuckDuckGo/SyncSettingsViewController.swift | 12 +++++----- LocalPackages/DuckUI/Package.swift | 2 +- LocalPackages/SyncUI/Package.swift | 2 +- LocalPackages/Waitlist/Package.swift | 2 +- 8 files changed, 41 insertions(+), 19 deletions(-) diff --git a/Core/PixelEvent.swift b/Core/PixelEvent.swift index 6853850b97..2f0fe3200b 100644 --- a/Core/PixelEvent.swift +++ b/Core/PixelEvent.swift @@ -489,6 +489,13 @@ extension Pixel { case syncCredentialsFailed case syncSettingsFailed case syncSettingsMetadataUpdateFailed + case syncSignupError + case syncLoginError + case syncLogoutError + case syncUpdateDeviceError + case syncRemoveDeviceError + case syncDeleteAccountError + case syncLoginExistingAccountError case bookmarksCleanupFailed case bookmarksCleanupAttemptedWhileSyncWasEnabled @@ -969,6 +976,13 @@ extension Pixel.Event { case .syncCredentialsFailed: return "m_d_sync_credentials_failed" case .syncSettingsFailed: return "m_d_sync_settings_failed" case .syncSettingsMetadataUpdateFailed: return "m_d_sync_settings_metadata_update_failed" + case .syncSignupError: return "m_d_sync_signup_error" + case .syncLoginError: return "m_d_sync_login_error" + case .syncLogoutError: return "m_d_sync_logout_error" + case .syncUpdateDeviceError: return "m_d_sync_update_device_error" + case .syncRemoveDeviceError: return "m_d_sync_remove_device_error" + case .syncDeleteAccountError: return "m_d_sync_delete_account_error" + case .syncLoginExistingAccountError: return "m_d_sync_login_existing_account_error" case .bookmarksCleanupFailed: return "m_d_bookmarks_cleanup_failed" diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index d282cb5bbb..5a74c6116c 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -9918,7 +9918,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 101.1.3; + version = 101.1.4; }; }; C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index e3490e21cb..3746e44125 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "13c6ccd65f45e9940371c28a1a3498ec29ac9d0b", - "version" : "101.1.3" + "revision" : "7f9eb807af196ec3db8fe6f0b168df89ea16a11c", + "version" : "101.1.4" } }, { diff --git a/DuckDuckGo/SyncSettingsViewController+SyncDelegate.swift b/DuckDuckGo/SyncSettingsViewController+SyncDelegate.swift index fc07683d51..6ec419d7a2 100644 --- a/DuckDuckGo/SyncSettingsViewController+SyncDelegate.swift +++ b/DuckDuckGo/SyncSettingsViewController+SyncDelegate.swift @@ -46,7 +46,7 @@ extension SyncSettingsViewController: SyncManagementViewModelDelegate { let devices = try await syncService.updateDeviceName(name) mapDevices(devices) } catch { - handleError(SyncError.unableToUpdateDeviceName, error: error) + handleError(SyncErrorMessage.unableToUpdateDeviceName, error: error, event: .syncUpdateDeviceError) } syncService.scheduler.resumeSyncQueue() } @@ -63,13 +63,14 @@ extension SyncSettingsViewController: SyncManagementViewModelDelegate { self.refreshDevices() navigationController?.topViewController?.dismiss(animated: true, completion: showRecoveryPDF) } catch { - handleError(SyncError.unableToSyncToServer, error: error) + handleError(SyncErrorMessage.unableToSyncToServer, error: error, event: .syncSignupError) } } } @MainActor - func handleError(_ type: SyncError, error: Error?) { + func handleError(_ type: SyncErrorMessage, error: Error?, event: Pixel.Event) { + firePixelIfNeededFor(event: event, error: error) let alertController = UIAlertController( title: type.title, message: [type.description, error?.localizedDescription].compactMap({ $0 }).joined(separator: "\n"), @@ -94,6 +95,13 @@ extension SyncSettingsViewController: SyncManagementViewModelDelegate { } } + private func firePixelIfNeededFor(event: Pixel.Event, error: Error?) { + guard let syncError = error as? SyncError else { return } + if !syncError.isServerError { + Pixel.fire(pixel: event, withAdditionalParameters: syncError.errorParameters) + } + } + func showSyncWithAnotherDevice() { collectCode(showConnectMode: true) } @@ -186,7 +194,7 @@ extension SyncSettingsViewController: SyncManagementViewModelDelegate { AppUserDefaults().isSyncCredentialsPaused = false continuation.resume(returning: true) } catch { - self.handleError(SyncError.unableToTurnSyncOff, error: error) + self.handleError(SyncErrorMessage.unableToTurnSyncOff, error: error, event: .syncLogoutError) continuation.resume(returning: false) } } @@ -212,7 +220,7 @@ extension SyncSettingsViewController: SyncManagementViewModelDelegate { AppUserDefaults().isSyncCredentialsPaused = false continuation.resume(returning: true) } catch { - self.handleError(SyncError.unableToDeleteData, error: error) + self.handleError(SyncErrorMessage.unableToDeleteData, error: error, event: .syncDeleteAccountError) continuation.resume(returning: false) } } @@ -248,7 +256,7 @@ extension SyncSettingsViewController: SyncManagementViewModelDelegate { try await syncService.disconnect(deviceId: device.id) refreshDevices() } catch { - handleError(SyncError.unableToRemoveDevice, error: error) + handleError(SyncErrorMessage.unableToRemoveDevice, error: error, event: .syncRemoveDeviceError) } } } @@ -286,7 +294,7 @@ private class PortraitNavigationController: UINavigationController { } -enum SyncError { +enum SyncErrorMessage { case unableToSyncToServer case unableToSyncWithDevice case unableToMergeTwoAccounts diff --git a/DuckDuckGo/SyncSettingsViewController.swift b/DuckDuckGo/SyncSettingsViewController.swift index 20f9238a31..d8b8dc28f4 100644 --- a/DuckDuckGo/SyncSettingsViewController.swift +++ b/DuckDuckGo/SyncSettingsViewController.swift @@ -248,7 +248,7 @@ extension SyncSettingsViewController: ScanOrPasteCodeViewModelDelegate { self.startPolling() return self.connector?.code } catch { - self.handleError(SyncError.unableToSyncToServer, error: error) + self.handleError(SyncErrorMessage.unableToSyncToServer, error: error, event: .syncLoginError) return nil } } @@ -274,7 +274,7 @@ extension SyncSettingsViewController: ScanOrPasteCodeViewModelDelegate { return } } catch { - handleError(SyncError.unableToSyncWithDevice, error: error) + handleError(SyncErrorMessage.unableToSyncWithDevice, error: error, event: .syncLoginError) } } } @@ -292,9 +292,9 @@ extension SyncSettingsViewController: ScanOrPasteCodeViewModelDelegate { return true } catch { if self.rootView.model.isSyncEnabled { - handleError(.unableToMergeTwoAccounts, error: nil) + handleError(.unableToMergeTwoAccounts, error: error, event: .syncLoginExistingAccountError) } else { - handleError(.unableToSyncToServer, error: error) + handleError(.unableToSyncToServer, error: error, event: .syncLoginError) } } } else if let connectKey = syncCode.connect { @@ -308,7 +308,7 @@ extension SyncSettingsViewController: ScanOrPasteCodeViewModelDelegate { shouldShowSyncEnabled = false rootView.model.syncEnabled(recoveryCode: recoveryCode) } catch { - handleError(.unableToSyncToServer, error: error) + handleError(.unableToSyncToServer, error: error, event: .syncSignupError) } } do { @@ -324,7 +324,7 @@ extension SyncSettingsViewController: ScanOrPasteCodeViewModelDelegate { } }.store(in: &cancellables) } catch { - handleError(.unableToSyncWithDevice, error: error) + handleError(.unableToSyncWithDevice, error: error, event: .syncLoginError) } return true diff --git a/LocalPackages/DuckUI/Package.swift b/LocalPackages/DuckUI/Package.swift index e663006baa..97fa85f214 100644 --- a/LocalPackages/DuckUI/Package.swift +++ b/LocalPackages/DuckUI/Package.swift @@ -31,7 +31,7 @@ let package = Package( targets: ["DuckUI"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.1.3"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.1.4"), ], targets: [ .target( diff --git a/LocalPackages/SyncUI/Package.swift b/LocalPackages/SyncUI/Package.swift index 2e2aafac4a..cf466c9fc2 100644 --- a/LocalPackages/SyncUI/Package.swift +++ b/LocalPackages/SyncUI/Package.swift @@ -33,7 +33,7 @@ let package = Package( ], dependencies: [ .package(path: "../DuckUI"), - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.1.3"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.1.4"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ diff --git a/LocalPackages/Waitlist/Package.swift b/LocalPackages/Waitlist/Package.swift index 125e62f7d7..d561061857 100644 --- a/LocalPackages/Waitlist/Package.swift +++ b/LocalPackages/Waitlist/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["Waitlist", "WaitlistMocks"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.1.3"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.1.4"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ From dc589453d1394b985960ab2bf939e22272293e47 Mon Sep 17 00:00:00 2001 From: Christopher Brind Date: Fri, 19 Jan 2024 15:12:35 +0000 Subject: [PATCH 12/70] refresh the model after the tab has been closed (#2358) --- DuckDuckGo/MainViewController.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 8a825a9d73..47528d7e39 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -1919,6 +1919,7 @@ extension MainViewController: TabSwitcherDelegate { hideSuggestionTray() tabManager.remove(at: index) updateCurrentTab() + tabsBarController?.refresh(tabsModel: tabManager.model) } func tabSwitcherDidRequestForgetAll(tabSwitcher: TabSwitcherViewController) { From b49dabe08eca48151427ce368ba27830025d7f62 Mon Sep 17 00:00:00 2001 From: Graeme Arthur Date: Fri, 19 Jan 2024 16:19:27 +0100 Subject: [PATCH 13/70] Bump build number to 2 --- DuckDuckGo.xcodeproj/project.pbxproj | 56 +++++++++---------- .../xcshareddata/swiftpm/Package.resolved | 2 +- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index c6b884ff43..cf7e314bec 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -8135,7 +8135,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProvider.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -8172,7 +8172,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8264,7 +8264,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8292,7 +8292,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8442,7 +8442,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8468,7 +8468,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; INFOPLIST_FILE = DuckDuckGo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8533,7 +8533,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -8568,7 +8568,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8602,7 +8602,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8633,7 +8633,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8920,7 +8920,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8951,7 +8951,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8980,7 +8980,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -9014,7 +9014,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -9045,7 +9045,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProvider.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -9078,11 +9078,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; + DYLIB_CURRENT_VERSION = 2; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9320,7 +9320,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGoAlpha.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9347,7 +9347,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9380,7 +9380,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9418,7 +9418,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9454,7 +9454,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9489,11 +9489,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; + DYLIB_CURRENT_VERSION = 2; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9667,11 +9667,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; + DYLIB_CURRENT_VERSION = 2; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9700,10 +9700,10 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; + DYLIB_CURRENT_VERSION = 2; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 64a6614e8d..67fbd40e2b 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -156,7 +156,7 @@ { "identity" : "trackerradarkit", "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/TrackerRadarKit.git", + "location" : "https://github.com/duckduckgo/TrackerRadarKit", "state" : { "revision" : "a6b7ba151d9dc6684484f3785293875ec01cc1ff", "version" : "1.2.2" From f5401af115994ac943302d439fc344556ba87d3e Mon Sep 17 00:00:00 2001 From: Christopher Brind Date: Fri, 19 Jan 2024 17:28:18 +0000 Subject: [PATCH 14/70] support multiple passes (#2349) --- DuckDuckGo.xcodeproj/project.pbxproj | 21 ++++++ .../xcshareddata/swiftpm/Package.resolved | 11 +++- DuckDuckGo/FilePreviewHelper.swift | 4 +- DuckDuckGo/MIMEType.swift | 1 + DuckDuckGo/PassKitPreviewHelper.swift | 2 +- DuckDuckGo/ZippedPassKitPreviewHelper.swift | 65 +++++++++++++++++++ 6 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 DuckDuckGo/ZippedPassKitPreviewHelper.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 8239f3286e..38136f576a 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -365,6 +365,7 @@ 85058370219F424500ED4EDB /* SearchBarExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F143C3451E4AA32D00CFDE3A /* SearchBarExtension.swift */; }; 850ABD012AC3961100A733DF /* MainViewController+Segues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850ABD002AC3961100A733DF /* MainViewController+Segues.swift */; }; 850ABD032AC4D46C00A733DF /* SuggestionTray.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 850ABD022AC4D46C00A733DF /* SuggestionTray.storyboard */; }; + 850F93DB2B594AB800823EEA /* ZippedPassKitPreviewHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850F93DA2B594AB800823EEA /* ZippedPassKitPreviewHelper.swift */; }; 8512EA4F24ED30D20073EE19 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8512EA4E24ED30D20073EE19 /* WidgetKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 8512EA5124ED30D20073EE19 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8512EA5024ED30D20073EE19 /* SwiftUI.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 8512EA5424ED30D20073EE19 /* Widgets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8512EA5324ED30D20073EE19 /* Widgets.swift */; }; @@ -406,6 +407,7 @@ 853A717820F645FB00FE60BC /* PixelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 853A717720F645FB00FE60BC /* PixelTests.swift */; }; 853C5F5B21BFF0AE001F7A05 /* HomeCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 853C5F5A21BFF0AE001F7A05 /* HomeCollectionView.swift */; }; 853C5F6121C277C7001F7A05 /* global.swift in Sources */ = {isa = PBXBuildFile; fileRef = 853C5F6021C277C7001F7A05 /* global.swift */; }; + 854007E72B57FC000001BD98 /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = 854007E62B57FC000001BD98 /* ZIPFoundation */; }; 8540BBA22440857A00017FE4 /* PreserveLoginsWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8540BBA12440857A00017FE4 /* PreserveLoginsWorker.swift */; }; 8540BD5223D8C2220057FDD2 /* PreserveLoginsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8540BD5123D8C2220057FDD2 /* PreserveLoginsTests.swift */; }; 8540BD5423D8D5080057FDD2 /* PreserveLoginsAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8540BD5323D8D5080057FDD2 /* PreserveLoginsAlert.swift */; }; @@ -1447,6 +1449,7 @@ 85058367219C49E000ED4EDB /* HomeViewSectionRenderers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewSectionRenderers.swift; sourceTree = ""; }; 850ABD002AC3961100A733DF /* MainViewController+Segues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainViewController+Segues.swift"; sourceTree = ""; }; 850ABD022AC4D46C00A733DF /* SuggestionTray.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = SuggestionTray.storyboard; sourceTree = ""; }; + 850F93DA2B594AB800823EEA /* ZippedPassKitPreviewHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZippedPassKitPreviewHelper.swift; sourceTree = ""; }; 8512BCBF2061B6110085E862 /* global.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = global.swift; sourceTree = ""; }; 8512EA4D24ED30D20073EE19 /* WidgetsExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WidgetsExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 8512EA4E24ED30D20073EE19 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; @@ -2680,6 +2683,7 @@ F4D7F634298C00C3006C3AE9 /* FindInPageIOSJSSupport in Frameworks */, 85D598872927F84C00FA3B1B /* Crashes in Frameworks */, D664C7DD2B28A02800CBFA76 /* StoreKit.framework in Frameworks */, + 854007E72B57FC000001BD98 /* ZIPFoundation in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3313,6 +3317,7 @@ 3132FA2527A0784600DD7A12 /* FilePreviewHelper.swift */, 3132FA2927A0788F00DD7A12 /* QuickLookPreviewHelper.swift */, 3132FA2727A0788400DD7A12 /* PassKitPreviewHelper.swift */, + 850F93DA2B594AB800823EEA /* ZippedPassKitPreviewHelper.swift */, ); name = FilePreview; sourceTree = ""; @@ -5689,6 +5694,7 @@ F42D541C29DCA40B004C4FF1 /* DesignResourcesKit */, 0238E44E29C0FAA100615E30 /* FindInPageIOSJSSupport */, 4B2754EB29E8C7DF00394032 /* Lottie */, + 854007E62B57FC000001BD98 /* ZIPFoundation */, ); productName = DuckDuckGo; productReference = 84E341921E2F7EFB00BDBA6F /* DuckDuckGo.app */; @@ -5991,6 +5997,7 @@ 0202568C29881E4300E694E7 /* XCRemoteSwiftPackageReference "CocoaAsyncSocket" */, 0238E44D29C0FAA100615E30 /* XCRemoteSwiftPackageReference "ios-js-support" */, 4B2754EA29E8C7DF00394032 /* XCRemoteSwiftPackageReference "lottie-ios" */, + 854007E52B57FB020001BD98 /* XCRemoteSwiftPackageReference "ZIPFoundation" */, ); productRefGroup = 84E341931E2F7EFB00BDBA6F /* Products */; projectDirPath = ""; @@ -6772,6 +6779,7 @@ 85EE7F572246685B000FE757 /* WebContainerViewController.swift in Sources */, 1EC458462948932500CB2B13 /* UIHostingControllerExtension.swift in Sources */, 1E4DCF4E27B6A69600961E25 /* DownloadsListHostingController.swift in Sources */, + 850F93DB2B594AB800823EEA /* ZippedPassKitPreviewHelper.swift in Sources */, 4BCD14672B05B682000B1E4C /* NetworkProtectionTermsAndConditionsStore.swift in Sources */, 020108A129A5610C00644F9D /* AppTPActivityHostingViewController.swift in Sources */, C1F341C92A6926920032057B /* EmailAddressPromptViewController.swift in Sources */, @@ -9913,6 +9921,14 @@ version = 3.3.0; }; }; + 854007E52B57FB020001BD98 /* XCRemoteSwiftPackageReference "ZIPFoundation" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/weichsel/ZIPFoundation.git"; + requirement = { + kind = exactVersion; + version = 0.9.17; + }; + }; 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; @@ -10045,6 +10061,11 @@ package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = RemoteMessaging; }; + 854007E62B57FC000001BD98 /* ZIPFoundation */ = { + isa = XCSwiftPackageProductDependency; + package = 854007E52B57FB020001BD98 /* XCRemoteSwiftPackageReference "ZIPFoundation" */; + productName = ZIPFoundation; + }; 85875B6029912A9900115F05 /* SyncUI */ = { isa = XCSwiftPackageProductDependency; productName = SyncUI; diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 3746e44125..182990508e 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -156,7 +156,7 @@ { "identity" : "trackerradarkit", "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/TrackerRadarKit", + "location" : "https://github.com/duckduckgo/TrackerRadarKit.git", "state" : { "revision" : "a6b7ba151d9dc6684484f3785293875ec01cc1ff", "version" : "1.2.2" @@ -170,6 +170,15 @@ "revision" : "2d8172c11478ab11b0f5ad49bdb4f93f4b3d5e0d", "version" : "1.1.1" } + }, + { + "identity" : "zipfoundation", + "kind" : "remoteSourceControl", + "location" : "https://github.com/weichsel/ZIPFoundation.git", + "state" : { + "revision" : "a3f5c2bae0f04b0bce9ef3c4ba6bd1031a0564c4", + "version" : "0.9.17" + } } ], "version" : 2 diff --git a/DuckDuckGo/FilePreviewHelper.swift b/DuckDuckGo/FilePreviewHelper.swift index f8f4009311..41bd3824e8 100644 --- a/DuckDuckGo/FilePreviewHelper.swift +++ b/DuckDuckGo/FilePreviewHelper.swift @@ -26,6 +26,8 @@ struct FilePreviewHelper { switch download.mimeType { case .passbook: return PassKitPreviewHelper(filePath, viewController: viewController) + case .multipass: + return ZippedPassKitPreviewHelper(filePath, viewController: viewController) default: return QuickLookPreviewHelper(filePath, viewController: viewController) } @@ -33,7 +35,7 @@ struct FilePreviewHelper { static func canAutoPreviewMIMEType(_ mimeType: MIMEType) -> Bool { switch mimeType { - case .passbook: + case .passbook, .multipass: return UIDevice.current.userInterfaceIdiom == .phone case .reality, .usdz, .calendar: diff --git a/DuckDuckGo/MIMEType.swift b/DuckDuckGo/MIMEType.swift index 863d5fe55e..29a7c47813 100644 --- a/DuckDuckGo/MIMEType.swift +++ b/DuckDuckGo/MIMEType.swift @@ -21,6 +21,7 @@ import Foundation enum MIMEType: String { case passbook = "application/vnd.apple.pkpass" + case multipass = "application/vnd.apple.pkpasses" case usdz = "model/vnd.usdz+zip" case reality = "model/vnd.reality" case octetStream = "application/octet-stream" diff --git a/DuckDuckGo/PassKitPreviewHelper.swift b/DuckDuckGo/PassKitPreviewHelper.swift index 3a734eee03..588f355d54 100644 --- a/DuckDuckGo/PassKitPreviewHelper.swift +++ b/DuckDuckGo/PassKitPreviewHelper.swift @@ -38,7 +38,7 @@ class PassKitPreviewHelper: FilePreview { viewController?.present(controller, animated: true) } } catch { - os_log("Can't present passkit: %s", type: .debug, error.localizedDescription) + os_log("Can't present passkit: %{public}s", type: .error, error.localizedDescription) } } } diff --git a/DuckDuckGo/ZippedPassKitPreviewHelper.swift b/DuckDuckGo/ZippedPassKitPreviewHelper.swift new file mode 100644 index 0000000000..0213ccc7d2 --- /dev/null +++ b/DuckDuckGo/ZippedPassKitPreviewHelper.swift @@ -0,0 +1,65 @@ +// +// ZippedPassKitPreviewHelper.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 Common +import Foundation +import UIKit +import PassKit +import ZIPFoundation + +class ZippedPassKitPreviewHelper: FilePreview { + private weak var viewController: UIViewController? + private let filePath: URL + + required init(_ filePath: URL, viewController: UIViewController) { + self.filePath = filePath + self.viewController = viewController + } + + func preview() { + do { + let passes: [PKPass] = try extractDataEntriesFromZipAtFilePath(self.filePath).compactMap({ try? PKPass(data: $0) }) + if passes.count > 0, + let controller = PKAddPassesViewController(passes: passes) { + viewController?.present(controller, animated: true) + } else { + os_log("Can't present passkit: No valid passes in passes file", type: .error) + } + } catch { + os_log("Can't present passkit: %{public}s", type: .error, error.localizedDescription) + } + } + + func extractDataEntriesFromZipAtFilePath(_ zipPath: URL) throws -> [Data] { + var dataObjects = [Data]() + let archive = try Archive(url: zipPath, accessMode: .read) + try archive.forEach { entry in + var passData = Data() + _ = try archive.extract(entry, skipCRC32: true) { data in + passData.append(data) + } + + if passData.count > 0 { + dataObjects.append(passData) + } + } + + return dataObjects + } +} From f84fb45b4c1a85f8d6280c164a3d53d509f63e57 Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Sun, 21 Jan 2024 17:54:28 -0800 Subject: [PATCH 15/70] Fix tunnel monitor check (#2361) Task/Issue URL: https://app.asana.com/0/414235014887631/1206397132422633/f Tech Design URL: CC: Description: Client PR for duckduckgo/BrowserServicesKit#628. --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- LocalPackages/DuckUI/Package.swift | 2 +- LocalPackages/SyncUI/Package.swift | 2 +- LocalPackages/Waitlist/Package.swift | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 38136f576a..eea70e8a5a 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -9934,7 +9934,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 101.1.4; + version = 101.1.5; }; }; C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 182990508e..cf42ab48df 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "7f9eb807af196ec3db8fe6f0b168df89ea16a11c", - "version" : "101.1.4" + "revision" : "1d3a891fa58182d10b7bfa3a9b29cec51909c3c7", + "version" : "101.1.5" } }, { diff --git a/LocalPackages/DuckUI/Package.swift b/LocalPackages/DuckUI/Package.swift index 97fa85f214..8ce072102e 100644 --- a/LocalPackages/DuckUI/Package.swift +++ b/LocalPackages/DuckUI/Package.swift @@ -31,7 +31,7 @@ let package = Package( targets: ["DuckUI"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.1.4"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.1.5"), ], targets: [ .target( diff --git a/LocalPackages/SyncUI/Package.swift b/LocalPackages/SyncUI/Package.swift index cf466c9fc2..5ea17fb2f3 100644 --- a/LocalPackages/SyncUI/Package.swift +++ b/LocalPackages/SyncUI/Package.swift @@ -33,7 +33,7 @@ let package = Package( ], dependencies: [ .package(path: "../DuckUI"), - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.1.4"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.1.5"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ diff --git a/LocalPackages/Waitlist/Package.swift b/LocalPackages/Waitlist/Package.swift index d561061857..b9d52ab4b9 100644 --- a/LocalPackages/Waitlist/Package.swift +++ b/LocalPackages/Waitlist/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["Waitlist", "WaitlistMocks"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.1.4"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.1.5"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ From 981d1b3eb3182633c428408b707a101097a6b72b Mon Sep 17 00:00:00 2001 From: Christopher Brind Date: Mon, 22 Jan 2024 09:11:04 +0000 Subject: [PATCH 16/70] Pull to refresh (#2348) --- Core/PixelEvent.swift | 4 +++- DuckDuckGo/TabViewController.swift | 19 +++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/Core/PixelEvent.swift b/Core/PixelEvent.swift index 2f0fe3200b..8609faf395 100644 --- a/Core/PixelEvent.swift +++ b/Core/PixelEvent.swift @@ -32,6 +32,7 @@ extension Pixel { case appLaunch case refreshPressed + case pullToRefresh case forgetAllPressedBrowsing case forgetAllPressedTabSwitching @@ -530,7 +531,8 @@ extension Pixel.Event { switch self { case .appLaunch: return "ml" case .refreshPressed: return "m_r" - + case .pullToRefresh: return "m_pull-to-reload" + case .forgetAllPressedBrowsing: return "mf_bp" case .forgetAllPressedTabSwitching: return "mf_tp" case .forgetAllExecuted: return "mf" diff --git a/DuckDuckGo/TabViewController.swift b/DuckDuckGo/TabViewController.swift index 22593be12e..62e9dd2845 100644 --- a/DuckDuckGo/TabViewController.swift +++ b/DuckDuckGo/TabViewController.swift @@ -415,7 +415,16 @@ class TabViewController: UIViewController { webView.navigationDelegate = self webView.uiDelegate = self webViewContainer.addSubview(webView) - + webView.scrollView.refreshControl = UIRefreshControl() + webView.scrollView.refreshControl?.addAction(UIAction { [weak self] _ in + guard let self else { return } + self.reload() + Pixel.fire(pixel: .pullToRefresh) + }, for: .valueChanged) + + webView.scrollView.refreshControl?.backgroundColor = .systemBackground + webView.scrollView.refreshControl?.tintColor = .label + updateContentMode() if #available(iOS 16.4, *) { @@ -646,6 +655,7 @@ class TabViewController: UIViewController { private func hideProgressIndicator() { progressWorker.didFinishLoading() + webView.scrollView.refreshControl?.endRefreshing() } public func reload() { @@ -1174,7 +1184,7 @@ extension TabViewController: WKNavigationDelegate { private func onWebpageDidFinishLoading() { os_log("webpageLoading finished", log: .generalLog, type: .debug) - + tabModel.link = link delegate?.tabLoadingStateDidChange(tab: self) @@ -2290,6 +2300,11 @@ extension TabViewController: Themable { errorHeader.textColor = theme.barTintColor errorMessage.textColor = theme.barTintColor + if let webView { + webView.scrollView.refreshControl?.backgroundColor = theme.mainViewBackgroundColor + webView.scrollView.refreshControl?.tintColor = .secondaryLabel + } + switch theme.currentImageSet { case .light: errorInfoImage?.image = UIImage(named: "ErrorInfoLight") From f3094870ccc7e63b3f530082fbb298f63adfbfcd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 14:18:28 +0100 Subject: [PATCH 17/70] Bump submodules/privacy-reference-tests from `a3acc21` to `6b7ad1e` (#2321) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Graeme Arthur --- submodules/privacy-reference-tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/privacy-reference-tests b/submodules/privacy-reference-tests index a3acc21947..6b7ad1e7f1 160000 --- a/submodules/privacy-reference-tests +++ b/submodules/privacy-reference-tests @@ -1 +1 @@ -Subproject commit a3acc2194758bec0f01f57dd0c5f106de01a354e +Subproject commit 6b7ad1e7f15270f9dfeb58a272199f4d57c3eb22 From 5533bb1dd3af5dcd415ebbe9355926ee7c1d3237 Mon Sep 17 00:00:00 2001 From: Anh Do <18567+quanganhdo@users.noreply.github.com> Date: Mon, 22 Jan 2024 14:50:03 -0500 Subject: [PATCH 18/70] Add NetP feedback form (#2343) Task/Issue URL: https://app.asana.com/0/72649045549333/1206151925871430/f Description: Implements the native feedback form for NetP and collect necessary metadata. --- Core/PixelEvent.swift | 3 + DuckDuckGo.xcodeproj/project.pbxproj | 30 ++- .../xcshareddata/swiftpm/Package.resolved | 6 +- DuckDuckGo/Feedback/VPNFeedbackCategory.swift | 42 +++ DuckDuckGo/Feedback/VPNFeedbackFormView.swift | 231 ++++++++++++++++ .../Feedback/VPNFeedbackFormViewModel.swift | 88 +++++++ DuckDuckGo/Feedback/VPNFeedbackSender.swift | 47 ++++ .../Feedback/VPNMetadataCollector.swift | 247 ++++++++++++++++++ DuckDuckGo/NetworkProtectionStatusView.swift | 18 +- DuckDuckGo/UserText.swift | 35 ++- DuckDuckGo/ViewExtension.swift | 13 +- DuckDuckGo/en.lproj/Localizable.strings | 78 ++++++ LocalPackages/DuckUI/Package.swift | 2 +- LocalPackages/SyncUI/Package.swift | 2 +- LocalPackages/Waitlist/Package.swift | 2 +- ...etworkProtectionPacketTunnelProvider.swift | 5 +- 16 files changed, 833 insertions(+), 16 deletions(-) create mode 100644 DuckDuckGo/Feedback/VPNFeedbackCategory.swift create mode 100644 DuckDuckGo/Feedback/VPNFeedbackFormView.swift create mode 100644 DuckDuckGo/Feedback/VPNFeedbackFormViewModel.swift create mode 100644 DuckDuckGo/Feedback/VPNFeedbackSender.swift create mode 100644 DuckDuckGo/Feedback/VPNMetadataCollector.swift diff --git a/Core/PixelEvent.swift b/Core/PixelEvent.swift index 8609faf395..8e449ec840 100644 --- a/Core/PixelEvent.swift +++ b/Core/PixelEvent.swift @@ -281,6 +281,8 @@ extension Pixel { case networkProtectionEnabledOnSearch + case networkProtectionBreakageReport + case networkProtectionRekeyCompleted case networkProtectionTunnelConfigurationNoServerRegistrationInfo @@ -780,6 +782,7 @@ extension Pixel.Event { case .networkProtectionLatencyError: return "m_netp_ev_latency_error_d" case .networkProtectionRekeyCompleted: return "m_netp_rekey_completed" case .networkProtectionEnabledOnSearch: return "m_netp_ev_enabled_on_search" + case .networkProtectionBreakageReport: return "m_vpn_breakage_report" case .networkProtectionTunnelConfigurationNoServerRegistrationInfo: return "m_netp_tunnel_config_error_no_server_registration_info" case .networkProtectionTunnelConfigurationCouldNotSelectClosestServer: return "m_netp_tunnel_config_error_could_not_select_closest_server" case .networkProtectionTunnelConfigurationCouldNotGetPeerPublicKey: return "m_netp_tunnel_config_error_could_not_get_peer_public_key" diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index eea70e8a5a..bc3555de17 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -697,6 +697,11 @@ B6BA95C528894A28004ABA20 /* BrowsingMenuViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B6BA95C428894A28004ABA20 /* BrowsingMenuViewController.storyboard */; }; B6BA95E828924730004ABA20 /* JSAlertController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B6BA95E728924730004ABA20 /* JSAlertController.storyboard */; }; B6CB93E5286445AB0090FEB4 /* Base64DownloadSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6CB93E4286445AB0090FEB4 /* Base64DownloadSession.swift */; }; + BD862E032B30DA170073E2EE /* VPNFeedbackFormViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD862E022B30DA170073E2EE /* VPNFeedbackFormViewModel.swift */; }; + BD862E052B30DB250073E2EE /* VPNFeedbackCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD862E042B30DB250073E2EE /* VPNFeedbackCategory.swift */; }; + BD862E072B30F5E30073E2EE /* VPNFeedbackSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD862E062B30F5E30073E2EE /* VPNFeedbackSender.swift */; }; + BD862E092B30F63E0073E2EE /* VPNMetadataCollector.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD862E082B30F63E0073E2EE /* VPNMetadataCollector.swift */; }; + BD862E0B2B30F9300073E2EE /* VPNFeedbackFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD862E0A2B30F9300073E2EE /* VPNFeedbackFormView.swift */; }; BDC234F72B27F51100D3C798 /* UniquePixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDC234F62B27F51100D3C798 /* UniquePixel.swift */; }; C10CB5F32A1A5BDF0048E503 /* AutofillViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10CB5F22A1A5BDF0048E503 /* AutofillViews.swift */; }; C111B26927F579EF006558B1 /* BookmarkOrFolderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C111B26827F579EF006558B1 /* BookmarkOrFolderTests.swift */; }; @@ -2324,6 +2329,11 @@ B6BA95C428894A28004ABA20 /* BrowsingMenuViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = BrowsingMenuViewController.storyboard; sourceTree = ""; }; B6BA95E728924730004ABA20 /* JSAlertController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = JSAlertController.storyboard; sourceTree = ""; }; B6CB93E4286445AB0090FEB4 /* Base64DownloadSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Base64DownloadSession.swift; sourceTree = ""; }; + BD862E022B30DA170073E2EE /* VPNFeedbackFormViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNFeedbackFormViewModel.swift; sourceTree = ""; }; + BD862E042B30DB250073E2EE /* VPNFeedbackCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNFeedbackCategory.swift; sourceTree = ""; }; + BD862E062B30F5E30073E2EE /* VPNFeedbackSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNFeedbackSender.swift; sourceTree = ""; }; + BD862E082B30F63E0073E2EE /* VPNMetadataCollector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNMetadataCollector.swift; sourceTree = ""; }; + BD862E0A2B30F9300073E2EE /* VPNFeedbackFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNFeedbackFormView.swift; sourceTree = ""; }; BDC234F62B27F51100D3C798 /* UniquePixel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UniquePixel.swift; sourceTree = ""; }; C10CB5F22A1A5BDF0048E503 /* AutofillViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillViews.swift; sourceTree = ""; }; C111B26827F579EF006558B1 /* BookmarkOrFolderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkOrFolderTests.swift; sourceTree = ""; }; @@ -4372,6 +4382,18 @@ name = ContentBlocking; sourceTree = ""; }; + BD862E012B30D9FB0073E2EE /* Feedback */ = { + isa = PBXGroup; + children = ( + BD862E022B30DA170073E2EE /* VPNFeedbackFormViewModel.swift */, + BD862E042B30DB250073E2EE /* VPNFeedbackCategory.swift */, + BD862E062B30F5E30073E2EE /* VPNFeedbackSender.swift */, + BD862E082B30F63E0073E2EE /* VPNMetadataCollector.swift */, + BD862E0A2B30F9300073E2EE /* VPNFeedbackFormView.swift */, + ); + path = Feedback; + sourceTree = ""; + }; C14882D627F2010700D59F0C /* ImportExport */ = { isa = PBXGroup; children = ( @@ -4790,6 +4812,7 @@ EE0153E22A6FE031002A8B26 /* Root */, EE0153DF2A6EABAF002A8B26 /* Helpers */, EEFD562D2A65B68B00DAEC48 /* Invite */, + BD862E012B30D9FB0073E2EE /* Feedback */, EECD94B32A28B96C0085C66E /* Status */, 4B5C46282AF2A6DB002A4432 /* Intents */, 4B274F5E2AFEAEB3003F0745 /* Widget */, @@ -6597,6 +6620,7 @@ CB258D1329A4F24E00DEBA24 /* ConfigurationStore.swift in Sources */, 85058370219F424500ED4EDB /* SearchBarExtension.swift in Sources */, 310D09212799FD1A00DC0060 /* MIMEType.swift in Sources */, + BD862E032B30DA170073E2EE /* VPNFeedbackFormViewModel.swift in Sources */, F4147354283BF834004AA7A5 /* AutofillContentScopeFeatureToggles.swift in Sources */, 986DA94A24884B18004A7E39 /* WebViewTransition.swift in Sources */, 31B524572715BB23002225AB /* WebJSAlert.swift in Sources */, @@ -6682,6 +6706,7 @@ D6E83C602B22B3C9006C8AFB /* SettingsState.swift in Sources */, D6E83C482B20C812006C8AFB /* SettingsHostingController.swift in Sources */, F46FEC5727987A5F0061D9DF /* KeychainItemsDebugViewController.swift in Sources */, + BD862E0B2B30F9300073E2EE /* VPNFeedbackFormView.swift in Sources */, 02341FA62A4379CC008A1531 /* OnboardingStepViewModel.swift in Sources */, 850365F323DE087800D0F787 /* UIImageViewExtension.swift in Sources */, 373608922ABB430D00629E7F /* FavoritesDisplayMode+UserDefaults.swift in Sources */, @@ -6714,6 +6739,7 @@ EEC02C142B0519DE0045CE11 /* NetworkProtectionVPNLocationViewModel.swift in Sources */, F13B4BC01F180D8A00814661 /* TabsModel.swift in Sources */, 02025B0C29884D2C00E694E7 /* AppTrackerData.swift in Sources */, + BD862E052B30DB250073E2EE /* VPNFeedbackCategory.swift in Sources */, 85AE6690209724120014CF04 /* NotificationView.swift in Sources */, 1EA51376286596A000493C6A /* PrivacyIconLogic.swift in Sources */, 980891A92238504B00313A70 /* UILabelExtension.swift in Sources */, @@ -6790,6 +6816,7 @@ B652DF13287C373A00C12A9C /* ScriptSourceProviding.swift in Sources */, 854A012B2A54412600FCC628 /* ActivityViewController.swift in Sources */, F1CA3C391F045885005FADB3 /* PrivacyUserDefaults.swift in Sources */, + BD862E072B30F5E30073E2EE /* VPNFeedbackSender.swift in Sources */, AA4D6A6A23DB87B1007E8790 /* AppIconManager.swift in Sources */, 8563A03C1F9288D600F04442 /* BrowserChromeManager.swift in Sources */, 980891A32237146B00313A70 /* Feedback.swift in Sources */, @@ -6865,6 +6892,7 @@ 1E7A711C2934EEBC00B7EA19 /* OmniBarNotification.swift in Sources */, 02EC02C429AFA33000557F1A /* AppTPBreakageFormView.swift in Sources */, F15D43201E706CC500BF2CDC /* AutocompleteViewController.swift in Sources */, + BD862E092B30F63E0073E2EE /* VPNMetadataCollector.swift in Sources */, 98728E822417E3300033960E /* BrokenSiteInfo.swift in Sources */, D6E83C682B23B6A3006C8AFB /* FontSettings.swift in Sources */, 31EF52E1281B3BDC0034796E /* AutofillLoginListItemViewModel.swift in Sources */, @@ -9934,7 +9962,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 101.1.5; + version = 101.2.0; }; }; C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index cf42ab48df..abda3a95e5 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "1d3a891fa58182d10b7bfa3a9b29cec51909c3c7", - "version" : "101.1.5" + "revision" : "4b9b5339f21647efca6aef66b8ed400a7cfa804f", + "version" : "101.2.0" } }, { @@ -156,7 +156,7 @@ { "identity" : "trackerradarkit", "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/TrackerRadarKit.git", + "location" : "https://github.com/duckduckgo/TrackerRadarKit", "state" : { "revision" : "a6b7ba151d9dc6684484f3785293875ec01cc1ff", "version" : "1.2.2" diff --git a/DuckDuckGo/Feedback/VPNFeedbackCategory.swift b/DuckDuckGo/Feedback/VPNFeedbackCategory.swift new file mode 100644 index 0000000000..4c478c662d --- /dev/null +++ b/DuckDuckGo/Feedback/VPNFeedbackCategory.swift @@ -0,0 +1,42 @@ +// +// VPNFeedbackCategory.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +enum VPNFeedbackCategory: String, CaseIterable { + case unableToInstall + case failsToConnect + case tooSlow + case issueWithAppOrWebsite + case cantConnectToLocalDevice + case appCrashesOrFreezes + case featureRequest + case somethingElse + + var displayName: String { + switch self { + case .unableToInstall: return UserText.vpnFeedbackFormCategoryUnableToInstall + case .failsToConnect: return UserText.vpnFeedbackFormCategoryFailsToConnect + case .tooSlow: return UserText.vpnFeedbackFormCategoryTooSlow + case .issueWithAppOrWebsite: return UserText.vpnFeedbackFormCategoryIssuesWithApps + case .cantConnectToLocalDevice: return UserText.vpnFeedbackFormCategoryLocalDeviceConnectivity + case .appCrashesOrFreezes: return UserText.vpnFeedbackFormCategoryBrowserCrashOrFreeze + case .featureRequest: return UserText.vpnFeedbackFormCategoryFeatureRequest + case .somethingElse: return UserText.vpnFeedbackFormCategoryOther + } + } +} diff --git a/DuckDuckGo/Feedback/VPNFeedbackFormView.swift b/DuckDuckGo/Feedback/VPNFeedbackFormView.swift new file mode 100644 index 0000000000..9e666dccc8 --- /dev/null +++ b/DuckDuckGo/Feedback/VPNFeedbackFormView.swift @@ -0,0 +1,231 @@ +// +// VPNFeedbackFormView.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if NETWORK_PROTECTION + +import SwiftUI +import NetworkProtection + +@available(iOS 15.0, *) +struct VPNFeedbackFormCategoryView: View { + @Environment(\.dismiss) private var dismiss + + var body: some View { + VStack { + List { + Section { + ForEach(VPNFeedbackCategory.allCases, id: \.self) { category in + NavigationLink { + VPNFeedbackFormView(viewModel: VPNFeedbackFormViewModel(category: category)) { + dismiss() + DispatchQueue.main.async { + ActionMessageView.present(message: UserText.vpnFeedbackFormSubmittedMessage, + presentationLocation: .withoutBottomBar) + } + } + } label: { + Text(category.displayName) + .daxBodyRegular() + .foregroundColor(.init(designSystemColor: .textPrimary)) + } + } + } header: { + header() + } + .increaseHeaderProminence() + } + .listRowBackground(Color(designSystemColor: .surface)) + } + .applyInsetGroupedListStyle() + .navigationTitle(UserText.netPStatusViewShareFeedback) + } + + @ViewBuilder + private func header() -> some View { + HStack { + Spacer(minLength: 0) + VStack(alignment: .center, spacing: 8) { + Text(UserText.vpnFeedbackFormTitle) + .daxHeadline() + .multilineTextAlignment(.center) + .foregroundColor(.init(designSystemColor: .textPrimary)) + Text(UserText.vpnFeedbackFormCategorySelect) + .daxFootnoteRegular() + .multilineTextAlignment(.center) + .foregroundColor(.init(designSystemColor: .textSecondary)) + } + .padding(.vertical, 16) + .background(Color(designSystemColor: .background)) + Spacer(minLength: 0) + } + } +} + +@available(iOS 15.0, *) +struct VPNFeedbackFormView: View { + @ObservedObject var viewModel: VPNFeedbackFormViewModel + @Environment(\.dismiss) private var dismiss + @State private var showsError = false + @FocusState private var isTextEditorFocused: Bool + + var onDismiss: () -> Void + + var body: some View { + configuredForm() + .applyBackground() + .navigationTitle(UserText.netPStatusViewShareFeedback) + .alert(isPresented: $showsError) { + Alert(title: Text(UserText.vpnFeedbackFormErrorTitle), + message: Text(UserText.vpnFeedbackFormErrorMessage), + dismissButton: .default(Text(UserText.vpnFeedbackFormErrorAction))) + } + } + + @ViewBuilder + private func form() -> some View { + ScrollView { + ScrollViewReader { scrollView in + VStack { + header() + textEditor() + .focused($isTextEditorFocused) + .onChange(of: isTextEditorFocused) { isFocused in + guard isFocused else { return } + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + withAnimation { + scrollView.scrollTo(1, anchor: .top) + } + } + } + submitButton() + } + } + } + } + + @ViewBuilder + private func configuredForm() -> some View { + if #available(iOS 16, *) { + form().scrollDismissesKeyboard(.interactively) + } else { + form() + } + } + + @ViewBuilder + private func header() -> some View { + HStack { + Spacer(minLength: 0) + VStack(alignment: .center, spacing: 8) { + Text(UserText.vpnFeedbackFormTitle) + .daxHeadline() + .multilineTextAlignment(.center) + .foregroundColor(.init(designSystemColor: .textPrimary)) + Text(viewModel.categoryName) + .daxFootnoteRegular() + .multilineTextAlignment(.center) + .foregroundColor(.init(designSystemColor: .textSecondary)) + } + .padding(.vertical, 16) + .background(Color(designSystemColor: .background)) + Spacer(minLength: 0) + } + } + + @ViewBuilder + private func textEditor() -> some View { + VStack(alignment: .leading, spacing: 10) { + Text(UserText.vpnFeedbackFormText1) + .multilineTextAlignment(.leading) + .lineLimit(nil) + .fixedSize(horizontal: false, vertical: true) + + Spacer() + .frame(height: 1) + .id(1) + + TextEditor(text: $viewModel.feedbackFormText) + .font(.body) + .foregroundColor(.primary) + .frame(height: 100) + .fixedSize(horizontal: false, vertical: true) + .onChange(of: viewModel.feedbackFormText) { + viewModel.feedbackFormText = String($0.prefix(1000)) + } + .padding(EdgeInsets(top: 3.0, leading: 6.0, bottom: 5.0, trailing: 0.0)) + .clipShape(RoundedRectangle(cornerRadius: 8.0, style: .continuous)) + .background( + ZStack { + RoundedRectangle(cornerRadius: 8.0) + .stroke(Color(designSystemColor: .textPrimary), lineWidth: 0.4) + RoundedRectangle(cornerRadius: 8.0) + .fill(Color(designSystemColor: .panel)) + } + ) + + Text(UserText.vpnFeedbackFormText2) + .multilineTextAlignment(.leading) + .lineLimit(nil) + .fixedSize(horizontal: false, vertical: true) + + VStack(alignment: .leading) { + Text(UserText.vpnFeedbackFormText3) + Text(UserText.vpnFeedbackFormText4) + } + + Text(UserText.vpnFeedbackFormText5) + .multilineTextAlignment(.leading) + .lineLimit(nil) + .fixedSize(horizontal: false, vertical: true) + } + .foregroundColor(.secondary) + .background(Color(designSystemColor: .background)) + .padding(16) + .daxFootnoteRegular() + } + + @ViewBuilder + private func submitButton() -> some View { + Button { + Task { + let success = await viewModel.process() + if success { + dismiss() + onDismiss() + } else { + showsError = true + } + } + } label: { + Text(UserText.vpnFeedbackFormButtonSubmit) + .daxButton() + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .frame(height: 50) + .background( + RoundedRectangle(cornerRadius: 8) + .fill(Color(designSystemColor: .accent)) + ) + .padding(.horizontal, 16) + } + .padding(.vertical, 16) + } +} + +#endif diff --git a/DuckDuckGo/Feedback/VPNFeedbackFormViewModel.swift b/DuckDuckGo/Feedback/VPNFeedbackFormViewModel.swift new file mode 100644 index 0000000000..ec7a10852f --- /dev/null +++ b/DuckDuckGo/Feedback/VPNFeedbackFormViewModel.swift @@ -0,0 +1,88 @@ +// +// VPNFeedbackFormViewModel.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +final class VPNFeedbackFormViewModel: ObservableObject { + + enum ViewState { + case feedbackPending + case feedbackSending + case feedbackSendingFailed + case feedbackSent + + var canSubmit: Bool { + switch self { + case .feedbackPending: return true + case .feedbackSending: return false + case .feedbackSendingFailed: return true + case .feedbackSent: return false + } + } + } + + @Published var viewState: ViewState = .feedbackPending { + didSet { + updateSubmitButtonStatus() + } + } + + @Published var feedbackFormText: String = "" { + didSet { + updateSubmitButtonStatus() + } + } + + @Published private(set) var submitButtonEnabled: Bool = false + + var categoryName: String { + category.displayName + } + + private let metadataCollector: VPNMetadataCollector + private let feedbackSender: VPNFeedbackSender + private let category: VPNFeedbackCategory + + init(metadataCollector: VPNMetadataCollector = DefaultVPNMetadataCollector(), feedbackSender: VPNFeedbackSender = DefaultVPNFeedbackSender(), category: VPNFeedbackCategory) { + self.metadataCollector = metadataCollector + self.feedbackSender = feedbackSender + self.category = category + } + + @MainActor + func process() async -> Bool { + viewState = .feedbackSending + + do { + let metadata = await metadataCollector.collectMetadata() + try await feedbackSender.send(metadata: metadata, category: category, userText: feedbackFormText) + viewState = .feedbackSent + return true + } catch { + viewState = .feedbackSendingFailed + } + + return false + } + + private func updateSubmitButtonStatus() { + self.submitButtonEnabled = viewState.canSubmit && !feedbackFormText.isEmpty + } + +} diff --git a/DuckDuckGo/Feedback/VPNFeedbackSender.swift b/DuckDuckGo/Feedback/VPNFeedbackSender.swift new file mode 100644 index 0000000000..4b80c1b961 --- /dev/null +++ b/DuckDuckGo/Feedback/VPNFeedbackSender.swift @@ -0,0 +1,47 @@ +// +// VPNFeedbackSender.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import Core + +protocol VPNFeedbackSender { + func send(metadata: VPNMetadata, category: VPNFeedbackCategory, userText: String) async throws +} + +struct DefaultVPNFeedbackSender: VPNFeedbackSender { + + func send(metadata: VPNMetadata, category: VPNFeedbackCategory, userText: String) async throws { + let encodedUserText = userText.addingPercentEncoding(withAllowedCharacters: .alphanumerics.union(.init(charactersIn: "-._~"))) ?? userText + + try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in + Pixel.fire(pixel: .networkProtectionBreakageReport, withAdditionalParameters: [ + "breakageCategory": category.rawValue, + "breakageDescription": encodedUserText, + "breakageMetadata": metadata.toBase64(), + ]) { error in + if let error { + continuation.resume(throwing: error) + } else { + continuation.resume() + } + } + } + } + +} diff --git a/DuckDuckGo/Feedback/VPNMetadataCollector.swift b/DuckDuckGo/Feedback/VPNMetadataCollector.swift new file mode 100644 index 0000000000..3ab9f25421 --- /dev/null +++ b/DuckDuckGo/Feedback/VPNMetadataCollector.swift @@ -0,0 +1,247 @@ +// +// VPNMetadataCollector.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import BrowserServicesKit +import Core +import Common +import NetworkProtection +import NetworkExtension +import Network + +struct VPNMetadata: Encodable { + + struct AppInfo: Encodable { + let appVersion: String + let lastVersionRun: String + let isInternalUser: Bool + } + + struct DeviceInfo: Encodable { + let osVersion: String + let lowPowerModeEnabled: Bool + } + + struct NetworkInfo: Encodable { + let currentPath: String + let lastPathChangeDate: String + let lastPathChange: String + let secondsSincePathChange: String + } + + struct VPNState: Encodable { + let connectionState: String + let lastDisconnectError: String + let connectedServer: String + let connectedServerIP: String + } + + struct VPNSettingsState: Encodable { + let connectOnLoginEnabled: Bool + let includeAllNetworksEnabled: Bool + let enforceRoutesEnabled: Bool + let excludeLocalNetworksEnabled: Bool + let notifyStatusChangesEnabled: Bool + let selectedServer: String + } + + let appInfo: AppInfo + let deviceInfo: DeviceInfo + let networkInfo: NetworkInfo + let vpnState: VPNState + let vpnSettingsState: VPNSettingsState + + func toPrettyPrintedJSON() -> String? { + let encoder = JSONEncoder() + encoder.outputFormatting = .prettyPrinted + + guard let encodedMetadata = try? encoder.encode(self) else { + assertionFailure("Failed to encode metadata") + return nil + } + + return String(data: encodedMetadata, encoding: .utf8) + } + + func toBase64() -> String { + let encoder = JSONEncoder() + + do { + let encodedMetadata = try encoder.encode(self) + return encodedMetadata.base64EncodedString() + } catch { + return "Failed to encode metadata to JSON, error message: \(error.localizedDescription)" + } + } +} + +protocol VPNMetadataCollector { + func collectMetadata() async -> VPNMetadata +} + +final class DefaultVPNMetadataCollector: VPNMetadataCollector { + private let statusObserver: ConnectionStatusObserver + private let serverInfoObserver: ConnectionServerInfoObserver + private let settings: VPNSettings + + init(statusObserver: ConnectionStatusObserver = ConnectionStatusObserverThroughSession(), + serverInfoObserver: ConnectionServerInfoObserver = ConnectionServerInfoObserverThroughSession(), + settings: VPNSettings = .init(defaults: .networkProtectionGroupDefaults)) { + self.statusObserver = statusObserver + self.serverInfoObserver = serverInfoObserver + self.settings = settings + } + + func collectMetadata() async -> VPNMetadata { + let appInfoMetadata = collectAppInfoMetadata() + let deviceInfoMetadata = collectDeviceInfoMetadata() + let networkInfoMetadata = await collectNetworkInformation() + let vpnState = await collectVPNState() + let vpnSettingsState = collectVPNSettingsState() + + return VPNMetadata( + appInfo: appInfoMetadata, + deviceInfo: deviceInfoMetadata, + networkInfo: networkInfoMetadata, + vpnState: vpnState, + vpnSettingsState: vpnSettingsState + ) + } + + // MARK: - Metadata Collection + + private func collectAppInfoMetadata() -> VPNMetadata.AppInfo { + let appVersion = AppVersion.shared.versionNumber + let versionStore = NetworkProtectionLastVersionRunStore() + let isInternalUser = AppDependencyProvider.shared.internalUserDecider.isInternalUser + + return .init(appVersion: appVersion, lastVersionRun: versionStore.lastVersionRun ?? "Unknown", isInternalUser: isInternalUser) + } + + private func collectDeviceInfoMetadata() -> VPNMetadata.DeviceInfo { + .init(osVersion: AppVersion.shared.osVersion, lowPowerModeEnabled: ProcessInfo.processInfo.isLowPowerModeEnabled) + } + + func collectNetworkInformation() async -> VPNMetadata.NetworkInfo { + let monitor = NWPathMonitor() + monitor.start(queue: DispatchQueue(label: "VPNMetadataCollector.NWPathMonitor.paths")) + + var path: Network.NWPath? + let startTime = CFAbsoluteTimeGetCurrent() + + let dateFormatter = DateFormatter() + dateFormatter.calendar = Calendar.current + dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) + dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" + + let networkPathChange = settings.networkPathChange + + let lastPathChange = String(describing: networkPathChange) + var lastPathChangeDate = "unknown" + var secondsSincePathChange = "unknown" + + if let changeDate = networkPathChange?.date { + lastPathChangeDate = dateFormatter.string(from: changeDate) + secondsSincePathChange = String(Date().timeIntervalSince(changeDate)) + } + + while true { + if !monitor.currentPath.availableInterfaces.isEmpty { + path = monitor.currentPath + monitor.cancel() + + return .init(currentPath: path.debugDescription, + lastPathChangeDate: lastPathChangeDate, + lastPathChange: lastPathChange, + secondsSincePathChange: secondsSincePathChange) + } + + // Wait up to 3 seconds to fetch the path. + let currentExecutionTime = CFAbsoluteTimeGetCurrent() - startTime + if currentExecutionTime >= 3.0 { + return .init(currentPath: "Timed out fetching path", + lastPathChangeDate: lastPathChangeDate, + lastPathChange: lastPathChange, + secondsSincePathChange: secondsSincePathChange) + } + } + } + + @MainActor + func collectVPNState() async -> VPNMetadata.VPNState { + let connectionState = String(describing: statusObserver.recentValue) + let connectedServer = serverInfoObserver.recentValue.serverLocation ?? "none" + let connectedServerIP = serverInfoObserver.recentValue.serverAddress ?? "none" + + return .init(connectionState: connectionState, + lastDisconnectError: await lastDisconnectError(), + connectedServer: connectedServer, + connectedServerIP: connectedServerIP) + } + + private func lastDisconnectError() async -> String { + if #available(iOS 16, *) { + guard let tunnelManager = try? await NETunnelProviderManager.loadAllFromPreferences().first else { + return "none" + } + + return await withCheckedContinuation { continuation in + tunnelManager.connection.fetchLastDisconnectError { error in + let message = { + if let error = error as? NSError { + if error.domain == NEVPNConnectionErrorDomain, let code = NEDNSSettingsManagerError(rawValue: error.code) { + switch code { + case .configurationCannotBeRemoved: + return "configurationCannotBeRemoved" + case .configurationDisabled: + return "configurationDisabled" + case .configurationInvalid: + return "configurationInvalid" + case .configurationStale: + return "configurationStale" + default: + return error.localizedDescription + } + } else { + return error.localizedDescription + } + } + + return "none" + }() + + continuation.resume(returning: message) + } + } + } + + return "none" + } + + func collectVPNSettingsState() -> VPNMetadata.VPNSettingsState { + return .init( + connectOnLoginEnabled: settings.connectOnLogin, + includeAllNetworksEnabled: settings.includeAllNetworks, + enforceRoutesEnabled: settings.enforceRoutes, + excludeLocalNetworksEnabled: settings.excludeLocalNetworks, + notifyStatusChangesEnabled: settings.notifyStatusChanges, + selectedServer: settings.selectedServer.stringValue ?? "automatic" + ) + } +} diff --git a/DuckDuckGo/NetworkProtectionStatusView.swift b/DuckDuckGo/NetworkProtectionStatusView.swift index ba7ab7aaaa..a862281d9a 100644 --- a/DuckDuckGo/NetworkProtectionStatusView.swift +++ b/DuckDuckGo/NetworkProtectionStatusView.swift @@ -25,6 +25,7 @@ import NetworkProtection @available(iOS 15, *) struct NetworkProtectionStatusView: View { @StateObject public var statusModel: NetworkProtectionStatusViewModel + @State private var isFeedbackFormActive = false var body: some View { List { @@ -153,12 +154,25 @@ struct NetworkProtectionStatusView: View { @ViewBuilder private func inviteOnlyFooter() -> some View { - // Needs to be inlined like this for the markdown parsing to work - Text("\(UserText.networkProtectionWaitlistAvailabilityDisclaimer) [\(UserText.netPStatusViewShareFeedback)](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)") + Text("\(UserText.networkProtectionWaitlistAvailabilityDisclaimer) [\(UserText.netPStatusViewShareFeedback)](share-feedback)") .foregroundColor(.init(designSystemColor: .textSecondary)) .accentColor(.init(designSystemColor: .accent)) .daxFootnoteRegular() .padding(.top, 6) + .background(NavigationLink(isActive: $isFeedbackFormActive) { + VPNFeedbackFormCategoryView() + } label: { + EmptyView() + }) + .environment(\.openURL, OpenURLAction { url in + switch url.absoluteString { + case "share-feedback": + isFeedbackFormActive = true + return .handled + default: + return .discarded + } + }) } } diff --git a/DuckDuckGo/UserText.swift b/DuckDuckGo/UserText.swift index 6e6f3bb0a0..6f1b1a32be 100644 --- a/DuckDuckGo/UserText.swift +++ b/DuckDuckGo/UserText.swift @@ -660,8 +660,39 @@ In addition to the details entered into this form, your app issue report will co static let inviteDialogGetStartedButton = NSLocalizedString("invite.dialog.get.started.button", value: "Get Started", comment: "Get Started button on an invite dialog") static let inviteDialogUnrecognizedCodeMessage = NSLocalizedString("invite.dialog.unrecognized.code.message", value: "We didn’t recognize this Invite Code.", comment: "Message to show after user enters an unrecognized invite code") static let inviteDialogErrorAlertOKButton = NSLocalizedString("invite.alert.ok.button", value: "OK", comment: "OK title for invite screen alert dismissal button") - - + + // MARK: - Feedback Form + static let vpnFeedbackFormTitle = NSLocalizedString("vpn.feedback-form.title", value: "Help Improve the DuckDuckGo VPN", comment: "Title for each screen of the VPN feedback form") + static let vpnFeedbackFormCategorySelect = NSLocalizedString("vpn.feedback-form.category.select-category", value: "Select a category", comment: "Title for the category selection state of the VPN feedback form") + static let vpnFeedbackFormCategoryUnableToInstall = NSLocalizedString("vpn.feedback-form.category.unable-to-install", value: "Unable to install VPN", comment: "Title for the 'unable to install' category of the VPN feedback form") + static let vpnFeedbackFormCategoryFailsToConnect = NSLocalizedString("vpn.feedback-form.category.fails-to-connect", value: "VPN fails to connect", comment: "Title for the 'VPN fails to connect' category of the VPN feedback form") + static let vpnFeedbackFormCategoryTooSlow = NSLocalizedString("vpn.feedback-form.category.too-slow", value: "VPN connection is too slow", comment: "Title for the 'VPN is too slow' category of the VPN feedback form") + static let vpnFeedbackFormCategoryIssuesWithApps = NSLocalizedString("vpn.feedback-form.category.issues-with-apps", value: "VPN causes issues with other apps or websites", comment: "Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form") + static let vpnFeedbackFormCategoryLocalDeviceConnectivity = NSLocalizedString("vpn.feedback-form.category.local-device-connectivity", value: "VPN won't let me connect to local device", comment: "Title for the local device connectivity category of the VPN feedback form") + static let vpnFeedbackFormCategoryBrowserCrashOrFreeze = NSLocalizedString("vpn.feedback-form.category.browser-crash-or-freeze", value: "VPN causes browser to crash or freeze", comment: "Title for the browser crash/freeze category of the VPN feedback form") + static let vpnFeedbackFormCategoryFeatureRequest = NSLocalizedString("vpn.feedback-form.category.feature-request", value: "VPN feature request", comment: "Title for the 'VPN feature request' category of the VPN feedback form") + static let vpnFeedbackFormCategoryOther = NSLocalizedString("vpn.feedback-form.category.other", value: "Other VPN feedback", comment: "Title for the 'other VPN feedback' category of the VPN feedback form") + + static let vpnFeedbackFormText1 = NSLocalizedString("vpn.feedback-form.text-1", value: "Please describe what's happening, what you expected to happen, and the steps that led to the issue:", comment: "Text for the body of the VPN feedback form") + static let vpnFeedbackFormText2 = NSLocalizedString("vpn.feedback-form.text-2", value: "In addition to the details entered into this form, your app issue report will contain:", comment: "Text for the body of the VPN feedback form") + static let vpnFeedbackFormText3 = NSLocalizedString("vpn.feedback-form.text-3", value: "• Whether specific DuckDuckGo features are enabled", comment: "Bullet text for the body of the VPN feedback form") + static let vpnFeedbackFormText4 = NSLocalizedString("vpn.feedback-form.text-4", value: "• Aggregate DuckDuckGo app diagnostics", comment: "Bullet text for the body of the VPN feedback form") + static let vpnFeedbackFormText5 = NSLocalizedString("vpn.feedback-form.text-5", value: "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features.", comment: "Text for the body of the VPN feedback form") + + static let vpnFeedbackFormSendingConfirmationTitle = NSLocalizedString("vpn.feedback-form.sending-confirmation.title", value: "Thank you!", comment: "Title for the feedback sent view title of the VPN feedback form") + static let vpnFeedbackFormSendingConfirmationDescription = NSLocalizedString("vpn.feedback-form.sending-confirmation.description", value: "Your feedback will help us improve the\nDuckDuckGo VPN.", comment: "Title for the feedback sent view description of the VPN feedback form") + static let vpnFeedbackFormSendingConfirmationError = NSLocalizedString("vpn.feedback-form.sending-confirmation.error", value: "We couldn't send your feedback right now, please try again.", comment: "Title for the feedback sending error text of the VPN feedback form") + + static let vpnFeedbackFormButtonDone = NSLocalizedString("vpn.feedback-form.button.done", value: "Done", comment: "Title for the Done button of the VPN feedback form") + static let vpnFeedbackFormButtonCancel = NSLocalizedString("vpn.feedback-form.button.cancel", value: "Cancel", comment: "Title for the Cancel button of the VPN feedback form") + static let vpnFeedbackFormButtonSubmit = NSLocalizedString("vpn.feedback-form.button.submit", value: "Submit", comment: "Title for the Submit button of the VPN feedback form") + static let vpnFeedbackFormButtonSubmitting = NSLocalizedString("vpn.feedback-form.button.submitting", value: "Submitting…", comment: "Title for the Submitting state of the VPN feedback form") + + static let vpnFeedbackFormSubmittedMessage = NSLocalizedString("vpn.feedback-form.submitted.message", value: "Thank You! Feedback submitted.", comment: "Toast message when the VPN feedback form is submitted successfully") + static let vpnFeedbackFormErrorTitle = NSLocalizedString("vpn.feedback-form.error.title", value: "Error", comment: "Title for the alert when the VPN feedback form can't be submitted") + static let vpnFeedbackFormErrorMessage = NSLocalizedString("vpn.feedback-form.error.message", value: "Failed to share your feedback. Please try again.", comment: "Message for the alert when the VPN feedback form can't be submitted") + static let vpnFeedbackFormErrorAction = NSLocalizedString("vpn.feedback-form.error.action", value: "OK", comment: "Action title for the alert when the VPN feedback form can't be submitted") + // MARK: Notifications public static let macWaitlistAvailableNotificationTitle = NSLocalizedString("mac-waitlist.available.notification.title", value: "DuckDuckGo for Mac is ready!", comment: "Title for the macOS waitlist notification") diff --git a/DuckDuckGo/ViewExtension.swift b/DuckDuckGo/ViewExtension.swift index d7f45ec31c..24dab516d9 100644 --- a/DuckDuckGo/ViewExtension.swift +++ b/DuckDuckGo/ViewExtension.swift @@ -46,10 +46,15 @@ extension View { func applyInsetGroupedListStyle() -> some View { self .listStyle(.insetGrouped) - .hideScrollContentBackground() - .background( - Rectangle().ignoresSafeArea().foregroundColor(Color(designSystemColor: .background)) - ) + .applyBackground() + } + + @ViewBuilder + func applyBackground() -> some View { + hideScrollContentBackground() + .background( + Rectangle().ignoresSafeArea().foregroundColor(Color(designSystemColor: .background)) + ) } @ViewBuilder diff --git a/DuckDuckGo/en.lproj/Localizable.strings b/DuckDuckGo/en.lproj/Localizable.strings index 721303ff0f..3f4fec34a1 100644 --- a/DuckDuckGo/en.lproj/Localizable.strings +++ b/DuckDuckGo/en.lproj/Localizable.strings @@ -2131,6 +2131,84 @@ But if you *do* want a peek under the hood, you can find more information about /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "Audio is processed on-device. It's not stored or shared with anyone, including DuckDuckGo."; +/* Title for the Cancel button of the VPN feedback form */ +"vpn.feedback-form.button.cancel" = "Cancel"; + +/* Title for the Done button of the VPN feedback form */ +"vpn.feedback-form.button.done" = "Done"; + +/* Title for the Submit button of the VPN feedback form */ +"vpn.feedback-form.button.submit" = "Submit"; + +/* Title for the Submitting state of the VPN feedback form */ +"vpn.feedback-form.button.submitting" = "Submitting…"; + +/* Title for the browser crash/freeze category of the VPN feedback form */ +"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; + +/* Title for the 'VPN fails to connect' category of the VPN feedback form */ +"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; + +/* Title for the 'VPN feature request' category of the VPN feedback form */ +"vpn.feedback-form.category.feature-request" = "VPN feature request"; + +/* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ +"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; + +/* Title for the local device connectivity category of the VPN feedback form */ +"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; + +/* Title for the 'other VPN feedback' category of the VPN feedback form */ +"vpn.feedback-form.category.other" = "Other VPN feedback"; + +/* Title for the category selection state of the VPN feedback form */ +"vpn.feedback-form.category.select-category" = "Select a category"; + +/* Title for the 'VPN is too slow' category of the VPN feedback form */ +"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; + +/* Title for the 'unable to install' category of the VPN feedback form */ +"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; + +/* Action title for the alert when the VPN feedback form can't be submitted */ +"vpn.feedback-form.error.action" = "OK"; + +/* Message for the alert when the VPN feedback form can't be submitted */ +"vpn.feedback-form.error.message" = "Failed to share your feedback. Please try again."; + +/* Title for the alert when the VPN feedback form can't be submitted */ +"vpn.feedback-form.error.title" = "Error"; + +/* Title for the feedback sent view description of the VPN feedback form */ +"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; + +/* Title for the feedback sending error text of the VPN feedback form */ +"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; + +/* Title for the feedback sent view title of the VPN feedback form */ +"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; + +/* Toast message when the VPN feedback form is submitted successfully */ +"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; + +/* Text for the body of the VPN feedback form */ +"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; + +/* Text for the body of the VPN feedback form */ +"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; + +/* Bullet text for the body of the VPN feedback form */ +"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; + +/* Bullet text for the body of the VPN feedback form */ +"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; + +/* Text for the body of the VPN feedback form */ +"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; + +/* Title for each screen of the VPN feedback form */ +"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; + /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Allow Notifications"; diff --git a/LocalPackages/DuckUI/Package.swift b/LocalPackages/DuckUI/Package.swift index 8ce072102e..782c4c4dc6 100644 --- a/LocalPackages/DuckUI/Package.swift +++ b/LocalPackages/DuckUI/Package.swift @@ -31,7 +31,7 @@ let package = Package( targets: ["DuckUI"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.1.5"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.0"), ], targets: [ .target( diff --git a/LocalPackages/SyncUI/Package.swift b/LocalPackages/SyncUI/Package.swift index 5ea17fb2f3..d6975cabeb 100644 --- a/LocalPackages/SyncUI/Package.swift +++ b/LocalPackages/SyncUI/Package.swift @@ -33,7 +33,7 @@ let package = Package( ], dependencies: [ .package(path: "../DuckUI"), - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.1.5"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.0"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ diff --git a/LocalPackages/Waitlist/Package.swift b/LocalPackages/Waitlist/Package.swift index b9d52ab4b9..3cb19de708 100644 --- a/LocalPackages/Waitlist/Package.swift +++ b/LocalPackages/Waitlist/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["Waitlist", "WaitlistMocks"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.1.5"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.0"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ diff --git a/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift b/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift index 8bb0ed7ea8..180342ab9c 100644 --- a/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift +++ b/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift @@ -35,9 +35,10 @@ final class NetworkProtectionPacketTunnelProvider: PacketTunnelProvider { // MARK: - PacketTunnelProvider.Event reporting private static var packetTunnelProviderEvents: EventMapping = .init { event, _, _, _ in + let settings = VPNSettings(defaults: .networkProtectionGroupDefaults) + switch event { case .userBecameActive: - let settings = VPNSettings(defaults: .networkProtectionGroupDefaults) DailyPixel.fire(pixel: .networkProtectionActiveUser, withAdditionalParameters: ["cohort": UniquePixel.dateString(for: settings.vpnFirstEnabled)]) case .reportConnectionAttempt(attempt: let attempt): @@ -55,6 +56,8 @@ final class NetworkProtectionPacketTunnelProvider: PacketTunnelProvider { DailyPixel.fireDailyAndCount(pixel: .networkProtectionTunnelFailureDetected) case .failureRecovered: DailyPixel.fireDailyAndCount(pixel: .networkProtectionTunnelFailureRecovered) + case .networkPathChanged(let newPath): + settings.apply(change: .setNetworkPathChange(newPath)) } case .reportLatency(result: let result): switch result { From 6b6302326f6888704f8a91463d870242cab5c9d0 Mon Sep 17 00:00:00 2001 From: bwaresiak Date: Mon, 22 Jan 2024 21:20:43 +0100 Subject: [PATCH 19/70] Release 7.106.0 (#2366) --- Configuration/Version.xcconfig | 2 +- .../AppPrivacyConfigurationDataProvider.swift | 4 +- Core/ios-config.json | 46 +++++++++++++-- DuckDuckGo.xcodeproj/project.pbxproj | 56 +++++++++---------- DuckDuckGo/Settings.bundle/Root.plist | 2 +- fastlane/metadata/default/release_notes.txt | 3 +- 6 files changed, 74 insertions(+), 39 deletions(-) diff --git a/Configuration/Version.xcconfig b/Configuration/Version.xcconfig index 70dd42904b..cccf98adf5 100644 --- a/Configuration/Version.xcconfig +++ b/Configuration/Version.xcconfig @@ -1 +1 @@ -MARKETING_VERSION = 7.105.0 +MARKETING_VERSION = 7.106.0 diff --git a/Core/AppPrivacyConfigurationDataProvider.swift b/Core/AppPrivacyConfigurationDataProvider.swift index 763ffb8717..4f4eb843c8 100644 --- a/Core/AppPrivacyConfigurationDataProvider.swift +++ b/Core/AppPrivacyConfigurationDataProvider.swift @@ -23,8 +23,8 @@ import BrowserServicesKit final public class AppPrivacyConfigurationDataProvider: EmbeddedDataProvider { public struct Constants { - public static let embeddedDataETag = "\"489ab3f1fc4e889123d6f51a4c0aefec\"" - public static let embeddedDataSHA = "41adeed122f363b3ec2cd3ac189468bbe68b85b44e6a6ab342950561163cc263" + public static let embeddedDataETag = "\"4e984b6034f1e27fe85fdad5f4bf37c9\"" + public static let embeddedDataSHA = "d599888e7b447bbaeb2d9a7fd7ccf06956fce8976c316be2f497561a6832613e" } public var embeddedDataEtag: String { diff --git a/Core/ios-config.json b/Core/ios-config.json index 26dd418b97..d884041033 100644 --- a/Core/ios-config.json +++ b/Core/ios-config.json @@ -1,6 +1,6 @@ { "readme": "https://github.com/duckduckgo/privacy-configuration", - "version": 1705334213687, + "version": 1705931475791, "features": { "adClickAttribution": { "readme": "https://help.duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/#3rd-party-tracker-loading-protection", @@ -263,6 +263,9 @@ { "domain": "tuc.org.uk" }, + { + "domain": "newsmax.com" + }, { "domain": "earth.google.com" }, @@ -283,7 +286,7 @@ ] }, "state": "enabled", - "hash": "0e798c688c53c17c989fa3a41b8c9f65" + "hash": "b082ab4a63d22eb4eafee199c52b1c61" }, "autofill": { "exceptions": [ @@ -1091,9 +1094,13 @@ }, "customUserAgent": { "settings": { - "defaultPolicy": "closest", + "defaultPolicy": "brand", "ddgFixedSites": [], "ddgDefaultSites": [ + { + "domain": "chase.com", + "reason": "Login issues" + }, { "domain": "duckduckgo.com", "reason": "Internal exclusion to roll out experiment" @@ -1102,12 +1109,16 @@ "closestUserAgent": { "versions": [] }, - "omitApplicationSites": [], + "omitApplicationSites": [ + { + "domain": "chase.com" + } + ], "omitVersionSites": [] }, "exceptions": [], "state": "enabled", - "hash": "0f937241a1692188edfa86eb1d098d06" + "hash": "9461d22c4f0a25b30a46cc53c0718a32" }, "dbp": { "state": "disabled", @@ -4839,6 +4850,7 @@ "rule": "c.amazon-adsystem.com/aax2/apstag.js", "domains": [ "applesfera.com", + "inquirer.com", "thesurfersview.com", "wildrivers.lostcoastoutpost.com" ] @@ -4871,6 +4883,17 @@ } ] }, + "anyclip.com": { + "rules": [ + { + "rule": "player.anyclip.com/anyclip-widget/lre-widget", + "domains": [ + "dictionary.com", + "thesaurus.com" + ] + } + ] + }, "appboycdn.com": { "rules": [ { @@ -5339,6 +5362,7 @@ { "rule": "doubleclick.net/ondemand/hls/content/", "domains": [ + "10play.com.au", "history.com" ] }, @@ -5377,14 +5401,22 @@ "domains": [ "ah.nl", "applesfera.com", + "goplay.be", "nytimes.com", "realmadrid.com", "rocketnews24.com", + "stuff.co.nz", "uwbadgers.com", "wunderground.com", "youmath.it" ] }, + { + "rule": "securepubads.g.doubleclick.net/pagead/managed/js/gpt/.*/pubads_impl.js", + "domains": [ + "stuff.co.nz" + ] + }, { "rule": "securepubads.g.doubleclick.net/gpt/pubads_impl_", "domains": [ @@ -6275,6 +6307,8 @@ "domains": [ "essentialpraxis.com", "kidsguide.com", + "muc-off.com", + "paria.cc", "urbanebikes.com" ] }, @@ -7496,7 +7530,7 @@ "domain": "sundancecatalog.com" } ], - "hash": "3e64e53a87c3c5a3deb156f036be273e" + "hash": "f918a51b5651f2e3a99945ba530f3264" }, "trackingCookies1p": { "settings": { diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index eea70e8a5a..5ce8407ae1 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -8159,7 +8159,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProvider.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -8196,7 +8196,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8288,7 +8288,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8316,7 +8316,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8466,7 +8466,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8492,7 +8492,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; INFOPLIST_FILE = DuckDuckGo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8557,7 +8557,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -8592,7 +8592,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8626,7 +8626,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8657,7 +8657,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8944,7 +8944,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8975,7 +8975,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -9004,7 +9004,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -9038,7 +9038,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -9069,7 +9069,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProvider.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -9102,11 +9102,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 2; + DYLIB_CURRENT_VERSION = 0; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9344,7 +9344,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGoAlpha.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9371,7 +9371,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9404,7 +9404,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9442,7 +9442,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9478,7 +9478,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9513,11 +9513,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 2; + DYLIB_CURRENT_VERSION = 0; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9691,11 +9691,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 2; + DYLIB_CURRENT_VERSION = 0; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9724,10 +9724,10 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 2; + DYLIB_CURRENT_VERSION = 0; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; diff --git a/DuckDuckGo/Settings.bundle/Root.plist b/DuckDuckGo/Settings.bundle/Root.plist index 1b62a4befe..f6b1bab836 100644 --- a/DuckDuckGo/Settings.bundle/Root.plist +++ b/DuckDuckGo/Settings.bundle/Root.plist @@ -6,7 +6,7 @@ DefaultValue - 7.105.0 + 7.106.0 Key version Title diff --git a/fastlane/metadata/default/release_notes.txt b/fastlane/metadata/default/release_notes.txt index ea2040b588..e0c7a0d1ed 100644 --- a/fastlane/metadata/default/release_notes.txt +++ b/fastlane/metadata/default/release_notes.txt @@ -1,2 +1,3 @@ -Bug fixes and other improvements. +- You can now pull the page down to reload it. +- Bug fixes and other improvements. Join our fully distributed team and help raise the standard of trust online! https://duckduckgo.com/hiring From 09419bb94b40eff86c8243b84f0ab1d3187e89b8 Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Mon, 22 Jan 2024 21:13:55 -0800 Subject: [PATCH 20/70] Avoid sending latency immediately after the monitor starts (#2368) Task/Issue URL: https://app.asana.com/0/0/1206412872188595/f Tech Design URL: CC: @diegoreymendez Description: Client PR for duckduckgo/BrowserServicesKit#630 --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- LocalPackages/DuckUI/Package.swift | 2 +- LocalPackages/SyncUI/Package.swift | 2 +- LocalPackages/Waitlist/Package.swift | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index fd01290cb8..e9beba212a 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -9962,7 +9962,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 101.2.0; + version = 101.2.1; }; }; C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index abda3a95e5..66294e4aa3 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "4b9b5339f21647efca6aef66b8ed400a7cfa804f", - "version" : "101.2.0" + "revision" : "08a05ac3fa0b151f346bbcd48aec09f082a2ef4d", + "version" : "101.2.1" } }, { diff --git a/LocalPackages/DuckUI/Package.swift b/LocalPackages/DuckUI/Package.swift index 782c4c4dc6..d449a97803 100644 --- a/LocalPackages/DuckUI/Package.swift +++ b/LocalPackages/DuckUI/Package.swift @@ -31,7 +31,7 @@ let package = Package( targets: ["DuckUI"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.1"), ], targets: [ .target( diff --git a/LocalPackages/SyncUI/Package.swift b/LocalPackages/SyncUI/Package.swift index d6975cabeb..18de7a87dd 100644 --- a/LocalPackages/SyncUI/Package.swift +++ b/LocalPackages/SyncUI/Package.swift @@ -33,7 +33,7 @@ let package = Package( ], dependencies: [ .package(path: "../DuckUI"), - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.1"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ diff --git a/LocalPackages/Waitlist/Package.swift b/LocalPackages/Waitlist/Package.swift index 3cb19de708..771c8cdaac 100644 --- a/LocalPackages/Waitlist/Package.swift +++ b/LocalPackages/Waitlist/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["Waitlist", "WaitlistMocks"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.1"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ From ffd2a3ded5736020f75e518f43673ecdd113ec31 Mon Sep 17 00:00:00 2001 From: Diego Rey Mendez Date: Tue, 23 Jan 2024 09:54:05 +0100 Subject: [PATCH 21/70] Updates BSK to fix a VPN issue (#2367) Task/Issue URL: https://app.asana.com/0/414235014887631/1206412843502195/f macOS PR: https://github.com/duckduckgo/macos-browser/pull/2102 BSK PR: https://github.com/duckduckgo/BrowserServicesKit/pull/629 ## Description Fixes a multithreading issue in `NetworkConnectionTester` (BSK). We're now making it impossible for the tester to be started on multiple threads. --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 6 +++--- LocalPackages/DuckUI/Package.swift | 2 +- LocalPackages/SyncUI/Package.swift | 2 +- LocalPackages/Waitlist/Package.swift | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index e9beba212a..f6bbf59ad9 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -9962,7 +9962,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 101.2.1; + version = 101.2.2; }; }; C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 66294e4aa3..6c74085f32 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "08a05ac3fa0b151f346bbcd48aec09f082a2ef4d", - "version" : "101.2.1" + "revision" : "1f7932fe67a0d8b1ae97e62cb333639353d4772f", + "version" : "101.2.2" } }, { @@ -156,7 +156,7 @@ { "identity" : "trackerradarkit", "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/TrackerRadarKit", + "location" : "https://github.com/duckduckgo/TrackerRadarKit.git", "state" : { "revision" : "a6b7ba151d9dc6684484f3785293875ec01cc1ff", "version" : "1.2.2" diff --git a/LocalPackages/DuckUI/Package.swift b/LocalPackages/DuckUI/Package.swift index d449a97803..74c51b6864 100644 --- a/LocalPackages/DuckUI/Package.swift +++ b/LocalPackages/DuckUI/Package.swift @@ -31,7 +31,7 @@ let package = Package( targets: ["DuckUI"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.1"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.2"), ], targets: [ .target( diff --git a/LocalPackages/SyncUI/Package.swift b/LocalPackages/SyncUI/Package.swift index 18de7a87dd..b7c6f13b13 100644 --- a/LocalPackages/SyncUI/Package.swift +++ b/LocalPackages/SyncUI/Package.swift @@ -33,7 +33,7 @@ let package = Package( ], dependencies: [ .package(path: "../DuckUI"), - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.1"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.2"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ diff --git a/LocalPackages/Waitlist/Package.swift b/LocalPackages/Waitlist/Package.swift index 771c8cdaac..3988db42e0 100644 --- a/LocalPackages/Waitlist/Package.swift +++ b/LocalPackages/Waitlist/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["Waitlist", "WaitlistMocks"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.1"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.2"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ From fc8c0781af00f300c63ce24b78c976c50b3e18c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mariusz=20=C5=9Apiewak?= Date: Tue, 23 Jan 2024 13:45:13 +0100 Subject: [PATCH 22/70] Display short URL when not editing (#2357) --- DuckDuckGo.xcodeproj/project.pbxproj | 12 ++-- DuckDuckGo/AddressDisplayHelper.swift | 46 +++++++++++++ DuckDuckGo/MainViewController.swift | 5 ++ DuckDuckGo/OmniBar.swift | 39 ++--------- .../AddressDisplayHelperTests.swift | 61 ++++++++++++++++++ DuckDuckGoTests/OmniBarTests.swift | 64 ------------------- 6 files changed, 124 insertions(+), 103 deletions(-) create mode 100644 DuckDuckGo/AddressDisplayHelper.swift create mode 100644 DuckDuckGoTests/AddressDisplayHelperTests.swift delete mode 100644 DuckDuckGoTests/OmniBarTests.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index f6bbf59ad9..24acdb46c7 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -323,6 +323,7 @@ 56244C1D2A137B1900EDF259 /* WaitlistViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56244C1C2A137B1900EDF259 /* WaitlistViews.swift */; }; 6AC6DAB328804F97002723C0 /* BarsAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AC6DAB228804F97002723C0 /* BarsAnimator.swift */; }; 6AC98419288055C1005FA9CA /* BarsAnimatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AC98418288055C1005FA9CA /* BarsAnimatorTests.swift */; }; + 6FDA1FB32B59584400AC962A /* AddressDisplayHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FDA1FB22B59584400AC962A /* AddressDisplayHelper.swift */; }; 83004E802193BB8200DA013C /* WKNavigationExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83004E7F2193BB8200DA013C /* WKNavigationExtension.swift */; }; 83004E862193E5ED00DA013C /* TabViewControllerBrowsingMenuExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83004E852193E5ED00DA013C /* TabViewControllerBrowsingMenuExtension.swift */; }; 83004E882193E8C700DA013C /* TabViewControllerLongPressMenuExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83004E872193E8C700DA013C /* TabViewControllerLongPressMenuExtension.swift */; }; @@ -509,7 +510,7 @@ 85F0E97329952D7A003D5181 /* DuckDuckGo Recovery Document.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 85F0E97229952D7A003D5181 /* DuckDuckGo Recovery Document.pdf */; }; 85F200002215C17B006BB258 /* FindInPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85F2FFFF2215C17B006BB258 /* FindInPage.swift */; }; 85F200042216F5D8006BB258 /* FindInPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85F200032216F5D8006BB258 /* FindInPageView.swift */; }; - 85F200072217032E006BB258 /* OmniBarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85F20005221702F7006BB258 /* OmniBarTests.swift */; }; + 85F200072217032E006BB258 /* AddressDisplayHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85F20005221702F7006BB258 /* AddressDisplayHelperTests.swift */; }; 85F21DB0210F5E32002631A6 /* AtbIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85F21DAF210F5E32002631A6 /* AtbIntegrationTests.swift */; }; 85F21DC021123B03002631A6 /* Core.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F143C2E41E4A4CD400CFDE3A /* Core.framework */; }; 85F21DC621145DD5002631A6 /* global.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8512BCBF2061B6110085E862 /* global.swift */; }; @@ -1404,6 +1405,7 @@ 6AC6DAB228804F97002723C0 /* BarsAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarsAnimator.swift; sourceTree = ""; }; 6AC98418288055C1005FA9CA /* BarsAnimatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarsAnimatorTests.swift; sourceTree = ""; }; 6FB030C7234331B400A10DB9 /* Configuration.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Configuration.xcconfig; path = Configuration/Configuration.xcconfig; sourceTree = ""; }; + 6FDA1FB22B59584400AC962A /* AddressDisplayHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressDisplayHelper.swift; sourceTree = ""; }; 83004E7F2193BB8200DA013C /* WKNavigationExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKNavigationExtension.swift; sourceTree = ""; }; 83004E832193E14C00DA013C /* UIAlertControllerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = UIAlertControllerExtension.swift; path = ../Core/UIAlertControllerExtension.swift; sourceTree = ""; }; 83004E852193E5ED00DA013C /* TabViewControllerBrowsingMenuExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabViewControllerBrowsingMenuExtension.swift; sourceTree = ""; }; @@ -1595,7 +1597,7 @@ 85EE7F58224673C5000FE757 /* WebContainerNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebContainerNavigationController.swift; sourceTree = ""; }; 85F0E97229952D7A003D5181 /* DuckDuckGo Recovery Document.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = "DuckDuckGo Recovery Document.pdf"; sourceTree = ""; }; 85F200032216F5D8006BB258 /* FindInPageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindInPageView.swift; sourceTree = ""; }; - 85F20005221702F7006BB258 /* OmniBarTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OmniBarTests.swift; sourceTree = ""; }; + 85F20005221702F7006BB258 /* AddressDisplayHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressDisplayHelperTests.swift; sourceTree = ""; }; 85F21DAD210F5E32002631A6 /* AtbUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AtbUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 85F21DAF210F5E32002631A6 /* AtbIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AtbIntegrationTests.swift; sourceTree = ""; }; 85F21DB1210F5E32002631A6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -5355,6 +5357,7 @@ children = ( F114C55A1E66EB020018F95F /* NibLoading.swift */, F1C4A70D1E57725800A6CA1B /* OmniBar.swift */, + 6FDA1FB22B59584400AC962A /* AddressDisplayHelper.swift */, 98D16975250CE707009513CC /* OmniBar.xib */, F130D7391E5776C500C45811 /* OmniBarDelegate.swift */, F1D477C51F2126CC0031ED49 /* OmniBarState.swift */, @@ -5449,7 +5452,7 @@ isa = PBXGroup; children = ( 8588026424E4209900C24AB6 /* LargeOmniBarStateTests.swift */, - 85F20005221702F7006BB258 /* OmniBarTests.swift */, + 85F20005221702F7006BB258 /* AddressDisplayHelperTests.swift */, F1D477C81F2139410031ED49 /* SmallOmniBarStateTests.swift */, 1E8146A628C8AAF500D1AF63 /* PrivacyIconAndTrackers */, ); @@ -6879,6 +6882,7 @@ F1AE54E81F0425FC00D9A700 /* AuthenticationViewController.swift in Sources */, 020108AE29A7F91600644F9D /* AppTPTrackerCell.swift in Sources */, 983D71B12A286E810072E26D /* SyncDebugViewController.swift in Sources */, + 6FDA1FB32B59584400AC962A /* AddressDisplayHelper.swift in Sources */, F103073B1E7C91330059FEC7 /* BookmarksDataSource.swift in Sources */, EE0153E62A6FE106002A8B26 /* NetworkProtectionRootViewModel.swift in Sources */, 85864FBC24D31EF300E756FF /* SuggestionTrayViewController.swift in Sources */, @@ -7072,7 +7076,7 @@ 85480CB429226B3B007E8F13 /* CrashCollectionExtensionTests.swift in Sources */, 4B6484FC27FFD14F0050A7A1 /* WindowsBrowserWaitlistTests.swift in Sources */, 8540BD5223D8C2220057FDD2 /* PreserveLoginsTests.swift in Sources */, - 85F200072217032E006BB258 /* OmniBarTests.swift in Sources */, + 85F200072217032E006BB258 /* AddressDisplayHelperTests.swift in Sources */, B6AD9E3728D4510A0019CDE9 /* ContentBlockingUpdatingTests.swift in Sources */, C14882E427F20D9A00D59F0C /* BookmarksImporterTests.swift in Sources */, 8588026A24E424EE00C24AB6 /* AppWidthObserverTests.swift in Sources */, diff --git a/DuckDuckGo/AddressDisplayHelper.swift b/DuckDuckGo/AddressDisplayHelper.swift new file mode 100644 index 0000000000..16b508bdeb --- /dev/null +++ b/DuckDuckGo/AddressDisplayHelper.swift @@ -0,0 +1,46 @@ +// +// AddressDisplayHelper.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 + +extension OmniBar { + + struct AddressDisplayHelper { + + static func addressForDisplay(url: URL, showsFullURL: Bool) -> String { + guard !showsFullURL, let shortAddress = shortURLString(url) else { + return url.absoluteString + } + + return shortAddress + } + + /// Creates a string containing a short version the http(s) URL. + /// + /// - returns: URL's host without `www` component. `nil` if no host present or scheme does not match http(s). + static func shortURLString(_ url: URL) -> String? { + + guard !url.isCustomURLScheme() else { + return nil + } + + return url.host?.droppingWwwPrefix() + } + } +} diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 47528d7e39..b112492b87 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -1511,6 +1511,10 @@ extension MainViewController: OmniBarDelegate { } func onTextFieldWillBeginEditing(_ omniBar: OmniBar) { + if let currentTab { + viewCoordinator.omniBar.refreshText(forUrl: currentTab.url, forceFullURL: true) + } + guard homeController == nil else { return } if !skipSERPFlow, isSERPPresented, let query = omniBar.textField.text { @@ -1521,6 +1525,7 @@ extension MainViewController: OmniBarDelegate { } func onTextFieldDidBeginEditing(_ omniBar: OmniBar) -> Bool { + let selectQueryText = !(isSERPPresented && !skipSERPFlow) skipSERPFlow = false diff --git a/DuckDuckGo/OmniBar.swift b/DuckDuckGo/OmniBar.swift index 2d0cc8c45b..4a3bc54fd0 100644 --- a/DuckDuckGo/OmniBar.swift +++ b/DuckDuckGo/OmniBar.swift @@ -384,11 +384,7 @@ class OmniBar: UIView { omniDelegate?.onOmniQueryUpdated("") } - func refreshText(forUrl url: URL?) { - - if textField.isEditing { - return - } + func refreshText(forUrl url: URL?, forceFullURL: Bool = false) { guard let url = url else { textField.text = nil @@ -398,37 +394,10 @@ class OmniBar: UIView { if let query = url.searchQuery { textField.text = query } else { - textField.attributedText = OmniBar.demphasisePath(forUrl: url) + textField.text = AddressDisplayHelper.addressForDisplay(url: url, showsFullURL: textField.isEditing || forceFullURL) } } - public class func demphasisePath(forUrl url: URL) -> NSAttributedString? { - - let s = url.absoluteString - let attributedString = NSMutableAttributedString(string: s) - guard let c = URLComponents(url: url, resolvingAgainstBaseURL: true) else { - return attributedString - } - - let theme = ThemeManager.shared.currentTheme - - if let pathStart = c.rangeOfPath?.lowerBound { - let urlEnd = s.endIndex - - let pathRange = NSRange(pathStart ..< urlEnd, in: s) - attributedString.addAttribute(.foregroundColor, value: theme.searchBarTextDeemphasisColor, range: pathRange) - - let domainRange = NSRange(s.startIndex ..< pathStart, in: s) - attributedString.addAttribute(.foregroundColor, value: theme.searchBarTextColor, range: domainRange) - - } else { - let range = NSRange(s.startIndex ..< s.endIndex, in: s) - attributedString.addAttribute(.foregroundColor, value: theme.searchBarTextColor, range: range) - } - - return attributedString - } - @IBAction func onTextEntered(_ sender: Any) { onQuerySubmitted() } @@ -517,7 +486,7 @@ class OmniBar: UIView { super.layoutSubviews() NotificationCenter.default.post(name: OmniBar.didLayoutNotification, object: self) } - + } // swiftlint:enable type_body_length @@ -571,7 +540,7 @@ extension OmniBar: Themable { searchStackContainer?.tintColor = theme.barTintColor if let url = textField.text.flatMap({ URL(trimmedAddressBarString: $0.trimmingWhitespace()) }) { - textField.attributedText = OmniBar.demphasisePath(forUrl: url) + textField.text = AddressDisplayHelper.addressForDisplay(url: url, showsFullURL: textField.isEditing) } textField.textColor = theme.searchBarTextColor textField.tintColor = UIColor(designSystemColor: .accent) diff --git a/DuckDuckGoTests/AddressDisplayHelperTests.swift b/DuckDuckGoTests/AddressDisplayHelperTests.swift new file mode 100644 index 0000000000..c4122c4f20 --- /dev/null +++ b/DuckDuckGoTests/AddressDisplayHelperTests.swift @@ -0,0 +1,61 @@ +// +// AddressDisplayHelperTests.swift +// DuckDuckGo +// +// Copyright © 2019 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 + +class AddressDisplayHelperTests: XCTestCase { + + private typealias AddressHelper = OmniBar.AddressDisplayHelper + + func testShortURL() { + + XCTAssertEqual(AddressHelper.shortURLString(URL(string: "https://www.duckduckgo.com")!), "duckduckgo.com") + XCTAssertEqual(AddressHelper.shortURLString(URL(string: "https://www.duckduckgo.com/some/path")!), "duckduckgo.com") + XCTAssertEqual(AddressHelper.shortURLString(URL(string: "https://www.subdomain.duckduckgo.com/some/path")!), "subdomain.duckduckgo.com") + XCTAssertEqual(AddressHelper.shortURLString(URL(string: "https://m.duckduckgo.com/some/path")!), "m.duckduckgo.com") + XCTAssertEqual(AddressHelper.shortURLString(URL(string: "http://some-other.sub.domain.duck.eu/with/path")!), "some-other.sub.domain.duck.eu") + XCTAssertEqual(AddressHelper.shortURLString(URL(string: "http://duckduckgo.com:1234")!), "duckduckgo.com") + XCTAssertEqual(AddressHelper.shortURLString(URL(string: "https://192.168.0.1:1234")!), "192.168.0.1") + + XCTAssertEqual(AddressHelper.shortURLString(URL(string: "https://www.com")!), "com") // This is an exception we are ok with) + + XCTAssertNil(AddressHelper.shortURLString(URL(string: "file:///some/path")!)) + XCTAssertNil(AddressHelper.shortURLString(URL(string: "somescheme:///some/path")!)) + XCTAssertNil(AddressHelper.shortURLString(URL(string: "blob:https://www.my.com/111-222-333-444")!)) + XCTAssertNil(AddressHelper.shortURLString(URL(string: "data:text/plain;charset=UTF-8;page=21,the%20data:12345")!)) + } + + func testShortensURLWhenShortVersionExpected() { + let addressForDisplay = AddressHelper.addressForDisplay(url: URL(string: "http://some.domain.eu/with/path")!, showsFullURL: false) + + XCTAssertEqual(addressForDisplay, "some.domain.eu") + } + + func testDoesNotShortenURLWhenFullVersionExpected() { + let addressForDisplay = AddressHelper.addressForDisplay(url: URL(string: "http://some.domain.eu/with/path")!, showsFullURL: true) + + XCTAssertEqual(addressForDisplay, "http://some.domain.eu/with/path") + } + + func testFallsBackToLongURLWhenCannotProduceShortURL() { + let addressForDisplay = AddressHelper.addressForDisplay(url: URL(string: "file:///some/path")!, showsFullURL: false) + + XCTAssertEqual(addressForDisplay, "file:///some/path") + } +} diff --git a/DuckDuckGoTests/OmniBarTests.swift b/DuckDuckGoTests/OmniBarTests.swift deleted file mode 100644 index ea9fb56af6..0000000000 --- a/DuckDuckGoTests/OmniBarTests.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// OmniBarTests.swift -// DuckDuckGo -// -// Copyright © 2019 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 XCTest -@testable import DuckDuckGo - -class OmniBarTests: XCTestCase { - - func testDeemphasisePathDoesNotCrash() { - - _ = OmniBar.demphasisePath(forUrl: URL(string: "example.com")!) - _ = OmniBar.demphasisePath(forUrl: URL(string: "a/b")!) - - testWith(prefix: "http:///") // crashes but we don't allow it anyway - testWith(prefix: "http://localhost") - testWith(prefix: "http://localhost/") - testWith(prefix: "http://example.com") - testWith(prefix: "http://example.com/") - testWith(prefix: "http://example.com/path") - testWith(prefix: "http://example.com/path/") - testWith(prefix: "http://user:password@example.com/path/") - - testWith(prefix: "http://localhost:8080") - testWith(prefix: "http://localhost:8080/") - testWith(prefix: "http://example.com:8080") - testWith(prefix: "http://example.com:8080/") - testWith(prefix: "http://example.com:8080/path") - testWith(prefix: "http://example.com:8080/path/") - testWith(prefix: "http://user:password@example.com:8080/path/") - - } - - private func testWith(prefix: String) { - - _ = OmniBar.demphasisePath(forUrl: URL(string: prefix)!) - _ = OmniBar.demphasisePath(forUrl: URL(string: "\(prefix)#")!) - _ = OmniBar.demphasisePath(forUrl: URL(string: "\(prefix)#/fragment")!) - _ = OmniBar.demphasisePath(forUrl: URL(string: "\(prefix)?")!) - _ = OmniBar.demphasisePath(forUrl: URL(string: "\(prefix)?x=1")!) - _ = OmniBar.demphasisePath(forUrl: URL(string: "\(prefix)?x=1&")!) - _ = OmniBar.demphasisePath(forUrl: URL(string: "\(prefix)?x=1&y=1")!) - _ = OmniBar.demphasisePath(forUrl: URL(string: "\(prefix)?x=1&y=1,2")!) - - } - -} From 4218c393a6e82c8f823b7410cd1ed397aebbf666 Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Tue, 23 Jan 2024 07:22:55 -0800 Subject: [PATCH 23/70] Update nightly jobs to use iOS 17 simulators (#2369) Task/Issue URL: https://app.asana.com/0/414235014887631/1206407464978337/f Tech Design URL: CC: @graeme Description: This PR fixes nightly builds for iOS. --- .github/workflows/nightly.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index dd87372054..ae31bc6122 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -44,7 +44,7 @@ jobs: run: | set -o pipefail && xcodebuild test \ -scheme "AtbUITests" \ - -destination "platform=iOS Simulator,name=iPhone 14,OS=16.4" \ + -destination "platform=iOS Simulator,name=iPhone 15,OS=17.2" \ -derivedDataPath "DerivedData" \ -skipPackagePluginValidation \ | tee xcodebuild.log \ @@ -86,7 +86,7 @@ jobs: run: | set -o pipefail && xcodebuild test \ -scheme "FingerprintingUITests" \ - -destination "platform=iOS Simulator,name=iPhone 14,OS=16.4" \ + -destination "platform=iOS Simulator,name=iPhone 15,OS=17.2" \ -derivedDataPath "DerivedData" \ -skipPackagePluginValidation \ | xcbeautify --report junit --report-path . --junit-report-filename unittests.xml From 55445fb84d7be6b029571c0a0db70eef8a80dc68 Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Tue, 23 Jan 2024 08:45:19 -0800 Subject: [PATCH 24/70] Only add VPN intents to alpha build (#2360) Task/Issue URL: https://app.asana.com/0/414235014887631/1206395221805116/f Tech Design URL: CC: @graeme Description: This PR removes app intents from non-alpha builds. --- DuckDuckGo/VPNIntents.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DuckDuckGo/VPNIntents.swift b/DuckDuckGo/VPNIntents.swift index 716396607f..d82a7c1b86 100644 --- a/DuckDuckGo/VPNIntents.swift +++ b/DuckDuckGo/VPNIntents.swift @@ -17,6 +17,8 @@ // limitations under the License. // +#if ALPHA + import AppIntents import NetworkExtension import WidgetKit @@ -100,3 +102,5 @@ struct EnableVPNIntent: AppIntent { } } + +#endif From bcf8c700dda30c38535e779eb404e4e83b3dc3f5 Mon Sep 17 00:00:00 2001 From: Daniel Bernal Date: Wed, 24 Jan 2024 02:21:09 +0100 Subject: [PATCH 25/70] Subscription Email restore & Other flows (#2365) Task/Issue URL: https://app.asana.com/0/72649045549333/1205054784245717/f Description: Allows restoring a subscription using your email Allows adding email to a subscription Allows managing email from a subscription --- DuckDuckGo.xcodeproj/project.pbxproj | 14 +++- .../xcshareddata/swiftpm/Package.resolved | 2 +- .../AppStoreAccountManagementFlow.swift | 3 +- .../Flows/AppStore/AppStorePurchaseFlow.swift | 6 +- .../Subscription/Services/APIService.swift | 6 +- .../Subscription/Services/AuthService.swift | 12 ++- .../Services/SubscriptionService.swift | 10 +-- .../SubscriptionPurchaseEnvironment.swift | 13 ++- ...scriptionPagesUseSubscriptionFeature.swift | 9 ++- .../SubscriptionEmailViewModel.swift | 80 +++++++++++++++++++ .../SubscriptionFlowNavController.swift | 28 +++++++ .../ViewModel/SubscriptionFlowViewModel.swift | 6 +- .../SubscriptionRestoreViewModel.swift | 19 ++++- .../SubscriptionSettingsViewModel.swift | 3 +- .../Subscription/Views/HeadlessWebView.swift | 31 ++++++- .../Views/SubscriptionEmailView.swift | 57 +++++++++++++ .../Views/SubscriptionFlowView.swift | 10 ++- .../Views/SubscriptionRestoreView.swift | 77 +++++++++++++----- .../Views/SubscriptionSettingsView.swift | 21 ++--- DuckDuckGo/UserText.swift | 62 ++++++++++---- DuckDuckGo/en.lproj/Localizable.strings | 61 ++++++++++++-- submodules/privacy-reference-tests | 2 +- 22 files changed, 443 insertions(+), 89 deletions(-) create mode 100644 DuckDuckGo/Subscription/ViewModel/SubscriptionEmailViewModel.swift create mode 100644 DuckDuckGo/Subscription/ViewModel/SubscriptionFlowNavController.swift create mode 100644 DuckDuckGo/Subscription/Views/SubscriptionEmailView.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 24acdb46c7..91f0ebe1a5 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -776,6 +776,8 @@ CBEFB9142AE0844700DEDE7B /* CriticalAlerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEFB9102ADFFE7900DEDE7B /* CriticalAlerts.swift */; }; CBFCB30E2B2CD47800253E9E /* ConfigurationURLDebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBFCB30D2B2CD47800253E9E /* ConfigurationURLDebugViewController.swift */; }; D63657192A7BAE7C001AF19D /* EmailManagerRequestDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63657182A7BAE7C001AF19D /* EmailManagerRequestDelegate.swift */; }; + D64648AD2B59936B0033090B /* SubscriptionEmailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64648AC2B59936B0033090B /* SubscriptionEmailView.swift */; }; + D64648AF2B5993890033090B /* SubscriptionEmailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64648AE2B5993890033090B /* SubscriptionEmailViewModel.swift */; }; D652498E2B515A6A0056B0DE /* SubscriptionSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D652498D2B515A6A0056B0DE /* SubscriptionSettingsViewModel.swift */; }; D664C7B62B289AA200CBFA76 /* SubscriptionFlowViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664C7942B289AA000CBFA76 /* SubscriptionFlowViewModel.swift */; }; D664C7B72B289AA200CBFA76 /* Subscription.storekit in Resources */ = {isa = PBXBuildFile; fileRef = D664C7952B289AA000CBFA76 /* Subscription.storekit */; }; @@ -803,6 +805,7 @@ D6D12CAB2B291CAA0054390C /* APIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C9C2B291CA90054390C /* APIService.swift */; }; D6D12CAC2B291CAA0054390C /* AuthService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C9D2B291CA90054390C /* AuthService.swift */; }; D6D12CAD2B291CAA0054390C /* PurchaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C9E2B291CA90054390C /* PurchaseManager.swift */; }; + D6D4B77C2B5AE99500996546 /* SubscriptionFlowNavController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D4B77B2B5AE99500996546 /* SubscriptionFlowNavController.swift */; }; D6E83C122B1E6AB3006C8AFB /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E83C112B1E6AB3006C8AFB /* SettingsView.swift */; }; D6E83C2E2B1EA06E006C8AFB /* SettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E83C2D2B1EA06E006C8AFB /* SettingsViewModel.swift */; }; D6E83C312B1EA309006C8AFB /* SettingsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E83C302B1EA309006C8AFB /* SettingsCell.swift */; }; @@ -2426,6 +2429,8 @@ CBF14FC627970C8A001D94D0 /* HomeMessageCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeMessageCollectionViewCell.swift; sourceTree = ""; }; CBFCB30D2B2CD47800253E9E /* ConfigurationURLDebugViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationURLDebugViewController.swift; sourceTree = ""; }; D63657182A7BAE7C001AF19D /* EmailManagerRequestDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmailManagerRequestDelegate.swift; sourceTree = ""; }; + D64648AC2B59936B0033090B /* SubscriptionEmailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionEmailView.swift; sourceTree = ""; }; + D64648AE2B5993890033090B /* SubscriptionEmailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionEmailViewModel.swift; sourceTree = ""; }; D652498D2B515A6A0056B0DE /* SubscriptionSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionSettingsViewModel.swift; sourceTree = ""; }; D664C7942B289AA000CBFA76 /* SubscriptionFlowViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionFlowViewModel.swift; sourceTree = ""; }; D664C7952B289AA000CBFA76 /* Subscription.storekit */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Subscription.storekit; sourceTree = ""; }; @@ -2453,6 +2458,7 @@ D6D12C9C2B291CA90054390C /* APIService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = APIService.swift; sourceTree = ""; }; D6D12C9D2B291CA90054390C /* AuthService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthService.swift; sourceTree = ""; }; D6D12C9E2B291CA90054390C /* PurchaseManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PurchaseManager.swift; sourceTree = ""; }; + D6D4B77B2B5AE99500996546 /* SubscriptionFlowNavController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionFlowNavController.swift; sourceTree = ""; }; D6E83C112B1E6AB3006C8AFB /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; D6E83C2D2B1EA06E006C8AFB /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = ""; }; D6E83C302B1EA309006C8AFB /* SettingsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsCell.swift; sourceTree = ""; }; @@ -4556,9 +4562,11 @@ D664C7932B289AA000CBFA76 /* ViewModel */ = { isa = PBXGroup; children = ( + D6D4B77B2B5AE99500996546 /* SubscriptionFlowNavController.swift */, D664C7942B289AA000CBFA76 /* SubscriptionFlowViewModel.swift */, - D652498D2B515A6A0056B0DE /* SubscriptionSettingsViewModel.swift */, D68DF81D2B5830380023DBEA /* SubscriptionRestoreViewModel.swift */, + D64648AE2B5993890033090B /* SubscriptionEmailViewModel.swift */, + D652498D2B515A6A0056B0DE /* SubscriptionSettingsViewModel.swift */, ); path = ViewModel; sourceTree = ""; @@ -4579,6 +4587,7 @@ D664C7AE2B289AA000CBFA76 /* SubscriptionFlowView.swift */, D6F93E3D2B50A8A0004C268D /* SubscriptionSettingsView.swift */, D68DF81B2B58302E0023DBEA /* SubscriptionRestoreView.swift */, + D64648AC2B59936B0033090B /* SubscriptionEmailView.swift */, ); path = Views; sourceTree = ""; @@ -6525,6 +6534,7 @@ 1E162610296C5C630004127F /* CustomDaxDialogViewModel.swift in Sources */, 8590CB69268A4E190089F6BF /* DebugEtagStorage.swift in Sources */, D6D12CA62B291CAA0054390C /* AppStoreRestoreFlow.swift in Sources */, + D6D4B77C2B5AE99500996546 /* SubscriptionFlowNavController.swift in Sources */, C1CDA3162AFB9C7F006D1476 /* AutofillNeverPromptWebsitesManager.swift in Sources */, F1CA3C371F045878005FADB3 /* PrivacyStore.swift in Sources */, 37FCAAC029930E26000E420A /* FailedAssertionView.swift in Sources */, @@ -6623,6 +6633,7 @@ CB258D1329A4F24E00DEBA24 /* ConfigurationStore.swift in Sources */, 85058370219F424500ED4EDB /* SearchBarExtension.swift in Sources */, 310D09212799FD1A00DC0060 /* MIMEType.swift in Sources */, + D64648AD2B59936B0033090B /* SubscriptionEmailView.swift in Sources */, BD862E032B30DA170073E2EE /* VPNFeedbackFormViewModel.swift in Sources */, F4147354283BF834004AA7A5 /* AutofillContentScopeFeatureToggles.swift in Sources */, 986DA94A24884B18004A7E39 /* WebViewTransition.swift in Sources */, @@ -6886,6 +6897,7 @@ F103073B1E7C91330059FEC7 /* BookmarksDataSource.swift in Sources */, EE0153E62A6FE106002A8B26 /* NetworkProtectionRootViewModel.swift in Sources */, 85864FBC24D31EF300E756FF /* SuggestionTrayViewController.swift in Sources */, + D64648AF2B5993890033090B /* SubscriptionEmailViewModel.swift in Sources */, 1EF24235273BB9D200DE3D02 /* IntervalSlider.swift in Sources */, 027F48782A4B663C001A1C6C /* AppTPFAQView.swift in Sources */, D6E83C3D2B1F2C03006C8AFB /* SettingsLoginsView.swift in Sources */, diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 6c74085f32..d85be58c3b 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -156,7 +156,7 @@ { "identity" : "trackerradarkit", "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/TrackerRadarKit.git", + "location" : "https://github.com/duckduckgo/TrackerRadarKit", "state" : { "revision" : "a6b7ba151d9dc6684484f3785293875ec01cc1ff", "version" : "1.2.2" diff --git a/DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStoreAccountManagementFlow.swift b/DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStoreAccountManagementFlow.swift index a78faa029a..4c199acf20 100644 --- a/DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStoreAccountManagementFlow.swift +++ b/DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStoreAccountManagementFlow.swift @@ -38,8 +38,7 @@ public final class AppStoreAccountManagementFlow { if #available(macOS 12.0, iOS 15.0, *) { // In case of invalid token attempt store based authentication to obtain a new one guard let lastTransactionJWSRepresentation = await PurchaseManager.mostRecentTransaction() else { - return .failure(.noPastTransaction) - } + return .failure(.noPastTransaction) } switch await AuthService.storeLogin(signature: lastTransactionJWSRepresentation) { case .success(let response): diff --git a/DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStorePurchaseFlow.swift b/DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStorePurchaseFlow.swift index 3da3548c24..28a3c0cfdd 100644 --- a/DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStorePurchaseFlow.swift +++ b/DuckDuckGo/Subscription/Subscription/Flows/AppStore/AppStorePurchaseFlow.swift @@ -80,7 +80,9 @@ public final class AppStorePurchaseFlow { if case let .success(accessToken) = await accountManager.exchangeAuthTokenToAccessToken(response.authToken), case let .success(accountDetails) = await accountManager.fetchAccountDetails(with: accessToken) { accountManager.storeAuthToken(token: response.authToken) - accountManager.storeAccount(token: accessToken, email: accountDetails.email, externalID: accountDetails.externalID) + accountManager.storeAccount(token: accessToken, + email: accountDetails.email, + externalID: accountDetails.externalID) } case .failure: return .failure(.accountCreationFailed) @@ -104,7 +106,7 @@ public final class AppStorePurchaseFlow { @discardableResult public static func completeSubscriptionPurchase() async -> Result { - let result = await checkForEntitlements(wait: 2.0, retry: 30) + let result = await checkForEntitlements(wait: 2.0, retry: 10) return result ? .success(PurchaseUpdate(type: "completed")) : .failure(.missingEntitlements) } diff --git a/DuckDuckGo/Subscription/Subscription/Services/APIService.swift b/DuckDuckGo/Subscription/Subscription/Services/APIService.swift index 741f3163dd..b0d036a41f 100644 --- a/DuckDuckGo/Subscription/Subscription/Services/APIService.swift +++ b/DuckDuckGo/Subscription/Subscription/Services/APIService.swift @@ -33,7 +33,6 @@ struct ErrorResponse: Decodable { } public protocol APIService { - static var logger: OSLog { get } static var baseURL: URL { get } static var session: URLSession { get } static func executeAPICall(method: String, endpoint: String, headers: [String: String]?, body: Data?) async -> Result where T: Decodable @@ -64,7 +63,7 @@ public extension APIService { } } } catch { - os_log("Service error: %{public}@", log: .error, error.localizedDescription) + os_log(.error, log: .subscription, "Service error: %{public}@", error.localizedDescription) return .failure(.connectionError) } } @@ -94,7 +93,8 @@ public extension APIService { private static func printDebugInfo(method: String, endpoint: String, data: Data, response: URLResponse) { let statusCode = (response as? HTTPURLResponse)!.statusCode let stringData = String(data: data, encoding: .utf8) ?? "" - os_log("[%d] %{public}@ /%{public}@ :: %{public}@", log: logger, statusCode, method, endpoint, stringData) + + os_log(.info, log: .subscription, "[API] %d %{public}s /%{public}s :: %{private}s", statusCode, method, endpoint, stringData) } static func makeAuthorizationHeader(for token: String) -> [String: String] { diff --git a/DuckDuckGo/Subscription/Subscription/Services/AuthService.swift b/DuckDuckGo/Subscription/Subscription/Services/AuthService.swift index 3f1d3c4088..1e4b7730ac 100644 --- a/DuckDuckGo/Subscription/Subscription/Services/AuthService.swift +++ b/DuckDuckGo/Subscription/Subscription/Services/AuthService.swift @@ -22,7 +22,6 @@ import Common public struct AuthService: APIService { - public static let logger: OSLog = .authService public static let session = { let configuration = URLSessionConfiguration.ephemeral return URLSession(configuration: configuration) @@ -107,13 +106,12 @@ public struct AuthService: APIService { public let externalID: String public let id: Int public let status: String - - // swiftlint:disable:next nesting + + // swiftlint:disable nesting enum CodingKeys: String, CodingKey { - case authToken = "authToken", - email, externalID = "externalId", - id, - status // no underscores due to keyDecodingStrategy = .convertFromSnakeCase + // no underscores due to keyDecodingStrategy = .convertFromSnakeCase + case authToken = "authToken", email, externalID = "externalId", id, status } + // swiftlint:enable nesting } } diff --git a/DuckDuckGo/Subscription/Subscription/Services/SubscriptionService.swift b/DuckDuckGo/Subscription/Subscription/Services/SubscriptionService.swift index 347cec975a..ff048cba70 100644 --- a/DuckDuckGo/Subscription/Subscription/Services/SubscriptionService.swift +++ b/DuckDuckGo/Subscription/Subscription/Services/SubscriptionService.swift @@ -21,8 +21,7 @@ import Foundation import Common public struct SubscriptionService: APIService { - - public static let logger: OSLog = .subscriptionService + public static let session = { let configuration = URLSessionConfiguration.ephemeral return URLSession(configuration: configuration) @@ -32,9 +31,10 @@ public struct SubscriptionService: APIService { // MARK: - public static func getSubscriptionDetails(token: String) async -> Result { - let result: Result = await executeAPICall(method: "GET", - endpoint: "subscription", - headers: makeAuthorizationHeader(for: token)) + let result: Result = await executeAPICall(method: "GET", + endpoint: "subscription", + headers: makeAuthorizationHeader(for: token)) switch result { case .success(let response): diff --git a/DuckDuckGo/Subscription/Subscription/SubscriptionPurchaseEnvironment.swift b/DuckDuckGo/Subscription/Subscription/SubscriptionPurchaseEnvironment.swift index 0dca730071..f8847dafaf 100644 --- a/DuckDuckGo/Subscription/Subscription/SubscriptionPurchaseEnvironment.swift +++ b/DuckDuckGo/Subscription/Subscription/SubscriptionPurchaseEnvironment.swift @@ -18,15 +18,18 @@ // import Foundation +import Common public final class SubscriptionPurchaseEnvironment { - public enum Environment { + public enum Environment: String { case appStore, stripe } public static var current: Environment = .appStore { didSet { + os_log(.info, log: .subscription, "[SubscriptionPurchaseEnvironment] Setting to %@", current.rawValue) + canPurchase = false switch current { @@ -38,8 +41,12 @@ public final class SubscriptionPurchaseEnvironment { } } - public static var canPurchase: Bool = false - + public static var canPurchase: Bool = false { + didSet { + os_log(.info, log: .subscription, "[SubscriptionPurchaseEnvironment] canPurchase %@", (canPurchase ? "true" : "false")) + } + } + private static func setupForAppStore() { if #available(macOS 12.0, iOS 15.0, *) { Task { diff --git a/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift index ba40e5890e..a481a14598 100644 --- a/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift +++ b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift @@ -67,6 +67,7 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec @Published var hasActiveSubscription = false @Published var purchaseError: AppStorePurchaseFlow.Error? @Published var activateSubscription: Bool = false + @Published var emailActivationComplete: Bool = false var broker: UserScriptMessageBroker? @@ -141,7 +142,7 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec case .success(let subscriptionOptions): return subscriptionOptions case .failure: - + os_log(.info, log: .subscription, "Failed to obtain subscription options") return nil } @@ -206,6 +207,8 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec case let .success(accountDetails) = await accountManager.fetchAccountDetails(with: accessToken) { accountManager.storeAuthToken(token: authToken) accountManager.storeAccount(token: accessToken, email: accountDetails.email, externalID: accountDetails.externalID) + } else { + os_log(.info, log: .subscription, "Failed to obtain subscription options") } return nil @@ -216,8 +219,10 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec if let accessToken = accountManager.accessToken, case let .success(accountDetails) = await accountManager.fetchAccountDetails(with: accessToken) { accountManager.storeAccount(token: accessToken, email: accountDetails.email, externalID: accountDetails.externalID) + emailActivationComplete = true + } else { + os_log(.info, log: .subscription, "Failed to restore subscription from Email") } - return nil } diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionEmailViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionEmailViewModel.swift new file mode 100644 index 0000000000..7e883f8e06 --- /dev/null +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionEmailViewModel.swift @@ -0,0 +1,80 @@ +// +// SubscriptionEmailViewModel.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 UserScript +import Combine +import Core + +#if SUBSCRIPTION +@available(iOS 15.0, *) +final class SubscriptionEmailViewModel: ObservableObject { + + let accountManager: AccountManager + let userScript: SubscriptionPagesUserScript + let subFeature: SubscriptionPagesUseSubscriptionFeature + + var emailURL = URL.activateSubscriptionViaEmail + var viewTitle = UserText.subscriptionRestoreEmail + @Published var subscriptionEmail: String? + @Published var shouldReloadWebView = false + @Published var activateSubscription = false + @Published var managingSubscriptionEmail = false + + private var cancellables = Set() + + init(userScript: SubscriptionPagesUserScript, + subFeature: SubscriptionPagesUseSubscriptionFeature, + accountManager: AccountManager) { + self.userScript = userScript + self.subFeature = subFeature + self.accountManager = accountManager + initializeView() + setupTransactionObservers() + } + + private func initializeView() { + if accountManager.isUserAuthenticated { + // If user is authenticated, we want to "Add or manage email" instead of activating + emailURL = accountManager.email == nil ? URL.addEmailToSubscription : URL.manageSubscriptionEmail + viewTitle = accountManager.email == nil ? UserText.subscriptionRestoreAddEmailTitle : UserText.subscriptionManageEmailTitle + + // Also we assume subscription requires managing, and not activation + managingSubscriptionEmail = true + } + } + + private func setupTransactionObservers() { + subFeature.$emailActivationComplete + .receive(on: DispatchQueue.main) + .sink { [weak self] value in + if value { + self?.completeActivation() + } + } + .store(in: &cancellables) + } + + private func completeActivation() { + subFeature.emailActivationComplete = false + activateSubscription = true + } + +} +#endif diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowNavController.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowNavController.swift new file mode 100644 index 0000000000..ddd207de0d --- /dev/null +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowNavController.swift @@ -0,0 +1,28 @@ +// +// SubscriptionFlowNavController.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 + +class SubscriptionFlowNavController: ObservableObject { + @Published var shouldDisplayRestoreView: Bool = false { + didSet { + print("shouldDisplayRestoreView changed to: \(shouldDisplayRestoreView)") + } + } +} diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift index 6fee092c1c..d0638d4862 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift @@ -38,7 +38,7 @@ final class SubscriptionFlowViewModel: ObservableObject { var purchaseURL = URL.purchaseSubscription @Published var hasActiveSubscription = false @Published var transactionStatus: SubscriptionPagesUseSubscriptionFeature.TransactionStatus = .idle - @Published var shouldReloadWebview = false + @Published var shouldReloadWebView = false @Published var activatingSubscription = false init(userScript: SubscriptionPagesUserScript = SubscriptionPagesUserScript(), @@ -85,13 +85,13 @@ final class SubscriptionFlowViewModel: ObservableObject { func initializeViewData() async { await self.setupTransactionObserver() - await MainActor.run { shouldReloadWebview = true } + await MainActor.run { shouldReloadWebView = true } } func restoreAppstoreTransaction() { Task { if await subFeature.restoreAccountFromAppStorePurchase() { - await MainActor.run { shouldReloadWebview = true } + await MainActor.run { shouldReloadWebView = true } } else { await MainActor.run { } diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift index 8b76e2b9d3..85998e1bd5 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift @@ -29,6 +29,8 @@ final class SubscriptionRestoreViewModel: ObservableObject { let userScript: SubscriptionPagesUserScript let subFeature: SubscriptionPagesUseSubscriptionFeature let purchaseManager: PurchaseManager + let accountManager: AccountManager + let isAddingDevice: Bool enum SubscriptionActivationResult { case unknown, activated, notFound, error @@ -36,13 +38,24 @@ final class SubscriptionRestoreViewModel: ObservableObject { @Published var transactionStatus: SubscriptionPagesUseSubscriptionFeature.TransactionStatus = .idle @Published var activationResult: SubscriptionActivationResult = .unknown + @Published var subscriptionEmail: String? + @Published var isManagingEmailSubscription: Bool = false init(userScript: SubscriptionPagesUserScript = SubscriptionPagesUserScript(), subFeature: SubscriptionPagesUseSubscriptionFeature = SubscriptionPagesUseSubscriptionFeature(), - purchaseManager: PurchaseManager = PurchaseManager.shared) { + purchaseManager: PurchaseManager = PurchaseManager.shared, + accountManager: AccountManager = AccountManager(), + isAddingDevice: Bool = false) { self.userScript = userScript self.subFeature = subFeature self.purchaseManager = purchaseManager + self.accountManager = accountManager + self.isAddingDevice = isAddingDevice + initializeView() + } + + func initializeView() { + subscriptionEmail = accountManager.email } @MainActor @@ -64,5 +77,9 @@ final class SubscriptionRestoreViewModel: ObservableObject { } } + func manageEmailSubscription() { + isManagingEmailSubscription = true + } + } #endif diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift index 243e046534..d7f5d98351 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift @@ -25,8 +25,7 @@ import StoreKit @available(iOS 15.0, *) final class SubscriptionSettingsViewModel: ObservableObject { - private let accountManager: AccountManager - + let accountManager: AccountManager var subscriptionDetails: String = "" @Published var shouldDisplayRemovalNotice: Bool = false diff --git a/DuckDuckGo/Subscription/Views/HeadlessWebView.swift b/DuckDuckGo/Subscription/Views/HeadlessWebView.swift index 2b3fafaeee..e80e3f5bdc 100644 --- a/DuckDuckGo/Subscription/Views/HeadlessWebView.swift +++ b/DuckDuckGo/Subscription/Views/HeadlessWebView.swift @@ -44,6 +44,8 @@ struct HeadlessWebview: UIViewRepresentable { webView.load(URLRequest(url: url)) } + webView.uiDelegate = context.coordinator + #if DEBUG if #available(iOS 16.4, *) { @@ -80,8 +82,35 @@ struct HeadlessWebview: UIViewRepresentable { return userContentController } - class Coordinator: NSObject { + class Coordinator: NSObject, WKUIDelegate { var webView: WKWebView? + + private func topMostViewController() -> UIViewController? { + var topController: UIViewController? = UIApplication.shared.windows.filter { $0.isKeyWindow } + .first? + .rootViewController + while let presentedViewController = topController?.presentedViewController { + topController = presentedViewController + } + return topController + } + + // MARK: WKUIDelegate + + // Enables presenting Javascript alerts via the native layer (window.confirm()) + func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, + initiatedByFrame frame: WKFrameInfo, + completionHandler: @escaping (Bool) -> Void) { + let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: UserText.actionCancel, style: .cancel, handler: { _ in completionHandler(false) })) + alertController.addAction(UIAlertAction(title: UserText.actionOK, style: .default, handler: { _ in completionHandler(true) })) + + if let topController = topMostViewController() { + topController.present(alertController, animated: true, completion: nil) + } else { + completionHandler(false) + } + } } } diff --git a/DuckDuckGo/Subscription/Views/SubscriptionEmailView.swift b/DuckDuckGo/Subscription/Views/SubscriptionEmailView.swift new file mode 100644 index 0000000000..714e9696ea --- /dev/null +++ b/DuckDuckGo/Subscription/Views/SubscriptionEmailView.swift @@ -0,0 +1,57 @@ +// +// SubscriptionEmailView.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. +// + +#if SUBSCRIPTION +import SwiftUI +import Foundation + +@available(iOS 15.0, *) +struct SubscriptionEmailView: View { + + @ObservedObject var viewModel: SubscriptionEmailViewModel + @Binding var isActivatingSubscription: Bool + @Environment(\.dismiss) var dismiss + + var body: some View { + ZStack { + VStack { + AsyncHeadlessWebView(url: $viewModel.emailURL, + userScript: viewModel.userScript, + subFeature: viewModel.subFeature, + shouldReload: $viewModel.shouldReloadWebView).background() + } + } + .onChange(of: viewModel.activateSubscription) { active in + if active { + // We just need to dismiss the current view + if viewModel.managingSubscriptionEmail { + dismiss() + } else { + // Update the binding to tear down the entire view stack + // This dismisses all views in between and takes you back to the welcome page + isActivatingSubscription = false + } + } + } + .navigationTitle(viewModel.viewTitle) + } + + +} +#endif diff --git a/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift b/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift index 3649d27492..6538daedea 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift @@ -45,7 +45,7 @@ struct SubscriptionFlowView: View { AsyncHeadlessWebView(url: $viewModel.purchaseURL, userScript: viewModel.userScript, subFeature: viewModel.subFeature, - shouldReload: $viewModel.shouldReloadWebview).background() + shouldReload: $viewModel.shouldReloadWebView).background() // Overlay that appears when transaction is in progress if viewModel.transactionStatus != .idle { @@ -53,14 +53,15 @@ struct SubscriptionFlowView: View { } // Activation View - NavigationLink(destination: SubscriptionRestoreView(viewModel: SubscriptionRestoreViewModel()), + NavigationLink(destination: SubscriptionRestoreView(viewModel: SubscriptionRestoreViewModel(), + isActivatingSubscription: $viewModel.activatingSubscription), isActive: $viewModel.activatingSubscription) { EmptyView() } } - .onChange(of: viewModel.shouldReloadWebview) { shouldReload in + .onChange(of: viewModel.shouldReloadWebView) { shouldReload in if shouldReload { - viewModel.shouldReloadWebview = false + viewModel.shouldReloadWebView = false } } .onChange(of: viewModel.hasActiveSubscription) { result in @@ -68,6 +69,7 @@ struct SubscriptionFlowView: View { isAlertVisible = true } } + .onAppear(perform: { Task { await viewModel.initializeViewData() } }) diff --git a/DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift b/DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift index 618223a245..3006cf4ace 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift @@ -26,10 +26,13 @@ import DesignResourcesKit struct SubscriptionRestoreView: View { @Environment(\.dismiss) var dismiss - @ObservedObject var viewModel: SubscriptionRestoreViewModel + @StateObject var viewModel: SubscriptionRestoreViewModel @State private var expandedItemId: Int = 0 @State private var isAlertVisible = false + // Binding used to dismiss the entire stack (Go back to settings from several levels down) + @Binding var isActivatingSubscription: Bool + private enum Constants { static let heroImage = "SyncTurnOnSyncHero" static let appleIDIcon = "Platform-Apple-16" @@ -51,7 +54,7 @@ struct SubscriptionRestoreView: View { listView } .background(Color(designSystemColor: .container)) - .navigationTitle(UserText.subscriptionActivate) + .navigationTitle(viewModel.isAddingDevice ? UserText.subscriptionAddDeviceTitle : UserText.subscriptionActivate) .navigationBarBackButtonHidden(viewModel.transactionStatus != .idle) .applyInsetGroupedListStyle() .alert(isPresented: $isAlertVisible) { getAlert() } @@ -60,13 +63,25 @@ struct SubscriptionRestoreView: View { isAlertVisible = true } } + .onAppear { + viewModel.initializeView() + } if viewModel.transactionStatus != .idle { PurchaseInProgressView(status: getTransactionStatus()) } } - + // Activation View + NavigationLink(destination: SubscriptionEmailView( + viewModel: SubscriptionEmailViewModel( + userScript: viewModel.userScript, + subFeature: viewModel.subFeature, + accountManager: viewModel.accountManager), + isActivatingSubscription: $isActivatingSubscription), + isActive: $viewModel.isManagingEmailSubscription) { + EmptyView() + } } private var listItems: [ListItem] { @@ -74,15 +89,11 @@ struct SubscriptionRestoreView: View { .init(id: 0, content: getCellTitle(icon: Constants.appleIDIcon, text: UserText.subscriptionActivateAppleID), - expandedContent: getCellContent(description: UserText.subscriptionActivateAppleIDDescription, - buttonText: UserText.subscriptionRestoreAppleID, - buttonAction: viewModel.restoreAppstoreTransaction)), + expandedContent: getAppleIDCellContent(buttonAction: viewModel.restoreAppstoreTransaction)), .init(id: 1, content: getCellTitle(icon: Constants.emailIcon, text: UserText.subscriptionActivateEmail), - expandedContent: getCellContent(description: UserText.subscriptionActivateEmailDescription, - buttonText: UserText.subscriptionRestoreEmail, - buttonAction: {})) + expandedContent: getEmailCellContent(buttonAction: viewModel.manageEmailSubscription )) ] } @@ -97,17 +108,48 @@ struct SubscriptionRestoreView: View { ) } - private func getCellContent(description: String, buttonText: String, buttonAction: @escaping () -> Void) -> AnyView { + private func getAppleIDCellContent(buttonAction: @escaping () -> Void) -> AnyView { AnyView( VStack(alignment: .leading) { - Text(description) + Text(viewModel.isAddingDevice ? UserText.subscriptionAvailableInApple : UserText.subscriptionActivateAppleIDDescription) .daxSubheadRegular() .foregroundColor(Color(designSystemColor: .textSecondary)) - getCellButton(buttonText: buttonText, action: buttonAction) + if !viewModel.isAddingDevice { + getCellButton(buttonText: UserText.subscriptionActivateAppleIDButton, action: buttonAction) + } } ) } + private func getEmailCellContent(buttonAction: @escaping () -> Void) -> AnyView { + AnyView( + VStack(alignment: .leading) { + if viewModel.subscriptionEmail == nil { + Text(UserText.subscriptionActivateEmailDescription) + .daxSubheadRegular() + .foregroundColor(Color(designSystemColor: .textSecondary)) + getCellButton(buttonText: UserText.subscriptionRestoreEmail, + action: buttonAction) + } else { + Text(viewModel.subscriptionEmail ?? "").daxSubheadSemibold() + Text(UserText.subscriptionActivateEmailDescription) + .daxSubheadRegular() + .foregroundColor(Color(designSystemColor: .textSecondary)) + HStack { + getCellButton(buttonText: UserText.subscriptionManageEmailButton, + action: buttonAction) + /* TO BE IMPLEMENTED ?? + Spacer() + Button(action: {}, label: { + Text(UserText.subscriptionManageEmailResendInstructions).daxButton().daxBodyBold() + }) + */ + } + } + } + ) + } + private func getCellButton(buttonText: String, action: @escaping () -> Void) -> AnyView { AnyView( Button(action: action, label: { @@ -142,11 +184,11 @@ struct SubscriptionRestoreView: View { private var headerView: some View { VStack(spacing: Constants.headerLineSpacing) { Image(Constants.heroImage) - Text(UserText.subscriptionActivateTitle) + Text(viewModel.isAddingDevice ? UserText.subscriptionAddDeviceHeaderTitle : UserText.subscriptionActivateTitle) .daxHeadline() .multilineTextAlignment(.center) .foregroundColor(Color(designSystemColor: .textPrimary)) - Text(UserText.subscriptionActivateDescription) + Text(viewModel.isAddingDevice ? UserText.subscriptionAddDeviceDescription : UserText.subscriptionActivateDescription) .daxFootnoteRegular() .foregroundColor(Color(designSystemColor: .textSecondary)) .multilineTextAlignment(.center) @@ -207,11 +249,4 @@ struct SubscriptionRestoreView: View { let expandedContent: AnyView } } - -@available(iOS 15.0, *) -struct SubscriptionRestoreView_Previews: PreviewProvider { - static var previews: some View { - SubscriptionRestoreView(viewModel: SubscriptionRestoreViewModel()) - } -} #endif diff --git a/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift b/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift index c10bd87f44..8e44ce451c 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift @@ -32,6 +32,7 @@ struct SubscriptionSettingsView: View { @ObservedObject var viewModel: SubscriptionSettingsViewModel @Environment(\.presentationMode) var presentationMode @StateObject var sceneEnvironment = SceneEnvironment() + @State private var isActivatingSubscription = false var body: some View { List { @@ -44,12 +45,16 @@ struct SubscriptionSettingsView: View { .hidden() }.textCase(nil) Section(header: Text(UserText.subscriptionManageDevices)) { - SettingsCustomCell(content: { - Text(UserText.subscriptionAddDevice) - .daxBodyRegular() - .foregroundColor(Color.init(designSystemColor: .accent)) - }, - action: {}) + + NavigationLink(destination: SubscriptionRestoreView( + viewModel: SubscriptionRestoreViewModel(isAddingDevice: true), + isActivatingSubscription: $isActivatingSubscription)) { + SettingsCustomCell(content: { + Text(UserText.subscriptionAddDeviceButton) + .daxBodyRegular() + .foregroundColor(Color.init(designSystemColor: .accent)) + }) + } SettingsCustomCell(content: { Text(UserText.subscriptionRemoveFromDevice) @@ -73,9 +78,7 @@ struct SubscriptionSettingsView: View { SettingsCustomCell(content: { Text(UserText.subscriptionFAQ) .daxBodyRegular() - }, - action: {}, - disclosureIndicator: false) + }) } } } diff --git a/DuckDuckGo/UserText.swift b/DuckDuckGo/UserText.swift index 6f1b1a32be..cd5f49ad45 100644 --- a/DuckDuckGo/UserText.swift +++ b/DuckDuckGo/UserText.swift @@ -65,6 +65,7 @@ public struct UserText { public static let actionRemoveFavorite = NSLocalizedString("action.title.remove.favorite", value: "Remove Favorite", comment: "Remove Favorite action") public static let actionManageFavorites = NSLocalizedString("action.manage.favorites", value: "Manage", comment: "Button label for managing favorites") + public static let actionOK = NSLocalizedString("action.ok", value: "OK", comment: "Button label for OK action") public static let voiceoverSuggestionTypeWebsite = NSLocalizedString("voiceover.suggestion.type.website", value: "Open website", comment: "Open suggested website action accessibility title") public static let voiceoverSuggestionTypeBookmark = NSLocalizedString("voiceover.suggestion.type.bookmark", value: "Bookmark", comment: "Voice-over title for a Bookmark suggestion. Noun") @@ -990,7 +991,7 @@ But if you *do* want a peek under the hood, you can find more information about public static let settingsAutolock = NSLocalizedString("settings.autolock", value: "Application Lock", comment: "Settings screen cell text for Application Lock") public static let settingsAutoLockDescription = NSLocalizedString("settings.autolock.description", value: "If Touch ID, Face ID or a system passcode is set, you’ll be requested to unlock the app when opening.", comment: "Section footer Autolock description") - // Privacy Pro Section + // Subscription Section public static let settingsPProSection = NSLocalizedString("settings.ppro", value: "Privacy Pro", comment: "Product name for the subscription bundle") public static let settingsPProSubscribe = NSLocalizedString("settings.subscription.subscribe", value: "Subscribe to Privacy Pro", comment: "Call to action title for Privacy Pro") public static let settingsPProDescription = NSLocalizedString("settings.subscription.description", value:"More seamless privacy with three new protections, including:", comment: "Privacy pro description subtext") @@ -1010,8 +1011,7 @@ But if you *do* want a peek under the hood, you can find more information about public static let settingsPProDBPSubTitle = NSLocalizedString("settings.subscription.DBP.subtitle", value: "Remove your info from sites that sell it", comment: "Data Broker protection cell subtitle for privacy pro") public static let settingsPProITRTitle = NSLocalizedString("settings.subscription.ITR.title", value: "Identity Theft Restoration", comment: "Identity theft restoration cell title for privacy pro") public static let settingsPProITRSubTitle = NSLocalizedString("settings.subscription.ITR.subtitle", value: "If your identity is stolen, we'll help restore it", comment: "Identity theft restoration cell subtitle for privacy pro") - - + // Customize Section public static let settingsCustomizeSection = NSLocalizedString("settings.customize", value: "Customize", comment: "Settings title for the customize section") public static let settingsKeyboard = NSLocalizedString("settings.keyboard", value: "Keyboard", comment: "Settings screen cell for Keyboard") @@ -1032,44 +1032,78 @@ But if you *do* want a peek under the hood, you can find more information about public static let settingsVersion = NSLocalizedString("settings.version", value: "Version", comment: "Settings cell for Version") public static let settingsFeedback = NSLocalizedString("settings.feedback", value: "Share Feedback", comment: "Settings cell for Feedback") - // Subscriptions + // MARK: Subscriptions + // Loaders static let subscriptionPurchasingTitle = NSLocalizedString("subscription.progress.view.purchasing.subscription", value: "Purchase in progress...", comment: "Progress view title when starting the purchase") static let subscriptionRestoringTitle = NSLocalizedString("subscription.progress.view.restoring.subscription", value: "Restoring subscription...", comment: "Progress view title when restoring past subscription purchase") static let subscriptionCompletingPurchaseTitle = NSLocalizedString("subscription.progress.view.completing.purchase", value: "Completing purchase...", comment: "Progress view title when completing the purchase") + + // Subscription Settings static func subscriptionInfo(expiration: String) -> String { let localized = NSLocalizedString("subscription.subscription.active.caption", value: "Your Privacy Pro subscription renews on %@", comment: "Subscription Expiration Data") return String(format: localized, expiration) } - public static let subscriptionManageDevices = NSLocalizedString("subscription.manage.device", value: "Manage Devices", comment: "Header for the device management section") - public static let subscriptionAddDevice = NSLocalizedString("subscription.add.device", value: "Add to Another Device", comment: "Add to another device button") - public static let subscriptionRemoveFromDevice = NSLocalizedString("subscription.remove.from.device", value: "Remove From This Device", comment: "Remove from this device button") + public static let subscriptionManageDevices = NSLocalizedString("subscription.manage.devices", value: "Manage Devices", comment: "Header for the device management section") + public static let subscriptionAddDeviceButton = NSLocalizedString("subscription.add.device.button", value: "Add to Another Device", comment: "Add to another device button") + public static let subscriptionRemoveFromDevice = NSLocalizedString("subscription.remove.from.device.button", value: "Remove From This Device", comment: "Remove from this device button") public static let subscriptionManagePlan = NSLocalizedString("subscription.manage.plan", value: "Manage Plan", comment: "Manage Plan header") public static let subscriptionChangePlan = NSLocalizedString("subscription.change.plan", value: "Change Plan Or Billing", comment: "Change plan or billing title") public static let subscriptionHelpAndSupport = NSLocalizedString("subscription.help", value: "Help and support", comment: "Help and support Section header") public static let subscriptionFAQ = NSLocalizedString("subscription.faq", value: "Privacy Pro FAQ", comment: "FAQ Button") public static let subscriptionFAQFooter = NSLocalizedString("subscription.faq.description", value: "Visit our Privacy Pro help pages for answers to frequently asked questions", comment: "FAQ Description") + + // Remove subscription confirmation public static let subscriptionRemoveFromDeviceConfirmTitle = NSLocalizedString("subscription.remove.from.device.title", value: "Remove From This Device?", comment: "Remove from device confirmation dialog title") public static let subscriptionRemoveFromDeviceConfirmText = NSLocalizedString("subscription.remove.from.device.text", value: "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices.", comment: "Remove from device confirmation dialog text") public static let subscriptionRemove = NSLocalizedString("subscription.remove.subscription", value: "Remove Subscription", comment: "Remove subscription button text") public static let subscriptionRemoveCancel = NSLocalizedString("subscription.remove.subscription.cancel", value: "Cancel", comment: "Remove subscription cancel button text") - public static let subscriptionFoundTitle = NSLocalizedString("subscription.found.tite", value: "Subscription Found", comment: "Title for the existing subscription dialog") - public static let subscriptionFoundText = NSLocalizedString("subscription.found.text", value: "We found a subscription associated with this Apple ID.", comment: "Message for the existing subscription dialog") - public static let subscriptionFoundCancel = NSLocalizedString("subscription.found.cancel", value: "Cancel", comment: "Cancel action for the existing subscription dialog") - public static let subscriptionFoundRestore = NSLocalizedString("subscription.found.restore", value: "Restore", comment: "Restore action for the existing subscription dialog") + + // Subscription Restore + public static let subscriptionActivate = NSLocalizedString("subscription.activate", value: "Activate Subscription", comment: "Subscription Activation Window Title") public static let subscriptionActivateTitle = NSLocalizedString("subscription.activate.title", value: "Activate your subscription on this device", comment: "Subscription Activation Title") public static let subscriptionActivateDescription = NSLocalizedString("subscription.activate.description", value: "Access your Privacy Pro subscription on this device via Apple ID or an email address.", comment: "Subscription Activation Info") - public static let subscriptionActivate = NSLocalizedString("subscription.activate", value: "Activate Subscription.", comment: "Subscription Activation Window Title") public static let subscriptionActivateAppleID = NSLocalizedString("subscription.activate.appleid", value: "Apple ID", comment: "Apple ID option for activation") + public static let subscriptionActivateAppleIDButton = NSLocalizedString("subscription.activate.appleid.button", value: "Restore Purchase", comment: "Button text for restoring purchase via Apple ID") public static let subscriptionActivateAppleIDDescription = NSLocalizedString("subscription.activate.appleid.description", value: "Restore your purchase to activate your subscription on this device.", comment: "Description for Apple ID activation") public static let subscriptionRestoreAppleID = NSLocalizedString("subscription.activate.restore.apple", value: "Restore", comment: "Restore button title for AppleID") public static let subscriptionActivateEmail = NSLocalizedString("subscription.activate.email", value: "Email", comment: "Email option for activation") - public static let subscriptionActivateEmailDescription = NSLocalizedString("subscription.activate.appleid.description", value: "Use your email to activate your subscription on this device.", comment: "Description for Email activation") + public static let subscriptionActivateEmailDescription = NSLocalizedString("subscription.activate.email.description", value: "Use your email to activate your subscription on this device.", comment: "Description for Email activation") public static let subscriptionRestoreEmail = NSLocalizedString("subscription.activate.restore.email", value: "Enter Email", comment: "Restore button title for Email") + + // Add to other devices (AppleID / Email) + public static let subscriptionAddDeviceTitle = NSLocalizedString("subscription.add.device.title", value: "Add Device", comment: "Add to another device view title") + public static let subscriptionAddDeviceHeaderTitle = NSLocalizedString("subscription.add.device.header.title", value: "Use your subscription on all your devices", comment: "Add subscription to other device title ") + public static let subscriptionAddDeviceDescription = NSLocalizedString("subscription.add.device.description", value: "Access your Privacy Pro subscription on any of your devices via Apple ID or by adding an email address.", comment: "Subscription Add device Info") + public static let subscriptionAvailableInApple = NSLocalizedString("subscription.available.apple", value: "Privacy Pro is available on any device signed in to the same Apple ID.", comment: "Subscription availability message on Apple devices") + public static let subscriptionManageEmailResendInstructions = NSLocalizedString("subscription.add.device.resend.instructions", value: "Resend Instructions", comment: "Resend activation instructions button") + + + // Add Email To subscription + public static let subscriptionAddEmail = NSLocalizedString("subscription.add.email", value: "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription.", comment: "Add email to an existing subscription") + public static let subscriptionRestoreAddEmailButton = NSLocalizedString("subscription.add.email.button", value: "Add Email", comment: "Button title for adding email to subscription") + public static let subscriptionRestoreAddEmailTitle = NSLocalizedString("subscription.add.email.title", value: "Add Email", comment: "View title for adding email to subscription") + + // Manage Subscription Email + public static let subscriptionManageEmailDescription = NSLocalizedString("subscription.manage.email.description", value: "You can use this email to activate your subscription on your other devices.", comment: "Description for Email Management options") + public static let subscriptionManageEmailButton = NSLocalizedString("subscription.activate.manage.email.button", value: "Manage", comment: "Restore button title for Managing Email") + public static let subscriptionManageEmailTitle = NSLocalizedString("subscription.activate.manage.email.title", value: "Manage Email", comment: "View Title for managing your email account") + public static let subscriptionManageEmailCancelButton = NSLocalizedString("subscription.activate.manage.email.cancel", value: "Cancel", comment: "Button title for cancelling email deletion") + public static let subscriptionManageEmailOKButton = NSLocalizedString("subscription.activate.manage.email.OK", value: "OK", comment: "Button title for confirming email deletion") + + + + // Subscribe & Restore Flow + public static let subscriptionFoundTitle = NSLocalizedString("subscription.found.title", value: "Subscription Found", comment: "Title for the existing subscription dialog") + public static let subscriptionFoundText = NSLocalizedString("subscription.found.text", value: "We found a subscription associated with this Apple ID.", comment: "Message for the existing subscription dialog") + public static let subscriptionFoundCancel = NSLocalizedString("subscription.found.cancel", value: "Cancel", comment: "Cancel action for the existing subscription dialog") + public static let subscriptionFoundRestore = NSLocalizedString("subscription.found.restore", value: "Restore", comment: "Restore action for the existing subscription dialog") public static let subscriptionRestoreNotFoundTitle = NSLocalizedString("subscription.notFound.alert.title", value: "Subscription Not Found", comment: "Alert title for not found subscription") public static let subscriptionRestoreNotFoundMessage = NSLocalizedString("subscription.notFound.alert.message", value: "The subscription associated with this Apple ID is no longer active.", comment: "Alert content for not found subscription") public static let subscriptionRestoreNotFoundPlans = NSLocalizedString("subscription.notFound.view.plans", value: "View Plans", comment: "View plans button text") public static let subscriptionRestoreSuccessfulTitle = NSLocalizedString("subscription.restore.success.alert.title", value: "You’re all set.", comment: "Alert title for restored purchase") public static let subscriptionRestoreSuccessfulMessage = NSLocalizedString("subscription.restore.success.alert.message", value: "Your purchases have been restored.", comment: "Alert message for restored purchase") - public static let subscriptionRestoreSuccessfulButton = NSLocalizedString("subscription.restore.success.alert.button", value: "OK", comment: "Alert button text for restored purchase alert") + public static let subscriptionRestoreSuccessfulButton = NSLocalizedString("subscription.restore.success.alert.button", value: "OK", comment: "Alert button text for restored purchase alert") + + } diff --git a/DuckDuckGo/en.lproj/Localizable.strings b/DuckDuckGo/en.lproj/Localizable.strings index 3f4fec34a1..f50749e44b 100644 --- a/DuckDuckGo/en.lproj/Localizable.strings +++ b/DuckDuckGo/en.lproj/Localizable.strings @@ -10,6 +10,9 @@ /* Button label for managing favorites */ "action.manage.favorites" = "Manage"; +/* Button label for OK action */ +"action.ok" = "OK"; + /* Add action - button shown in alert */ "action.title.add" = "Add"; @@ -1915,13 +1918,15 @@ But if you *do* want a peek under the hood, you can find more information about "siteFeedback.urlPlaceholder" = "Which website is broken?"; /* Subscription Activation Window Title */ -"subscription.activate" = "Activate Subscription."; +"subscription.activate" = "Activate Subscription"; /* Apple ID option for activation */ "subscription.activate.appleid" = "Apple ID"; -/* Description for Apple ID activation - Description for Email activation */ +/* Button text for restoring purchase via Apple ID */ +"subscription.activate.appleid.button" = "Restore Purchase"; + +/* Description for Apple ID activation */ "subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; /* Subscription Activation Info */ @@ -1930,6 +1935,21 @@ But if you *do* want a peek under the hood, you can find more information about /* Email option for activation */ "subscription.activate.email" = "Email"; +/* Description for Email activation */ +"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; + +/* Restore button title for Managing Email */ +"subscription.activate.manage.email.button" = "Manage"; + +/* Button title for cancelling email deletion */ +"subscription.activate.manage.email.cancel" = "Cancel"; + +/* Button title for confirming email deletion */ +"subscription.activate.manage.email.OK" = "OK"; + +/* View Title for managing your email account */ +"subscription.activate.manage.email.title" = "Manage Email"; + /* Restore button title for AppleID */ "subscription.activate.restore.apple" = "Restore"; @@ -1940,7 +1960,31 @@ But if you *do* want a peek under the hood, you can find more information about "subscription.activate.title" = "Activate your subscription on this device"; /* Add to another device button */ -"subscription.add.device" = "Add to Another Device"; +"subscription.add.device.button" = "Add to Another Device"; + +/* Subscription Add device Info */ +"subscription.add.device.description" = "Access your Privacy Pro subscription on any of your devices via Apple ID or by adding an email address."; + +/* Add subscription to other device title */ +"subscription.add.device.header.title" = "Use your subscription on all your devices"; + +/* Resend activation instructions button */ +"subscription.add.device.resend.instructions" = "Resend Instructions"; + +/* Add to another device view title */ +"subscription.add.device.title" = "Add Device"; + +/* Add email to an existing subscription */ +"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; + +/* Button title for adding email to subscription */ +"subscription.add.email.button" = "Add Email"; + +/* View title for adding email to subscription */ +"subscription.add.email.title" = "Add Email"; + +/* Subscription availability message on Apple devices */ +"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; /* Change plan or billing title */ "subscription.change.plan" = "Change Plan Or Billing"; @@ -1961,13 +2005,16 @@ But if you *do* want a peek under the hood, you can find more information about "subscription.found.text" = "We found a subscription associated with this Apple ID."; /* Title for the existing subscription dialog */ -"subscription.found.tite" = "Subscription Found"; +"subscription.found.title" = "Subscription Found"; /* Help and support Section header */ "subscription.help" = "Help and support"; /* Header for the device management section */ -"subscription.manage.device" = "Manage Devices"; +"subscription.manage.devices" = "Manage Devices"; + +/* Description for Email Management options */ +"subscription.manage.email.description" = "You can use this email to activate your subscription on your other devices."; /* Manage Plan header */ "subscription.manage.plan" = "Manage Plan"; @@ -1991,7 +2038,7 @@ But if you *do* want a peek under the hood, you can find more information about "subscription.progress.view.restoring.subscription" = "Restoring subscription..."; /* Remove from this device button */ -"subscription.remove.from.device" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Remove From This Device"; /* Remove from device confirmation dialog text */ "subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; diff --git a/submodules/privacy-reference-tests b/submodules/privacy-reference-tests index 6b7ad1e7f1..a3acc21947 160000 --- a/submodules/privacy-reference-tests +++ b/submodules/privacy-reference-tests @@ -1 +1 @@ -Subproject commit 6b7ad1e7f15270f9dfeb58a272199f4d57c3eb22 +Subproject commit a3acc2194758bec0f01f57dd0c5f106de01a354e From a747ca8961ce96efbaf1b6e3823537a07d904d2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mariusz=20=C5=9Apiewak?= Date: Wed, 24 Jan 2024 12:11:12 +0100 Subject: [PATCH 26/70] Update tagging instructions in PR template (#2364) --- .github/PULL_REQUEST_TEMPLATE.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 2560570f5d..00f6595286 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -11,11 +11,7 @@ CC: **Description**: **Steps to test this PR**: From defadca27db2882dde7ff850ed6f91347151a771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacek=20=C5=81yp?= Date: Wed, 24 Jan 2024 17:29:12 +0100 Subject: [PATCH 27/70] Cleanup old experiments (#2377) Task/Issue URL: https://app.asana.com/0/414235014887631/1206404176623188/f Description: Cleanup old experiments --- Core/DefaultVariantManager.swift | 4 ---- Core/UserAgentManager.swift | 6 ------ 2 files changed, 10 deletions(-) diff --git a/Core/DefaultVariantManager.swift b/Core/DefaultVariantManager.swift index 158b52d99d..29b3d5b46f 100644 --- a/Core/DefaultVariantManager.swift +++ b/Core/DefaultVariantManager.swift @@ -62,10 +62,6 @@ public struct VariantIOS: Variant { VariantIOS(name: "sc", weight: doNotAllocate, isIncluded: When.always, features: []), VariantIOS(name: "sd", weight: doNotAllocate, isIncluded: When.always, features: []), VariantIOS(name: "se", weight: doNotAllocate, isIncluded: When.always, features: []), - VariantIOS(name: "me", weight: doNotAllocate, isIncluded: When.always, features: []), - VariantIOS(name: "mf", weight: doNotAllocate, isIncluded: When.always, features: []), - VariantIOS(name: "mg", weight: 1, isIncluded: When.always, features: [.fixedUserAgent]), - VariantIOS(name: "mh", weight: 1, isIncluded: When.always, features: [.closestUserAgent]), returningUser ] diff --git a/Core/UserAgentManager.swift b/Core/UserAgentManager.swift index 52fb02b2e4..80db1e64a0 100644 --- a/Core/UserAgentManager.swift +++ b/Core/UserAgentManager.swift @@ -217,12 +217,6 @@ struct UserAgent { return ddgFixedLogic(forUrl: url, isDesktop: isDesktop, privacyConfig: privacyConfig) } - if DefaultVariantManager().isSupported(feature: .fixedUserAgent) { - return ddgFixedLogic(forUrl: url, isDesktop: isDesktop, privacyConfig: privacyConfig) - } else if DefaultVariantManager().isSupported(feature: .closestUserAgent) { - return closestLogic(forUrl: url, isDesktop: isDesktop, privacyConfig: privacyConfig) - } - switch defaultPolicy(forConfig: privacyConfig) { case .ddg: return oldLogic(forUrl: url, isDesktop: isDesktop, privacyConfig: privacyConfig) case .ddgFixed: return ddgFixedLogic(forUrl: url, isDesktop: isDesktop, privacyConfig: privacyConfig) From 2061bde87ec0b28768e9caf4e5cfa6be2e90ea62 Mon Sep 17 00:00:00 2001 From: Graeme Arthur Date: Wed, 24 Jan 2024 17:29:24 +0100 Subject: [PATCH 28/70] Link documentation in e2e failure task description (#2375) --- .github/workflows/end-to-end.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/end-to-end.yml b/.github/workflows/end-to-end.yml index 2f31dc7bf3..c0a2e39498 100644 --- a/.github/workflows/end-to-end.yml +++ b/.github/workflows/end-to-end.yml @@ -80,7 +80,7 @@ jobs: --header "Accept: application/json" \ --header "Authorization: Bearer ${{ secrets.ASANA_ACCESS_TOKEN }}" \ --header "Content-Type: application/json" \ - --data ' { "data": { "name": "GH Workflow Failure - End to end tests", "workspace": "${{ vars.GH_ASANA_WORKSPACE_ID }}", "projects": [ "${{ vars.GH_ASANA_IOS_APP_PROJECT_ID }}" ], "notes" : "The end to end workflow has failed. See https://github.com/duckduckgo/iOS/actions/runs/${{ github.run_id }}" } }' + --data ' { "data": { "name": "GH Workflow Failure - End to end tests", "workspace": "${{ vars.GH_ASANA_WORKSPACE_ID }}", "projects": [ "${{ vars.GH_ASANA_IOS_APP_PROJECT_ID }}" ], "notes" : "The end to end workflow has failed. See https://github.com/duckduckgo/iOS/actions/runs/${{ github.run_id }}. For instructions on how to handle the failure(s), check https://app.asana.com/0/0/1206423571874502/f" } }' - name: Upload logs when workflow failed uses: actions/upload-artifact@v4 From 4a3c10efce81f0148ad75266e47838b616b71152 Mon Sep 17 00:00:00 2001 From: Daniel Bernal Date: Wed, 24 Jan 2024 18:11:57 +0100 Subject: [PATCH 29/70] Implements Welcome Links Navigation in Subscriptions (#2371) Task/Issue URL: https://app.asana.com/0/1200019156869587/1205017548855433/f Description: Implements onAppear navigation for Settings, so you can navigate to a view programmatically when the view is shown Implements navigation from the Subscription Welcome page to each feature --- DuckDuckGo.xcodeproj/project.pbxproj | 4 -- DuckDuckGo/SettingsState.swift | 2 +- DuckDuckGo/SettingsSubscriptionView.swift | 12 ++--- DuckDuckGo/SettingsViewModel.swift | 44 ++++++++++++++++--- ...scriptionPagesUseSubscriptionFeature.swift | 10 +++-- .../SubscriptionFlowNavController.swift | 28 ------------ .../ViewModel/SubscriptionFlowViewModel.swift | 38 +++++++++++++++- .../SubscriptionRestoreViewModel.swift | 5 ++- .../SubscriptionSettingsViewModel.swift | 1 + .../Subscription/Views/HeadlessWebView.swift | 6 +-- .../Views/SubscriptionFlowView.swift | 6 +++ DuckDuckGo/UserText.swift | 6 +-- DuckDuckGo/en.lproj/Localizable.strings | 3 ++ 13 files changed, 107 insertions(+), 58 deletions(-) delete mode 100644 DuckDuckGo/Subscription/ViewModel/SubscriptionFlowNavController.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 91f0ebe1a5..69bb109f32 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -805,7 +805,6 @@ D6D12CAB2B291CAA0054390C /* APIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C9C2B291CA90054390C /* APIService.swift */; }; D6D12CAC2B291CAA0054390C /* AuthService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C9D2B291CA90054390C /* AuthService.swift */; }; D6D12CAD2B291CAA0054390C /* PurchaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D12C9E2B291CA90054390C /* PurchaseManager.swift */; }; - D6D4B77C2B5AE99500996546 /* SubscriptionFlowNavController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D4B77B2B5AE99500996546 /* SubscriptionFlowNavController.swift */; }; D6E83C122B1E6AB3006C8AFB /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E83C112B1E6AB3006C8AFB /* SettingsView.swift */; }; D6E83C2E2B1EA06E006C8AFB /* SettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E83C2D2B1EA06E006C8AFB /* SettingsViewModel.swift */; }; D6E83C312B1EA309006C8AFB /* SettingsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E83C302B1EA309006C8AFB /* SettingsCell.swift */; }; @@ -2458,7 +2457,6 @@ D6D12C9C2B291CA90054390C /* APIService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = APIService.swift; sourceTree = ""; }; D6D12C9D2B291CA90054390C /* AuthService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthService.swift; sourceTree = ""; }; D6D12C9E2B291CA90054390C /* PurchaseManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PurchaseManager.swift; sourceTree = ""; }; - D6D4B77B2B5AE99500996546 /* SubscriptionFlowNavController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionFlowNavController.swift; sourceTree = ""; }; D6E83C112B1E6AB3006C8AFB /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; D6E83C2D2B1EA06E006C8AFB /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = ""; }; D6E83C302B1EA309006C8AFB /* SettingsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsCell.swift; sourceTree = ""; }; @@ -4562,7 +4560,6 @@ D664C7932B289AA000CBFA76 /* ViewModel */ = { isa = PBXGroup; children = ( - D6D4B77B2B5AE99500996546 /* SubscriptionFlowNavController.swift */, D664C7942B289AA000CBFA76 /* SubscriptionFlowViewModel.swift */, D68DF81D2B5830380023DBEA /* SubscriptionRestoreViewModel.swift */, D64648AE2B5993890033090B /* SubscriptionEmailViewModel.swift */, @@ -6534,7 +6531,6 @@ 1E162610296C5C630004127F /* CustomDaxDialogViewModel.swift in Sources */, 8590CB69268A4E190089F6BF /* DebugEtagStorage.swift in Sources */, D6D12CA62B291CAA0054390C /* AppStoreRestoreFlow.swift in Sources */, - D6D4B77C2B5AE99500996546 /* SubscriptionFlowNavController.swift in Sources */, C1CDA3162AFB9C7F006D1476 /* AutofillNeverPromptWebsitesManager.swift in Sources */, F1CA3C371F045878005FADB3 /* PrivacyStore.swift in Sources */, 37FCAAC029930E26000E420A /* FailedAssertionView.swift in Sources */, diff --git a/DuckDuckGo/SettingsState.swift b/DuckDuckGo/SettingsState.swift index 9d9ff181a5..e6568efc7d 100644 --- a/DuckDuckGo/SettingsState.swift +++ b/DuckDuckGo/SettingsState.swift @@ -87,7 +87,7 @@ struct SettingsState { // Subscriptions Properties var subscription: Subscription - // Sync Propertiers + // Sync Properties var sync: SyncSettings static var defaults: SettingsState { diff --git a/DuckDuckGo/SettingsSubscriptionView.swift b/DuckDuckGo/SettingsSubscriptionView.swift index 53069a89c2..11e681147d 100644 --- a/DuckDuckGo/SettingsSubscriptionView.swift +++ b/DuckDuckGo/SettingsSubscriptionView.swift @@ -47,12 +47,14 @@ struct SettingsSubscriptionView: View { .daxBodyRegular() .foregroundColor(Color.init(designSystemColor: .accent)) } - - + private var purchaseSubscriptionView: some View { return Group { SettingsCustomCell(content: { subscriptionDescriptionView }) - NavigationLink(destination: SubscriptionFlowView(viewModel: SubscriptionFlowViewModel())) { + let viewModel = SubscriptionFlowViewModel(onFeatureSelected: { value in + self.viewModel.onAppearNavigationTarget = value + }) + NavigationLink(destination: SubscriptionFlowView(viewModel: viewModel)) { SettingsCustomCell(content: { learnMoreView }) } } @@ -66,11 +68,11 @@ struct SettingsSubscriptionView: View { disclosureIndicator: true, isButton: true) - NavigationLink(destination: Text("Data Broker Protection")) { + NavigationLink(destination: Text("Data Broker Protection"), isActive: $viewModel.shouldNavigateToDBP) { SettingsCellView(label: UserText.settingsPProDBPTitle, subtitle: UserText.settingsPProDBPSubTitle) } - NavigationLink(destination: Text("Identity Theft Restoration")) { + NavigationLink(destination: Text("Identity Theft Restoration"), isActive: $viewModel.shouldNavigateToITP) { SettingsCellView(label: UserText.settingsPProITRTitle, subtitle: UserText.settingsPProITRSubTitle) } diff --git a/DuckDuckGo/SettingsViewModel.swift b/DuckDuckGo/SettingsViewModel.swift index bff53aae81..fd452ddeca 100644 --- a/DuckDuckGo/SettingsViewModel.swift +++ b/DuckDuckGo/SettingsViewModel.swift @@ -56,12 +56,18 @@ final class SettingsViewModel: ObservableObject { @UserDefaultsWrapper(key: .subscriptionIsActive, defaultValue: false) static private var cachedHasActiveSubscription: Bool - // Closures to interact with legacy view controllers throught the container + // Closures to interact with legacy view controllers through the container var onRequestPushLegacyView: ((UIViewController) -> Void)? var onRequestPresentLegacyView: ((UIViewController, _ modal: Bool) -> Void)? var onRequestPopLegacyView: (() -> Void)? var onRequestDismissSettings: (() -> Void)? + // SwiftUI Programatic Navigation Variables + // Add more views as needed here... + @Published var shouldNavigateToDBP = false + @Published var shouldNavigateToITP = false + + // Subscription Entitlement names: TBD static let entitlementNames = ["dummy1", "dummy2", "dummy3"] // Our View State @@ -82,6 +88,12 @@ final class SettingsViewModel: ObservableObject { var shouldShowNoMicrophonePermissionAlert: Bool = false + // Used to automatically navigate on Appear to a specific section + enum SettingsSection: String { + case none, netP, dbp, itp + } + @Published var onAppearNavigationTarget: SettingsSection + // MARK: Bindings var themeBinding: Binding { Binding( @@ -183,10 +195,14 @@ final class SettingsViewModel: ObservableObject { } // MARK: Default Init - init(state: SettingsState? = nil, legacyViewProvider: SettingsLegacyViewProvider, accountManager: AccountManager) { + init(state: SettingsState? = nil, + legacyViewProvider: SettingsLegacyViewProvider, + accountManager: AccountManager, + navigateOnAppearDestination: SettingsSection = .none) { self.state = SettingsState.defaults self.legacyViewProvider = legacyViewProvider self.accountManager = accountManager + self.onAppearNavigationTarget = navigateOnAppearDestination } } @@ -287,7 +303,6 @@ extension SettingsViewModel { completion(true) } } - #if SUBSCRIPTION @available(iOS 15.0, *) @@ -348,7 +363,6 @@ extension SettingsViewModel { } } #endif - } // MARK: Subscribers @@ -365,7 +379,7 @@ extension SettingsViewModel { } .store(in: &cancellables) #endif - + } } @@ -374,6 +388,7 @@ extension SettingsViewModel { func onAppear() { initState() + Task { await MainActor.run { navigateOnAppear() } } } func setAsDefaultBrowser() { @@ -401,6 +416,25 @@ extension SettingsViewModel { onRequestDismissSettings?() } + @MainActor + private func navigateOnAppear() { + // We need a short delay to let the SwifttUI view lifecycle complete + // Otherwise the transition can be inconsistent + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + switch self.onAppearNavigationTarget { + case .netP: + self.presentLegacyView(.netP) + case .dbp: + self.shouldNavigateToDBP = true + case .itp: + self.shouldNavigateToITP = true + default: + break + } + self.onAppearNavigationTarget = .none + } + } + } // MARK: Legacy View Presentation diff --git a/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift index a481a14598..3b85bd54b5 100644 --- a/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift +++ b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift @@ -63,11 +63,16 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec case idle, purchasing, restoring, polling } + struct FeatureSelection: Codable { + let feature: String + } + @Published var transactionStatus: TransactionStatus = .idle @Published var hasActiveSubscription = false @Published var purchaseError: AppStorePurchaseFlow.Error? @Published var activateSubscription: Bool = false @Published var emailActivationComplete: Bool = false + @Published var selectedFeature: FeatureSelection? var broker: UserScriptMessageBroker? @@ -232,14 +237,11 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec } func featureSelected(params: Any, original: WKScriptMessage) async throws -> Encodable? { - struct FeatureSelection: Codable { - let feature: String - } - guard let featureSelection: FeatureSelection = DecodableHelper.decode(from: params) else { assertionFailure("SubscriptionPagesUserScript: expected JSON representation of FeatureSelection") return nil } + selectedFeature = featureSelection return nil } diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowNavController.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowNavController.swift deleted file mode 100644 index ddd207de0d..0000000000 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowNavController.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// SubscriptionFlowNavController.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 - -class SubscriptionFlowNavController: ObservableObject { - @Published var shouldDisplayRestoreView: Bool = false { - didSet { - print("shouldDisplayRestoreView changed to: \(shouldDisplayRestoreView)") - } - } -} diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift index d0638d4862..89cd53837b 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift @@ -29,24 +29,38 @@ final class SubscriptionFlowViewModel: ObservableObject { let userScript: SubscriptionPagesUserScript let subFeature: SubscriptionPagesUseSubscriptionFeature let purchaseManager: PurchaseManager - let viewTitle = UserText.settingsPProSection private var cancellables = Set() // State variables var purchaseURL = URL.purchaseSubscription + + // Closure passed to navigate to a specific section + // after returning to settings + var onFeatureSelected: ((SettingsViewModel.SettingsSection) -> Void) + + enum FeatureName { + static let netP = "vpn" + static let itp = "identity-theft-restoration" + static let dbp = "personal-information-removal" + } + + // Published properties @Published var hasActiveSubscription = false @Published var transactionStatus: SubscriptionPagesUseSubscriptionFeature.TransactionStatus = .idle @Published var shouldReloadWebView = false @Published var activatingSubscription = false + @Published var shouldDismissView = false init(userScript: SubscriptionPagesUserScript = SubscriptionPagesUserScript(), subFeature: SubscriptionPagesUseSubscriptionFeature = SubscriptionPagesUseSubscriptionFeature(), - purchaseManager: PurchaseManager = PurchaseManager.shared) { + purchaseManager: PurchaseManager = PurchaseManager.shared, + onFeatureSelected: @escaping ((SettingsViewModel.SettingsSection) -> Void)) { self.userScript = userScript self.subFeature = subFeature self.purchaseManager = purchaseManager + self.onFeatureSelected = onFeatureSelected } // Observe transaction status @@ -76,6 +90,26 @@ final class SubscriptionFlowViewModel: ObservableObject { } } .store(in: &cancellables) + + subFeature.$selectedFeature + .receive(on: DispatchQueue.main) + .sink { [weak self] value in + if value != nil { + self?.shouldDismissView = true + switch value?.feature { + case FeatureName.netP: + self?.onFeatureSelected(.netP) + case FeatureName.itp: + self?.onFeatureSelected(.itp) + case FeatureName.dbp: + self?.onFeatureSelected(.dbp) + default: + return + } + } + } + .store(in: &cancellables) + } @MainActor diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift index 85998e1bd5..458be8fad4 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift @@ -30,7 +30,7 @@ final class SubscriptionRestoreViewModel: ObservableObject { let subFeature: SubscriptionPagesUseSubscriptionFeature let purchaseManager: PurchaseManager let accountManager: AccountManager - let isAddingDevice: Bool + var isAddingDevice: Bool enum SubscriptionActivationResult { case unknown, activated, notFound, error @@ -56,6 +56,9 @@ final class SubscriptionRestoreViewModel: ObservableObject { func initializeView() { subscriptionEmail = accountManager.email + if accountManager.isUserAuthenticated { + isAddingDevice = true + } } @MainActor diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift index d7f5d98351..4cf960c44f 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift @@ -63,6 +63,7 @@ final class SubscriptionSettingsViewModel: ObservableObject { func removeSubscription() { AccountManager().signOut() + ActionMessageView.present(message: UserText.subscriptionRemovalConfirmation) } func manageSubscription() { diff --git a/DuckDuckGo/Subscription/Views/HeadlessWebView.swift b/DuckDuckGo/Subscription/Views/HeadlessWebView.swift index e80e3f5bdc..116e34f771 100644 --- a/DuckDuckGo/Subscription/Views/HeadlessWebView.swift +++ b/DuckDuckGo/Subscription/Views/HeadlessWebView.swift @@ -35,11 +35,9 @@ struct HeadlessWebview: UIViewRepresentable { configuration.userContentController = makeUserContentController() let webView = WKWebView(frame: .zero, configuration: configuration) + DefaultUserAgentManager.shared.update(webView: webView, isDesktop: false, url: url) - // We're using the macOS agent as the config for iOS has not been deployed in test env - webView.customUserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko)" - // DefaultUserAgentManager.shared.update(webView: webView, isDesktop: false, url: url) - + // Just add time if you need to hook the WebView inspector DispatchQueue.main.asyncAfter(deadline: .now() + 0) { webView.load(URLRequest(url: url)) } diff --git a/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift b/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift index 6538daedea..e9b06406ac 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift @@ -24,6 +24,7 @@ import Foundation @available(iOS 15.0, *) struct SubscriptionFlowView: View { + @Environment(\.dismiss) var dismiss @ObservedObject var viewModel: SubscriptionFlowViewModel @State private var isAlertVisible = false @@ -69,6 +70,11 @@ struct SubscriptionFlowView: View { isAlertVisible = true } } + .onChange(of: viewModel.shouldDismissView) { result in + if result { + dismiss() + } + } .onAppear(perform: { Task { await viewModel.initializeViewData() } diff --git a/DuckDuckGo/UserText.swift b/DuckDuckGo/UserText.swift index cd5f49ad45..531900ac59 100644 --- a/DuckDuckGo/UserText.swift +++ b/DuckDuckGo/UserText.swift @@ -1058,6 +1058,7 @@ But if you *do* want a peek under the hood, you can find more information about public static let subscriptionRemoveFromDeviceConfirmText = NSLocalizedString("subscription.remove.from.device.text", value: "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices.", comment: "Remove from device confirmation dialog text") public static let subscriptionRemove = NSLocalizedString("subscription.remove.subscription", value: "Remove Subscription", comment: "Remove subscription button text") public static let subscriptionRemoveCancel = NSLocalizedString("subscription.remove.subscription.cancel", value: "Cancel", comment: "Remove subscription cancel button text") + public static let subscriptionRemovalConfirmation = NSLocalizedString("subscription.cancel.message", value: "Your subscription has been removed from this device.", comment: "Subscription Removal confirmation message") // Subscription Restore public static let subscriptionActivate = NSLocalizedString("subscription.activate", value: "Activate Subscription", comment: "Subscription Activation Window Title") @@ -1091,8 +1092,6 @@ But if you *do* want a peek under the hood, you can find more information about public static let subscriptionManageEmailCancelButton = NSLocalizedString("subscription.activate.manage.email.cancel", value: "Cancel", comment: "Button title for cancelling email deletion") public static let subscriptionManageEmailOKButton = NSLocalizedString("subscription.activate.manage.email.OK", value: "OK", comment: "Button title for confirming email deletion") - - // Subscribe & Restore Flow public static let subscriptionFoundTitle = NSLocalizedString("subscription.found.title", value: "Subscription Found", comment: "Title for the existing subscription dialog") public static let subscriptionFoundText = NSLocalizedString("subscription.found.text", value: "We found a subscription associated with this Apple ID.", comment: "Message for the existing subscription dialog") @@ -1104,6 +1103,5 @@ But if you *do* want a peek under the hood, you can find more information about public static let subscriptionRestoreSuccessfulTitle = NSLocalizedString("subscription.restore.success.alert.title", value: "You’re all set.", comment: "Alert title for restored purchase") public static let subscriptionRestoreSuccessfulMessage = NSLocalizedString("subscription.restore.success.alert.message", value: "Your purchases have been restored.", comment: "Alert message for restored purchase") public static let subscriptionRestoreSuccessfulButton = NSLocalizedString("subscription.restore.success.alert.button", value: "OK", comment: "Alert button text for restored purchase alert") - - + } diff --git a/DuckDuckGo/en.lproj/Localizable.strings b/DuckDuckGo/en.lproj/Localizable.strings index f50749e44b..815afd61b6 100644 --- a/DuckDuckGo/en.lproj/Localizable.strings +++ b/DuckDuckGo/en.lproj/Localizable.strings @@ -1986,6 +1986,9 @@ But if you *do* want a peek under the hood, you can find more information about /* Subscription availability message on Apple devices */ "subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +/* Subscription Removal confirmation message */ +"subscription.cancel.message" = "Your subscription has been removed from this device."; + /* Change plan or billing title */ "subscription.change.plan" = "Change Plan Or Billing"; From c725cf7241d94ebed22579a8a46d164db986a538 Mon Sep 17 00:00:00 2001 From: Graeme Arthur Date: Wed, 24 Jan 2024 18:19:48 +0100 Subject: [PATCH 30/70] Alphabetize country list in geoswitching menu (#2363) --- ...etworkProtectionVPNLocationViewModel.swift | 12 +- ...kProtectionVPNLocationViewModelTests.swift | 105 +++++++++++++----- ...000170c180>{number = 1, name = main}.plist | Bin 106 -> 0 bytes 3 files changed, 90 insertions(+), 27 deletions(-) delete mode 100644 DuckDuckGoTests/NetworkProtectionVPNLocationViewModelTests.swift<_NSMainThread: 0x60000170c180>{number = 1, name = main}.plist diff --git a/DuckDuckGo/NetworkProtectionVPNLocationViewModel.swift b/DuckDuckGo/NetworkProtectionVPNLocationViewModel.swift index dfe0e162f3..dd6137928f 100644 --- a/DuckDuckGo/NetworkProtectionVPNLocationViewModel.swift +++ b/DuckDuckGo/NetworkProtectionVPNLocationViewModel.swift @@ -110,7 +110,7 @@ final class NetworkProtectionVPNLocationViewModel: ObservableObject { isSelected: isCountrySelected, cityPickerItems: cityPickerItems ) - } + }.sorted() self.isNearestSelected = isNearestSelected state = .loaded(countryItems: countryItems) } @@ -119,7 +119,15 @@ final class NetworkProtectionVPNLocationViewModel: ObservableObject { private typealias CountryItem = NetworkProtectionVPNCountryItemModel private typealias CityItem = NetworkProtectionVPNCityItemModel -struct NetworkProtectionVPNCountryItemModel: Identifiable { +struct NetworkProtectionVPNCountryItemModel: Identifiable, Comparable { + static func < (lhs: NetworkProtectionVPNCountryItemModel, rhs: NetworkProtectionVPNCountryItemModel) -> Bool { + lhs.title < rhs.title + } + + static func == (lhs: NetworkProtectionVPNCountryItemModel, rhs: NetworkProtectionVPNCountryItemModel) -> Bool { + lhs.id == rhs.id + } + private let labelsModel: NetworkProtectionVPNCountryLabelsModel var emoji: String { diff --git a/DuckDuckGoTests/NetworkProtectionVPNLocationViewModelTests.swift b/DuckDuckGoTests/NetworkProtectionVPNLocationViewModelTests.swift index 73e6bf09be..235d149d74 100644 --- a/DuckDuckGoTests/NetworkProtectionVPNLocationViewModelTests.swift +++ b/DuckDuckGoTests/NetworkProtectionVPNLocationViewModelTests.swift @@ -30,12 +30,13 @@ final class NetworkProtectionVPNLocationViewModelTests: XCTestCase { private var listRepository: MockNetworkProtectionLocationListRepository! private var settings: VPNSettings! private var viewModel: NetworkProtectionVPNLocationViewModel! + private var testDefaults: UserDefaults! @MainActor override func setUp() { super.setUp() listRepository = MockNetworkProtectionLocationListRepository() - let testDefaults = UserDefaults(suiteName: #file + Thread.current.debugDescription)! + testDefaults = UserDefaults() settings = VPNSettings(defaults: testDefaults) viewModel = NetworkProtectionVPNLocationViewModel(locationListRepository: listRepository, settings: settings) } @@ -45,6 +46,7 @@ final class NetworkProtectionVPNLocationViewModelTests: XCTestCase { settings = nil listRepository = nil viewModel = nil + testDefaults.removeSuite(named: #file + Thread.current.debugDescription) super.tearDown() } @@ -153,6 +155,12 @@ final class NetworkProtectionVPNLocationViewModelTests: XCTestCase { } } + func test_onViewAppeared_loadedListIsSorted() async throws { + try await assertOnListLoadSortsByCountryTitle { [weak self] in + await self?.viewModel.onViewAppeared() + } + } + // MARK: onNearestItemSelection func test_onNearestItemSelection_setsCorrectCountryTitle() async throws { @@ -209,6 +217,12 @@ final class NetworkProtectionVPNLocationViewModelTests: XCTestCase { } } + func test_onNearestItemSelection_loadedListIsSorted() async throws { + try await assertOnListLoadSortsByCountryTitle { [weak self] in + await self?.viewModel.onNearestItemSelection() + } + } + // MARK: onCountryItemSelection func test_onCountryItemSelection_setsCorrectCountryTitle() async throws { @@ -289,17 +303,52 @@ final class NetworkProtectionVPNLocationViewModelTests: XCTestCase { } } + func test_onCountryItemSelection_loadedListIsSorted() async throws { + try await assertOnListLoadSortsByCountryTitle { [weak self] in + await self?.viewModel.onCountryItemSelection(id: "CA") + } + } + // MARK: Assertions + func assertOnListLoadSortsByCountryTitle(when functionUnderTest: () async -> Void, + file: StaticString = #file, + line: UInt = #line) async throws { + let countryIDs = [ + "NL", + "ES", + "CA", + "UK", + "DE", + "SE" + ] + try stubLocationList(with: countryIDs) + + await functionUnderTest() + + let items = try loadedItems() + + let loaded = items.map(\.title) + let sortedCountryTitles = [ + "Canada", + "Germany", + "Netherlands", + "Spain", + "Sweden", + "United Kingdom" + ] + XCTAssertEqual(loaded, sortedCountryTitles, file: file, line: line) + } + func assertOnListLoadSetsCorrectCountryTitle(when functionUnderTest: () async -> Void, file: StaticString = #file, line: UInt = #line) async throws { let titlesForLocationsIDs = [ - "NL": "Netherlands", "DE": "Germany", + "NL": "Netherlands", "SE": "Sweden" ] - let countryIds = Array(titlesForLocationsIDs.keys) + let countryIds = Array(titlesForLocationsIDs.keys.sorted()) try stubLocationList(with: countryIds) await functionUnderTest() @@ -307,7 +356,9 @@ final class NetworkProtectionVPNLocationViewModelTests: XCTestCase { let items = try loadedItems() for i in 0.. Void, file: StaticString = #file, line: UInt = #line) async throws { - let countryIds = ["NL", "DE", "SE"] + let countryIds = ["DE", "NL", "SE"] try stubLocationList(with: countryIds) for i in 0.. Void, file: StaticString = #file, line: UInt = #line) async throws { - try stubLocationList(with: ["NL", "DE", "SE"]) + try stubLocationList(with: ["DE", "NL", "SE"]) await functionUnderTest() @@ -379,7 +434,7 @@ final class NetworkProtectionVPNLocationViewModelTests: XCTestCase { func assertNearestSelectedSetToFalse(when functionUnderTestWithTestCaseID: (String) async -> Void, file: StaticString = #file, line: UInt = #line) async throws { - let countryIds = ["NL", "DE", "SE"] + let countryIds = ["DE", "NL", "SE"] try stubLocationList(with: countryIds) for i in 0.. Void, file: StaticString = #file, line: UInt = #line) async throws { - let countryIds = ["NL", "DE", "SE"] + let countryIds = ["DE", "NL", "SE"] let countries: [NetworkProtectionLocation] = try countryIds.map { id in try .testData(country: id, cityNames: ["A city"]) } @@ -410,7 +465,7 @@ final class NetworkProtectionVPNLocationViewModelTests: XCTestCase { func assertOnListLoad_countryWith1City_shouldShowPickerIsFalse(when functionUnderTest: () async -> Void, file: StaticString = #file, line: UInt = #line) async throws { - let countryIds = ["NL", "DE", "SE"] + let countryIds = ["DE", "NL", "SE"] let countries: [NetworkProtectionLocation] = try countryIds.map { id in try .testData(country: id, cityNames: ["A city"]) } @@ -426,7 +481,7 @@ final class NetworkProtectionVPNLocationViewModelTests: XCTestCase { func assertOnListLoad_countryHasMoreThan1City_shouldShowPickerIsTrue(when functionUnderTest: () async -> Void, file: StaticString = #file, line: UInt = #line) async throws { - let countryIds = ["NL", "DE", "SE"] + let countryIds = ["DE", "NL", "SE"] var countries = [NetworkProtectionLocation]() for i in 0.. Void, file: StaticString = #file, line: UInt = #line) async throws { - let countryIds = ["NL", "DE", "SE"] + let countryIds = ["DE", "NL", "SE"] let countries: [NetworkProtectionLocation] = try countryIds.map { id in try .testData(country: id) } @@ -506,8 +561,8 @@ final class NetworkProtectionVPNLocationViewModelTests: XCTestCase { let cityTitles: [String] } let testCases: [TestCase] = [ - .init(countryId: "NL", cityTitles: ["Rotterdam", "Amsterdam"]), - .init(countryId: "DE", cityTitles: ["Berlin", "Frankfurt", "Bremen"]) + .init(countryId: "DE", cityTitles: ["Berlin", "Frankfurt", "Bremen"]), + .init(countryId: "NL", cityTitles: ["Rotterdam", "Amsterdam"]) ] let countries: [NetworkProtectionLocation] = try testCases.map { testCase in return try .testData(country: testCase.countryId, cityNames: testCase.cityTitles) @@ -532,13 +587,13 @@ final class NetworkProtectionVPNLocationViewModelTests: XCTestCase { file: StaticString = #file, line: UInt = #line) async throws { let countries: [NetworkProtectionLocation] = [ - try .testData(country: "NL", cityNames: ["Rotterdam", "Amsterdam"]), - try .testData(country: "DE", cityNames: ["Berlin", "Frankfurt", "Bremen"]) + try .testData(country: "DE", cityNames: ["Berlin", "Frankfurt", "Bremen"]), + try .testData(country: "NL", cityNames: ["Rotterdam", "Amsterdam"]) ] let selectionTestCases: [NetworkProtectionSelectedLocation] = [ - .init(country: "NL", city: "Amsterdam"), - .init(country: "DE", city: "Frankfurt") + .init(country: "DE", city: "Frankfurt"), + .init(country: "NL", city: "Amsterdam") ] listRepository.stubLocationList = countries @@ -555,8 +610,8 @@ final class NetworkProtectionVPNLocationViewModelTests: XCTestCase { file: StaticString = #file, line: UInt = #line) async throws { let countries: [NetworkProtectionLocation] = [ - try .testData(country: "NL", cityNames: ["Rotterdam", "Amsterdam"]), try .testData(country: "DE", cityNames: ["Berlin", "Frankfurt", "Bremen"]), + try .testData(country: "NL", cityNames: ["Rotterdam", "Amsterdam"]), try .testData(country: "SE", cityNames: ["Stockholm", "Malmö", "Helsingborg"]) ] diff --git a/DuckDuckGoTests/NetworkProtectionVPNLocationViewModelTests.swift<_NSMainThread: 0x60000170c180>{number = 1, name = main}.plist b/DuckDuckGoTests/NetworkProtectionVPNLocationViewModelTests.swift<_NSMainThread: 0x60000170c180>{number = 1, name = main}.plist deleted file mode 100644 index 4c375cd9b45468c03975f4da9db70030d6a2cd4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106 zcmYc)$jK}&F)+Bu$P_PNl9yUio?nz5P?TSinp~2ZpBJ23Qj(dM9-Nv3SB lg-h~_64O(|5_3vZUGhqEayYo17{Gv$5kfOCLunZ04gj8997zBG From 441ab3435b96254771ac4a422866bdbaffb47178 Mon Sep 17 00:00:00 2001 From: Christopher Brind Date: Wed, 24 Jan 2024 21:21:22 +0000 Subject: [PATCH 31/70] only show app store prompt on the serp (#2379) --- DuckDuckGo/TabViewController.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/DuckDuckGo/TabViewController.swift b/DuckDuckGo/TabViewController.swift index 62e9dd2845..f54d55883b 100644 --- a/DuckDuckGo/TabViewController.swift +++ b/DuckDuckGo/TabViewController.swift @@ -1052,7 +1052,9 @@ extension TabViewController: WKNavigationDelegate { appRatingPrompt.registerUsage() - if let scene = self.view.window?.windowScene, appRatingPrompt.shouldPrompt() { + if let scene = self.view.window?.windowScene, + appRatingPrompt.shouldPrompt(), + webView.url?.isDuckDuckGoSearch == true { SKStoreReviewController.requestReview(in: scene) appRatingPrompt.shown() } From 459cee9e94d0fe1b330489f52d57b76c3f1f1976 Mon Sep 17 00:00:00 2001 From: Anh Do <18567+quanganhdo@users.noreply.github.com> Date: Wed, 24 Jan 2024 16:30:40 -0500 Subject: [PATCH 32/70] Fix Alpha Debug build not installing NetP (#2380) --- DuckDuckGo.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 69bb109f32..b4a4f9bb5d 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -6408,7 +6408,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# Conditionally embeds PacketTunnelProvider extension for Debug and Alpha builds.\n\n# Conditionally embeds the PacketTunnelProvider extension for debug builds.\\n# To be moved to the Embed App Extensions phase on release.\n\nif [ \"${CONFIGURATION}\" = \"Debug\" ] || [ \"${CONFIGURATION}\" = \"Release\" ] || [ \"${CONFIGURATION}\" = \"Alpha\" ]; then\n# Copy the extension \n rsync -r --copy-links \"${CONFIGURATION_BUILD_DIR}/PacketTunnelProvider.appex\" \"${CONFIGURATION_BUILD_DIR}/${PLUGINS_FOLDER_PATH}\"\nfi\n"; + shellScript = "# Conditionally embeds PacketTunnelProvider extension for Debug and Alpha builds.\n\n# Conditionally embeds the PacketTunnelProvider extension for debug builds.\\n# To be moved to the Embed App Extensions phase on release.\n\nif [ \"${CONFIGURATION}\" = \"Debug\" ] || [ \"${CONFIGURATION}\" = \"Release\" ] || [ \"${CONFIGURATION}\" = \"Alpha\" || [ \"${CONFIGURATION}\" = \"Alpha Debug\" ]; then\n# Copy the extension \n rsync -r --copy-links \"${CONFIGURATION_BUILD_DIR}/PacketTunnelProvider.appex\" \"${CONFIGURATION_BUILD_DIR}/${PLUGINS_FOLDER_PATH}\"\nfi\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -9125,7 +9125,7 @@ MTL_FAST_MATH = YES; OTHER_CFLAGS = ""; OTHER_SWIFT_FLAGS = "-D NETWORK_EXTENSION"; - PRODUCT_BUNDLE_IDENTIFIER = com.duckduckgo.mobile.ios.NetworkExtension; + PRODUCT_BUNDLE_IDENTIFIER = com.duckduckgo.mobile.ios.alpha.NetworkExtension; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; From 6c22f4052ce354695c0dfb9f94fc64daff049386 Mon Sep 17 00:00:00 2001 From: Anh Do <18567+quanganhdo@users.noreply.github.com> Date: Wed, 24 Jan 2024 16:55:33 -0500 Subject: [PATCH 33/70] Integrate NetP with subscription (#2359) --- DuckDuckGo/MainViewController.swift | 56 +++++++++++++++++++++++++ DuckDuckGo/en.lproj/Localizable.strings | 12 ++++++ 2 files changed, 68 insertions(+) diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index b112492b87..785dc198a8 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -31,6 +31,10 @@ import Persistence import PrivacyDashboard import Networking +#if NETWORK_PROTECTION +import NetworkProtection +#endif + // swiftlint:disable file_length // swiftlint:disable type_body_length class MainViewController: UIViewController { @@ -98,6 +102,10 @@ class MainViewController: UIViewController { private var syncFeatureFlagsCancellable: AnyCancellable? private var favoritesDisplayModeCancellable: AnyCancellable? private var emailCancellables = Set() + +#if NETWORK_PROTECTION + private var netpCancellables = Set() +#endif private lazy var featureFlagger = AppDependencyProvider.shared.featureFlagger @@ -244,6 +252,10 @@ class MainViewController: UIViewController { addLaunchTabNotificationObserver() subscribeToEmailProtectionStatusNotifications() +#if NETWORK_PROTECTION + subscribeToNetworkProtectionSubscriptionEvents() +#endif + findInPageView.delegate = self findInPageBottomLayoutConstraint.constant = 0 registerForKeyboardNotifications() @@ -1221,6 +1233,50 @@ class MainViewController: UIViewController { .store(in: &emailCancellables) } +#if NETWORK_PROTECTION + private func subscribeToNetworkProtectionSubscriptionEvents() { + NotificationCenter.default.publisher(for: .accountDidSignIn) + .receive(on: DispatchQueue.main) + .sink { [weak self] notification in + self?.onNetworkProtectionAccountSignIn(notification) + } + .store(in: &netpCancellables) + NotificationCenter.default.publisher(for: .accountDidSignOut) + .receive(on: DispatchQueue.main) + .sink { [weak self] notification in + self?.onNetworkProtectionAccountSignOut(notification) + } + .store(in: &netpCancellables) + } + + @objc + private func onNetworkProtectionAccountSignIn(_ notification: Notification) { + guard let token = AccountManager().accessToken else { + assertionFailure("[NetP Subscription] AccountManager signed in but token could not be retrieved") + return + } + + Task { + do { + try await NetworkProtectionCodeRedemptionCoordinator().exchange(accessToken: token) + print("[NetP Subscription] Exchanged access token for auth token successfully") + } catch { + print("[NetP Subscription] Failed to exchange access token for auth token: \(error)") + } + } + } + + @objc + private func onNetworkProtectionAccountSignOut(_ notification: Notification) { + do { + try NetworkProtectionKeychainTokenStore().deleteToken() + print("[NetP Subscription] Deleted NetP auth token after signing out from Privacy Pro") + } catch { + print("[NetP Subscription] Failed to delete NetP auth token after signing out from Privacy Pro: \(error)") + } + } +#endif + @objc private func onDuckDuckGoEmailSignIn(_ notification: Notification) { fireEmailPixel(.emailEnabled, notification: notification) diff --git a/DuckDuckGo/en.lproj/Localizable.strings b/DuckDuckGo/en.lproj/Localizable.strings index 815afd61b6..91a2b13227 100644 --- a/DuckDuckGo/en.lproj/Localizable.strings +++ b/DuckDuckGo/en.lproj/Localizable.strings @@ -2067,6 +2067,18 @@ But if you *do* want a peek under the hood, you can find more information about /* Subscription Expiration Data */ "subscription.subscription.active.caption" = "Your Privacy Pro subscription renews on %@"; +/* Cancel action for the existing subscription dialog */ +"subscription.subscription.found.cancel" = "Cancel"; + +/* Restore action for the existing subscription dialog */ +"subscription.subscription.found.restore" = "Restore"; + +/* Message for the existing subscription dialog */ +"subscription.subscription.found.text" = "We found a subscription associated with this Apple ID."; + +/* Title for the existing subscription dialog */ +"subscription.subscription.found.title" = "Subscription Found"; + /* Message confirming that recovery code was copied to clipboard */ "sync.code.copied" = "Recovery code copied to clipboard"; From 5cab245577e0977efda8fb70b595a8ee277bf394 Mon Sep 17 00:00:00 2001 From: Christopher Brind Date: Thu, 25 Jan 2024 10:15:42 +0000 Subject: [PATCH 34/70] fix iPad tabs crash (#2381) --- DuckDuckGo/MainViewController.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 785dc198a8..ea83e4a139 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -869,8 +869,13 @@ class MainViewController: UIViewController { func select(tabAt index: Int) { viewCoordinator.navigationBarContainer.alpha = 1 allowContentUnderflow = false - let tab = tabManager.select(tabAt: index) - select(tab: tab) + + if tabManager.model.tabs.indices.contains(index) { + let tab = tabManager.select(tabAt: index) + select(tab: tab) + } else { + assertionFailure("Invalid index selected") + } } fileprivate func select(tab: TabViewController) { From 5ee96fb0f2e63eefeaee51b36abb68338241ed6a Mon Sep 17 00:00:00 2001 From: Sabrina Tardio <44158575+SabrinaTardio@users.noreply.github.com> Date: Thu, 25 Jan 2024 12:41:34 +0100 Subject: [PATCH 35/70] Display different copy in alerts when a flag is enabled in a newer version (#2374) Task/Issue URL: https://app.asana.com/0/72649045549333/1206310394453442/f Description: Shows a different copy in alerts when a sync flag is enabled in a newer version and the user needs to update the app --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 +-- DuckDuckGo/SyncSettingsViewController.swift | 1 + LocalPackages/DuckUI/Package.swift | 2 +- LocalPackages/SyncUI/Package.swift | 2 +- .../ViewModels/SyncSettingsViewModel.swift | 1 + .../Views/SyncSettingsViewExtension.swift | 28 +++++++++++++------ LocalPackages/Waitlist/Package.swift | 2 +- 8 files changed, 27 insertions(+), 15 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index b4a4f9bb5d..beac7c45ac 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -9974,7 +9974,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 101.2.2; + version = 101.2.3; }; }; C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index d85be58c3b..0e2ab17936 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "1f7932fe67a0d8b1ae97e62cb333639353d4772f", - "version" : "101.2.2" + "revision" : "826d7d95529d9feac4b25f7f1f236a7e4ac1d9a9", + "version" : "101.2.3" } }, { diff --git a/DuckDuckGo/SyncSettingsViewController.swift b/DuckDuckGo/SyncSettingsViewController.swift index d8b8dc28f4..f49d56003c 100644 --- a/DuckDuckGo/SyncSettingsViewController.swift +++ b/DuckDuckGo/SyncSettingsViewController.swift @@ -97,6 +97,7 @@ class SyncSettingsViewController: UIHostingController { viewModel.isConnectingDevicesAvailable = featureFlags.contains(.connectFlows) viewModel.isAccountCreationAvailable = featureFlags.contains(.accountCreation) viewModel.isAccountRecoveryAvailable = featureFlags.contains(.accountRecovery) + viewModel.isAppVersionNotSupported = featureFlags.unavailableReason == .appVersionNotSupported } .store(in: &cancellables) } diff --git a/LocalPackages/DuckUI/Package.swift b/LocalPackages/DuckUI/Package.swift index 74c51b6864..6d04a18ac4 100644 --- a/LocalPackages/DuckUI/Package.swift +++ b/LocalPackages/DuckUI/Package.swift @@ -31,7 +31,7 @@ let package = Package( targets: ["DuckUI"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.3"), ], targets: [ .target( diff --git a/LocalPackages/SyncUI/Package.swift b/LocalPackages/SyncUI/Package.swift index b7c6f13b13..f38c329ede 100644 --- a/LocalPackages/SyncUI/Package.swift +++ b/LocalPackages/SyncUI/Package.swift @@ -33,7 +33,7 @@ let package = Package( ], dependencies: [ .package(path: "../DuckUI"), - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.3"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ diff --git a/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/SyncSettingsViewModel.swift b/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/SyncSettingsViewModel.swift index 6934aaae01..a51474ebc3 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/SyncSettingsViewModel.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/SyncSettingsViewModel.swift @@ -88,6 +88,7 @@ public class SyncSettingsViewModel: ObservableObject { @Published public var isConnectingDevicesAvailable: Bool = true @Published public var isAccountCreationAvailable: Bool = true @Published public var isAccountRecoveryAvailable: Bool = true + @Published public var isAppVersionNotSupported: Bool = false public weak var delegate: SyncManagementViewModelDelegate? private(set) var isOnDevEnvironment: Bool diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsViewExtension.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsViewExtension.swift index b4f5c9e990..95922b61a5 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsViewExtension.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsViewExtension.swift @@ -26,7 +26,11 @@ extension SyncSettingsView { @ViewBuilder func syncUnavailableViewWhileLoggedOut() -> some View { if !model.isDataSyncingAvailable || !model.isConnectingDevicesAvailable || !model.isAccountCreationAvailable { - SyncWarningMessageView(title: UserText.syncUnavailableTitle, message: UserText.syncUnavailableMessage) + if model.isAppVersionNotSupported { + SyncWarningMessageView(title: UserText.syncUnavailableTitle, message: UserText.syncUnavailableMessageUpgradeRequired) + } else { + SyncWarningMessageView(title: UserText.syncUnavailableTitle, message: UserText.syncUnavailableMessage) + } } else { EmptyView() } @@ -105,7 +109,11 @@ extension SyncSettingsView { if model.isDataSyncingAvailable { EmptyView() } else { - SyncWarningMessageView(title: UserText.syncPausedTitle, message: UserText.syncUnavailableMessage) + if model.isAppVersionNotSupported { + SyncWarningMessageView(title: UserText.syncUnavailableTitle, message: UserText.syncUnavailableMessageUpgradeRequired) + } else { + SyncWarningMessageView(title: UserText.syncUnavailableTitle, message: UserText.syncUnavailableMessage) + } } } @@ -306,13 +314,15 @@ extension SyncSettingsView { @ViewBuilder func rolloutBanner() -> some View { - HStack(alignment: .top, spacing: 16) { - Image("Info-Color-16") - Text(UserText.syncRollOutBannerDescription) - .font(.system(size: 12)) - .foregroundColor(.primary) - .multilineTextAlignment(.leading) - .fixedSize(horizontal: false, vertical: true) + Section { + HStack(alignment: .top, spacing: 16) { + Image("Info-Color-16") + Text(UserText.syncRollOutBannerDescription) + .font(.system(size: 12)) + .foregroundColor(.primary) + .multilineTextAlignment(.leading) + .fixedSize(horizontal: false, vertical: true) + } } .padding() .background(RoundedRectangle(cornerRadius: 8).foregroundColor(Color("RolloutBannerBackground"))) diff --git a/LocalPackages/Waitlist/Package.swift b/LocalPackages/Waitlist/Package.swift index 3988db42e0..e0afabe4d9 100644 --- a/LocalPackages/Waitlist/Package.swift +++ b/LocalPackages/Waitlist/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["Waitlist", "WaitlistMocks"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.3"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ From a5a6cd9f2038ae577aa6e987740026f1ca52de9f Mon Sep 17 00:00:00 2001 From: Graeme Arthur Date: Thu, 25 Jan 2024 13:51:23 +0100 Subject: [PATCH 36/70] BSK changes to Launch VPN Locations from macOS status view (#2347) --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- LocalPackages/DuckUI/Package.swift | 2 +- LocalPackages/SyncUI/Package.swift | 2 +- LocalPackages/Waitlist/Package.swift | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index beac7c45ac..d2ff90629d 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -9974,7 +9974,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 101.2.3; + version = 102.0.0; }; }; C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 0e2ab17936..b09013884d 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "826d7d95529d9feac4b25f7f1f236a7e4ac1d9a9", - "version" : "101.2.3" + "revision" : "02ef8a68c623cd993adbebbf97646ff05d40593b", + "version" : "102.0.0" } }, { diff --git a/LocalPackages/DuckUI/Package.swift b/LocalPackages/DuckUI/Package.swift index 6d04a18ac4..e1418d52bf 100644 --- a/LocalPackages/DuckUI/Package.swift +++ b/LocalPackages/DuckUI/Package.swift @@ -31,7 +31,7 @@ let package = Package( targets: ["DuckUI"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.3"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "102.0.0"), ], targets: [ .target( diff --git a/LocalPackages/SyncUI/Package.swift b/LocalPackages/SyncUI/Package.swift index f38c329ede..f9583eb012 100644 --- a/LocalPackages/SyncUI/Package.swift +++ b/LocalPackages/SyncUI/Package.swift @@ -33,7 +33,7 @@ let package = Package( ], dependencies: [ .package(path: "../DuckUI"), - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.3"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "102.0.0"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ diff --git a/LocalPackages/Waitlist/Package.swift b/LocalPackages/Waitlist/Package.swift index e0afabe4d9..e74a9dfab1 100644 --- a/LocalPackages/Waitlist/Package.swift +++ b/LocalPackages/Waitlist/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["Waitlist", "WaitlistMocks"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "101.2.3"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "102.0.0"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ From 09bba67fb0bfb0c2ae706524f52fae992c7ff4f3 Mon Sep 17 00:00:00 2001 From: bwaresiak Date: Thu, 25 Jan 2024 15:04:38 +0100 Subject: [PATCH 37/70] Merge Release 7.105.1 (#2378) (#2383) Co-authored-by: Christopher Brind --- Core/CookieStorage.swift | 86 +++++++- Core/WebCacheManager.swift | 15 +- DuckDuckGo.xcodeproj/project.pbxproj | 4 + DuckDuckGoTests/CookieStorageTests.swift | 188 ++++++++++++++++++ .../FireButtonReferenceTests.swift | 93 +++------ 5 files changed, 302 insertions(+), 84 deletions(-) create mode 100644 DuckDuckGoTests/CookieStorageTests.swift diff --git a/Core/CookieStorage.swift b/Core/CookieStorage.swift index 03d6426a22..5624827ee1 100644 --- a/Core/CookieStorage.swift +++ b/Core/CookieStorage.swift @@ -22,16 +22,27 @@ import Foundation public class CookieStorage { - struct Constants { - static let key = "com.duckduckgo.allowedCookies" + struct Keys { + static let allowedCookies = "com.duckduckgo.allowedCookies" + static let consumed = "com.duckduckgo.consumedCookies" } private var userDefaults: UserDefaults - + + var isConsumed: Bool { + get { + userDefaults.bool(forKey: Keys.consumed, defaultValue: false) + } + set { + userDefaults.set(newValue, forKey: Keys.consumed) + } + } + + /// Use the `updateCookies` function rather than the setter which is only visible for testing. var cookies: [HTTPCookie] { get { var storedCookies = [HTTPCookie]() - if let cookies = userDefaults.object(forKey: Constants.key) as? [[String: Any?]] { + if let cookies = userDefaults.object(forKey: Keys.allowedCookies) as? [[String: Any?]] { for cookieData in cookies { var properties = [HTTPCookiePropertyKey: Any]() cookieData.forEach({ @@ -57,17 +68,76 @@ public class CookieStorage { } cookies.append(mappedCookie) } - userDefaults.setValue(cookies, forKey: Constants.key) + userDefaults.setValue(cookies, forKey: Keys.allowedCookies) } + } public init(userDefaults: UserDefaults = UserDefaults.app) { self.userDefaults = userDefaults } - func clear() { - userDefaults.removeObject(forKey: Constants.key) - os_log("cleared cookies", log: .generalLog, type: .debug) + enum CookieDomainsOnUpdate { + case empty + case match + case missing + case different } + + @discardableResult + func updateCookies(_ cookies: [HTTPCookie], keepingPreservedLogins preservedLogins: PreserveLogins) -> CookieDomainsOnUpdate { + isConsumed = false + + let persisted = self.cookies + + func cookiesByDomain(_ cookies: [HTTPCookie]) -> [String: [HTTPCookie]] { + var byDomain = [String: [HTTPCookie]]() + cookies.forEach { cookie in + var cookies = byDomain[cookie.domain, default: []] + cookies.append(cookie) + byDomain[cookie.domain] = cookies + } + return byDomain + } + + let updatedCookiesByDomain = cookiesByDomain(cookies) + var persistedCookiesByDomain = cookiesByDomain(persisted) + // Do the diagnostics before the dicts get changed. + let diagnosticResult = evaluateDomains( + updatedDomains: updatedCookiesByDomain.keys.sorted(), + persistedDomains: persistedCookiesByDomain.keys.sorted() + ) + + updatedCookiesByDomain.keys.forEach { + persistedCookiesByDomain[$0] = updatedCookiesByDomain[$0] + } + + persistedCookiesByDomain.keys.forEach { + guard $0 != "duckduckgo.com" else { return } // DDG cookies are for SERP settings only + + if !preservedLogins.isAllowed(cookieDomain: $0) { + persistedCookiesByDomain.removeValue(forKey: $0) + } + } + + let now = Date() + self.cookies = persistedCookiesByDomain.map { $0.value }.joined().compactMap { $0 } + .filter { $0.expiresDate == nil || $0.expiresDate! > now } + + return diagnosticResult + } + + private func evaluateDomains(updatedDomains: [String], persistedDomains: [String]) -> CookieDomainsOnUpdate { + if persistedDomains.isEmpty { + return .empty + } else if updatedDomains.count < persistedDomains.count { + return .missing + } else if updatedDomains == persistedDomains { + return .match + } else { + return .different + } + } + } diff --git a/Core/WebCacheManager.swift b/Core/WebCacheManager.swift index bafedec89b..2c66cdd06b 100644 --- a/Core/WebCacheManager.swift +++ b/Core/WebCacheManager.swift @@ -74,7 +74,7 @@ public class WebCacheManager { let cookies = cookieStorage.cookies - guard !cookies.isEmpty else { + guard !cookies.isEmpty, !cookieStorage.isConsumed else { completion() return } @@ -93,9 +93,9 @@ public class WebCacheManager { DispatchQueue.global(qos: .userInitiated).async { group.wait() + cookieStorage.isConsumed = true DispatchQueue.main.async { - cookieStorage.clear() completion() if cookieStorage.cookies.count > 0 { @@ -162,7 +162,7 @@ public class WebCacheManager { logins: PreserveLogins, storeIdManager: DataStoreIdManager, completion: @escaping () -> Void) { - + guard let containerId = storeIdManager.id else { completion() return @@ -181,11 +181,8 @@ public class WebCacheManager { await checkForLeftBehindDataStores() storeIdManager.allocateNewContainerId() - // If cookies is empty it's likely that the webview was not used since the last fire button so - // don't overwrite previously saved cookies - if let cookies, !cookies.isEmpty { - cookieStorage.cookies = cookies - } + + cookieStorage.updateCookies(cookies ?? [], keepingPreservedLogins: logins) completion() } @@ -208,7 +205,7 @@ public class WebCacheManager { // From this point onwards... use containers dataStoreIdManager.allocateNewContainerId() Task { @MainActor in - cookieStorage.cookies = cookies + cookieStorage.updateCookies(cookies, keepingPreservedLogins: logins) completion() } } else { diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index d2ff90629d..3c21818caf 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -460,6 +460,7 @@ 85A1B3B220C6CD9900C18F15 /* CookieStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85A1B3B120C6CD9900C18F15 /* CookieStorage.swift */; }; 85A313972028E78A00327D00 /* release_notes.txt in Resources */ = {isa = PBXBuildFile; fileRef = 85A313962028E78A00327D00 /* release_notes.txt */; }; 85A9C37920E0E00C00073340 /* HomeRow.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 85A9C37820E0E00C00073340 /* HomeRow.xcassets */; }; + 85AD49EE2B6149110085D2D1 /* CookieStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85AD49ED2B6149110085D2D1 /* CookieStorageTests.swift */; }; 85AE668E2097206E0014CF04 /* NotificationView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 85AE668D2097206E0014CF04 /* NotificationView.xib */; }; 85AE6690209724120014CF04 /* NotificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85AE668F209724120014CF04 /* NotificationView.swift */; }; 85AFA1212B45D14F0028A504 /* BookmarksMigrationAssertionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85AFA1202B45D14F0028A504 /* BookmarksMigrationAssertionTests.swift */; }; @@ -1550,6 +1551,7 @@ 85A313962028E78A00327D00 /* release_notes.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = release_notes.txt; path = fastlane/metadata/default/release_notes.txt; sourceTree = ""; }; 85A53EC9200D1FA20010D13F /* FileStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileStore.swift; sourceTree = ""; }; 85A9C37820E0E00C00073340 /* HomeRow.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = HomeRow.xcassets; sourceTree = ""; }; + 85AD49ED2B6149110085D2D1 /* CookieStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CookieStorageTests.swift; sourceTree = ""; }; 85AE668D2097206E0014CF04 /* NotificationView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NotificationView.xib; sourceTree = ""; }; 85AE668F209724120014CF04 /* NotificationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationView.swift; sourceTree = ""; }; 85AFA1202B45D14F0028A504 /* BookmarksMigrationAssertionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksMigrationAssertionTests.swift; sourceTree = ""; }; @@ -5305,6 +5307,7 @@ 8540BD5123D8C2220057FDD2 /* PreserveLoginsTests.swift */, 850559D123CF710C0055C0D5 /* WebCacheManagerTests.swift */, F198D7971E3A45D90088DA8A /* WKWebViewConfigurationExtensionTests.swift */, + 85AD49ED2B6149110085D2D1 /* CookieStorageTests.swift */, ); name = Web; sourceTree = ""; @@ -7055,6 +7058,7 @@ 8521FDE6238D414B00A44CC3 /* FileStoreTests.swift in Sources */, F14E491F1E391CE900DC037C /* URLExtensionTests.swift in Sources */, 85D2187424BF25CD004373D2 /* FaviconsTests.swift in Sources */, + 85AD49EE2B6149110085D2D1 /* CookieStorageTests.swift in Sources */, CBCCF96828885DEE006F4A71 /* AppPrivacyConfigurationTests.swift in Sources */, 310742AB2848E6FD0012660B /* BackForwardMenuHistoryItemURLSanitizerTests.swift in Sources */, 22CB1ED8203DDD2C00D2C724 /* AppDeepLinksTests.swift in Sources */, diff --git a/DuckDuckGoTests/CookieStorageTests.swift b/DuckDuckGoTests/CookieStorageTests.swift new file mode 100644 index 0000000000..79b0dc2751 --- /dev/null +++ b/DuckDuckGoTests/CookieStorageTests.swift @@ -0,0 +1,188 @@ +// +// CookieStorageTests.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 XCTest +@testable import Core +import WebKit + +public class CookieStorageTests: XCTestCase { + + var storage: CookieStorage! + + // This is updated by the `make` function which preserves any cookies added as part of this test + let logins = PreserveLogins.shared + + static let userDefaultsSuiteName = "test" + + public override func setUp() { + super.setUp() + let defaults = UserDefaults(suiteName: Self.userDefaultsSuiteName)! + defaults.removePersistentDomain(forName: Self.userDefaultsSuiteName) + storage = CookieStorage(userDefaults: defaults) + logins.clearAll() + } + + func testWhenUpdatedThenDuckDuckGoCookiesAreNotRemoved() { + storage.updateCookies([ + make("duckduckgo.com", name: "x", value: "1"), + ], keepingPreservedLogins: logins) + + XCTAssertEqual(1, storage.cookies.count) + + storage.updateCookies([ + make("test.com", name: "x", value: "1"), + ], keepingPreservedLogins: logins) + + XCTAssertEqual(2, storage.cookies.count) + + } + + func testWhenUpdatedThenCookiesWithFutureExpirationAreNotRemoved() { + storage.updateCookies([ + make("test.com", name: "x", value: "1", expires: .distantFuture), + ], keepingPreservedLogins: logins) + + storage.updateCookies([ + make("example.com", name: "x", value: "1"), + ], keepingPreservedLogins: logins) + + XCTAssertEqual(2, storage.cookies.count) + XCTAssertTrue(storage.cookies.contains(where: { $0.domain == "test.com" })) + XCTAssertTrue(storage.cookies.contains(where: { $0.domain == "example.com" })) + + } + + func testWhenUpdatingThenExistingExpiredCookiesAreRemoved() { + storage.cookies = [ + make("test.com", name: "x", value: "1", expires: Date(timeIntervalSinceNow: -100)), + ] + XCTAssertEqual(1, storage.cookies.count) + + storage.updateCookies([ + make("example.com", name: "x", value: "1"), + ], keepingPreservedLogins: logins) + + XCTAssertEqual(1, storage.cookies.count) + XCTAssertFalse(storage.cookies.contains(where: { $0.domain == "test.com" })) + XCTAssertTrue(storage.cookies.contains(where: { $0.domain == "example.com" })) + + } + + func testWhenExpiredCookieIsAddedThenItIsNotPersisted() { + + storage.updateCookies([ + make("example.com", name: "x", value: "1", expires: Date(timeIntervalSinceNow: -100)), + ], keepingPreservedLogins: logins) + + XCTAssertEqual(0, storage.cookies.count) + + } + + func testWhenUpdatedThenNoLongerPreservedDomainsAreCleared() { + storage.updateCookies([ + make("test.com", name: "x", value: "1"), + make("example.com", name: "x", value: "1"), + ], keepingPreservedLogins: logins) + + logins.remove(domain: "test.com") + + storage.updateCookies([ + make("example.com", name: "x", value: "1"), + ], keepingPreservedLogins: logins) + + XCTAssertEqual(1, storage.cookies.count) + XCTAssertFalse(storage.cookies.contains(where: { $0.domain == "test.com" })) + XCTAssertTrue(storage.cookies.contains(where: { $0.domain == "example.com" })) + } + + func testWhenStorageInitialiedThenItIsEmptyAndConsumedIsFalse() { + XCTAssertEqual(0, storage.cookies.count) + XCTAssertEqual(false, storage.isConsumed) + } + + func testWhenStorageIsUpdatedThenConsumedIsResetToFalse() { + storage.isConsumed = true + XCTAssertTrue(storage.isConsumed) + storage.updateCookies([ + make("test.com", name: "x", value: "1") + ], keepingPreservedLogins: logins) + XCTAssertFalse(storage.isConsumed) + } + + func testWhenStorageIsReinstanciatedThenUsesStoredData() { + storage.updateCookies([ + make("test.com", name: "x", value: "1") + ], keepingPreservedLogins: logins) + storage.isConsumed = true + + let otherStorage = CookieStorage(userDefaults: UserDefaults(suiteName: Self.userDefaultsSuiteName)!) + XCTAssertEqual(1, otherStorage.cookies.count) + XCTAssertTrue(otherStorage.isConsumed) + } + + func testWhenStorageIsUpdatedThenUpdatingAddsNewCookies() { + storage.updateCookies([ + make("test.com", name: "x", value: "1") + ], keepingPreservedLogins: logins) + XCTAssertEqual(1, storage.cookies.count) + } + + func testWhenStorageIsUpdatedThenExistingCookiesAreUnaffected() { + storage.updateCookies([ + make("test.com", name: "x", value: "1"), + make("example.com", name: "x", value: "1"), + ], keepingPreservedLogins: logins) + + storage.updateCookies([ + make("example.com", name: "x", value: "2"), + ], keepingPreservedLogins: logins) + + XCTAssertEqual(2, storage.cookies.count) + XCTAssertTrue(storage.cookies.contains(where: { $0.domain == "test.com" && $0.name == "x" && $0.value == "1" })) + XCTAssertTrue(storage.cookies.contains(where: { $0.domain == "example.com" && $0.name == "x" && $0.value == "2" })) + } + + func testWhenStorageHasMatchingDOmainThenUpdatingReplacesCookies() { + storage.updateCookies([ + make("test.com", name: "x", value: "1") + ], keepingPreservedLogins: logins) + + storage.updateCookies([ + make("test.com", name: "x", value: "2"), + make("test.com", name: "y", value: "3"), + ], keepingPreservedLogins: logins) + + XCTAssertEqual(2, storage.cookies.count) + XCTAssertFalse(storage.cookies.contains(where: { $0.domain == "test.com" && $0.name == "x" && $0.value == "1" })) + XCTAssertTrue(storage.cookies.contains(where: { $0.domain == "test.com" && $0.name == "x" && $0.value == "2" })) + XCTAssertTrue(storage.cookies.contains(where: { $0.domain == "test.com" && $0.name == "y" && $0.value == "3" })) + } + + func make(_ domain: String, name: String, value: String, expires: Date? = nil) -> HTTPCookie { + logins.addToAllowed(domain: domain) + return HTTPCookie(properties: [ + .domain: domain, + .name: name, + .value: value, + .path: "/", + .expires: expires as Any + ])! + } + +} diff --git a/DuckDuckGoTests/FireButtonReferenceTests.swift b/DuckDuckGoTests/FireButtonReferenceTests.swift index 2d81f4d26d..2f04f6fbc3 100644 --- a/DuckDuckGoTests/FireButtonReferenceTests.swift +++ b/DuckDuckGoTests/FireButtonReferenceTests.swift @@ -23,9 +23,6 @@ import WebKit @testable import Core final class FireButtonReferenceTests: XCTestCase { - private var referenceTests = [Test]() - private let preservedLogins = PreserveLogins.shared - private let dataStore = WKWebsiteDataStore.default() private enum Resource { static let tests = "privacy-reference-tests/storage-clearing/tests.json" @@ -36,19 +33,6 @@ final class FireButtonReferenceTests: XCTestCase { // swiftlint:disable:next force_try return try! JSONDecoder().decode(TestData.self, from: testJSON) }() - - override func tearDownWithError() throws { - try super.tearDownWithError() - - // Remove fireproofed sites - for site in testData.fireButtonFireproofing.fireproofedSites { - let sanitizedSite = sanitizedSite(site) - os_log("Removing %s from fireproofed sites", sanitizedSite) - PreserveLogins.shared.remove(domain: sanitizedSite) - } - - referenceTests.removeAll() - } private func sanitizedSite(_ site: String) -> String { let url: URL @@ -60,67 +44,42 @@ final class FireButtonReferenceTests: XCTestCase { return url.host! } - func testFireproofing() throws { - // Setup fireproofed sites + func testCookieStorage() { + let preservedLogins = PreserveLogins.shared + preservedLogins.clearAll() + for site in testData.fireButtonFireproofing.fireproofedSites { let sanitizedSite = sanitizedSite(site) os_log("Adding %s to fireproofed sites", sanitizedSite) preservedLogins.addToAllowed(domain: sanitizedSite) - - } - - referenceTests = testData.fireButtonFireproofing.tests.filter { - $0.exceptPlatforms.contains("ios-browser") == false } - let testsExecuted = expectation(description: "tests executed") - testsExecuted.expectedFulfillmentCount = referenceTests.count - - runReferenceTests(onTestExecuted: testsExecuted) - waitForExpectations(timeout: 30, handler: nil) - } - - private func runReferenceTests(onTestExecuted: XCTestExpectation) { - guard let test = referenceTests.popLast() else { - return - } - - guard let cookie = cookie(for: test) else { - XCTFail("Cookie should exist for test \(test.name)") - return + let referenceTests = testData.fireButtonFireproofing.tests.filter { + $0.exceptPlatforms.contains("ios-browser") == false } - - dataStore.cookieStore?.setCookie(cookie, completionHandler: { - let dataStoreIdManager = DataStoreIdManager() - WebCacheManager.shared.clear(logins: self.preservedLogins, dataStoreIdManager: dataStoreIdManager) { + let cookieStorage = CookieStorage() + for test in referenceTests { + guard let cookie = cookie(for: test) else { + XCTFail("Cookie should exist for test \(test.name)") + return + } + + cookieStorage.updateCookies([ + cookie + ], keepingPreservedLogins: preservedLogins) + + let testCookie = cookieStorage.cookies.filter { $0.name == test.cookieName }.first - self.dataStore.cookieStore?.getAllCookies { hotCookies in - let testCookie = hotCookies.filter { $0.name == test.cookieName }.first - - if test.expectCookieRemoved { - XCTAssertNil(testCookie, "Cookie should not exist for test: \(test.name)") - } else { - XCTAssertNotNil(testCookie, "Cookie should exist for test: \(test.name)") - } - - - // Remove all cookies from this test - let group = DispatchGroup() - for cookie in hotCookies { - group.enter() - self.dataStore.cookieStore?.delete(cookie, completionHandler: { - group.leave() - }) - } - - group.notify(queue: .main) { - onTestExecuted.fulfill() - self.runReferenceTests(onTestExecuted: onTestExecuted) - } - } + if test.expectCookieRemoved { + XCTAssertNil(testCookie, "Cookie should not exist for test: \(test.name)") + } else { + XCTAssertNotNil(testCookie, "Cookie should exist for test: \(test.name)") } - }) + + // Reset cache + cookieStorage.cookies = [] + } } private func cookie(for test: Test) -> HTTPCookie? { From ae4dd793ef0577a195edad91b9d5f51017f2f0b4 Mon Sep 17 00:00:00 2001 From: bwaresiak Date: Thu, 25 Jan 2024 15:04:53 +0100 Subject: [PATCH 38/70] Update Release 7.106.0 (#2384) Co-authored-by: Christopher Brind --- Core/CookieStorage.swift | 86 +++++++- Core/WebCacheManager.swift | 15 +- DuckDuckGo.xcodeproj/project.pbxproj | 4 + DuckDuckGoTests/CookieStorageTests.swift | 188 ++++++++++++++++++ .../FireButtonReferenceTests.swift | 93 +++------ 5 files changed, 302 insertions(+), 84 deletions(-) create mode 100644 DuckDuckGoTests/CookieStorageTests.swift diff --git a/Core/CookieStorage.swift b/Core/CookieStorage.swift index 03d6426a22..5624827ee1 100644 --- a/Core/CookieStorage.swift +++ b/Core/CookieStorage.swift @@ -22,16 +22,27 @@ import Foundation public class CookieStorage { - struct Constants { - static let key = "com.duckduckgo.allowedCookies" + struct Keys { + static let allowedCookies = "com.duckduckgo.allowedCookies" + static let consumed = "com.duckduckgo.consumedCookies" } private var userDefaults: UserDefaults - + + var isConsumed: Bool { + get { + userDefaults.bool(forKey: Keys.consumed, defaultValue: false) + } + set { + userDefaults.set(newValue, forKey: Keys.consumed) + } + } + + /// Use the `updateCookies` function rather than the setter which is only visible for testing. var cookies: [HTTPCookie] { get { var storedCookies = [HTTPCookie]() - if let cookies = userDefaults.object(forKey: Constants.key) as? [[String: Any?]] { + if let cookies = userDefaults.object(forKey: Keys.allowedCookies) as? [[String: Any?]] { for cookieData in cookies { var properties = [HTTPCookiePropertyKey: Any]() cookieData.forEach({ @@ -57,17 +68,76 @@ public class CookieStorage { } cookies.append(mappedCookie) } - userDefaults.setValue(cookies, forKey: Constants.key) + userDefaults.setValue(cookies, forKey: Keys.allowedCookies) } + } public init(userDefaults: UserDefaults = UserDefaults.app) { self.userDefaults = userDefaults } - func clear() { - userDefaults.removeObject(forKey: Constants.key) - os_log("cleared cookies", log: .generalLog, type: .debug) + enum CookieDomainsOnUpdate { + case empty + case match + case missing + case different } + + @discardableResult + func updateCookies(_ cookies: [HTTPCookie], keepingPreservedLogins preservedLogins: PreserveLogins) -> CookieDomainsOnUpdate { + isConsumed = false + + let persisted = self.cookies + + func cookiesByDomain(_ cookies: [HTTPCookie]) -> [String: [HTTPCookie]] { + var byDomain = [String: [HTTPCookie]]() + cookies.forEach { cookie in + var cookies = byDomain[cookie.domain, default: []] + cookies.append(cookie) + byDomain[cookie.domain] = cookies + } + return byDomain + } + + let updatedCookiesByDomain = cookiesByDomain(cookies) + var persistedCookiesByDomain = cookiesByDomain(persisted) + // Do the diagnostics before the dicts get changed. + let diagnosticResult = evaluateDomains( + updatedDomains: updatedCookiesByDomain.keys.sorted(), + persistedDomains: persistedCookiesByDomain.keys.sorted() + ) + + updatedCookiesByDomain.keys.forEach { + persistedCookiesByDomain[$0] = updatedCookiesByDomain[$0] + } + + persistedCookiesByDomain.keys.forEach { + guard $0 != "duckduckgo.com" else { return } // DDG cookies are for SERP settings only + + if !preservedLogins.isAllowed(cookieDomain: $0) { + persistedCookiesByDomain.removeValue(forKey: $0) + } + } + + let now = Date() + self.cookies = persistedCookiesByDomain.map { $0.value }.joined().compactMap { $0 } + .filter { $0.expiresDate == nil || $0.expiresDate! > now } + + return diagnosticResult + } + + private func evaluateDomains(updatedDomains: [String], persistedDomains: [String]) -> CookieDomainsOnUpdate { + if persistedDomains.isEmpty { + return .empty + } else if updatedDomains.count < persistedDomains.count { + return .missing + } else if updatedDomains == persistedDomains { + return .match + } else { + return .different + } + } + } diff --git a/Core/WebCacheManager.swift b/Core/WebCacheManager.swift index bafedec89b..2c66cdd06b 100644 --- a/Core/WebCacheManager.swift +++ b/Core/WebCacheManager.swift @@ -74,7 +74,7 @@ public class WebCacheManager { let cookies = cookieStorage.cookies - guard !cookies.isEmpty else { + guard !cookies.isEmpty, !cookieStorage.isConsumed else { completion() return } @@ -93,9 +93,9 @@ public class WebCacheManager { DispatchQueue.global(qos: .userInitiated).async { group.wait() + cookieStorage.isConsumed = true DispatchQueue.main.async { - cookieStorage.clear() completion() if cookieStorage.cookies.count > 0 { @@ -162,7 +162,7 @@ public class WebCacheManager { logins: PreserveLogins, storeIdManager: DataStoreIdManager, completion: @escaping () -> Void) { - + guard let containerId = storeIdManager.id else { completion() return @@ -181,11 +181,8 @@ public class WebCacheManager { await checkForLeftBehindDataStores() storeIdManager.allocateNewContainerId() - // If cookies is empty it's likely that the webview was not used since the last fire button so - // don't overwrite previously saved cookies - if let cookies, !cookies.isEmpty { - cookieStorage.cookies = cookies - } + + cookieStorage.updateCookies(cookies ?? [], keepingPreservedLogins: logins) completion() } @@ -208,7 +205,7 @@ public class WebCacheManager { // From this point onwards... use containers dataStoreIdManager.allocateNewContainerId() Task { @MainActor in - cookieStorage.cookies = cookies + cookieStorage.updateCookies(cookies, keepingPreservedLogins: logins) completion() } } else { diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 5ce8407ae1..21186a57c8 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -459,6 +459,7 @@ 85A1B3B220C6CD9900C18F15 /* CookieStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85A1B3B120C6CD9900C18F15 /* CookieStorage.swift */; }; 85A313972028E78A00327D00 /* release_notes.txt in Resources */ = {isa = PBXBuildFile; fileRef = 85A313962028E78A00327D00 /* release_notes.txt */; }; 85A9C37920E0E00C00073340 /* HomeRow.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 85A9C37820E0E00C00073340 /* HomeRow.xcassets */; }; + 85AD49EE2B6149110085D2D1 /* CookieStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85AD49ED2B6149110085D2D1 /* CookieStorageTests.swift */; }; 85AE668E2097206E0014CF04 /* NotificationView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 85AE668D2097206E0014CF04 /* NotificationView.xib */; }; 85AE6690209724120014CF04 /* NotificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85AE668F209724120014CF04 /* NotificationView.swift */; }; 85AFA1212B45D14F0028A504 /* BookmarksMigrationAssertionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85AFA1202B45D14F0028A504 /* BookmarksMigrationAssertionTests.swift */; }; @@ -1541,6 +1542,7 @@ 85A313962028E78A00327D00 /* release_notes.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = release_notes.txt; path = fastlane/metadata/default/release_notes.txt; sourceTree = ""; }; 85A53EC9200D1FA20010D13F /* FileStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileStore.swift; sourceTree = ""; }; 85A9C37820E0E00C00073340 /* HomeRow.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = HomeRow.xcassets; sourceTree = ""; }; + 85AD49ED2B6149110085D2D1 /* CookieStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CookieStorageTests.swift; sourceTree = ""; }; 85AE668D2097206E0014CF04 /* NotificationView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NotificationView.xib; sourceTree = ""; }; 85AE668F209724120014CF04 /* NotificationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationView.swift; sourceTree = ""; }; 85AFA1202B45D14F0028A504 /* BookmarksMigrationAssertionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksMigrationAssertionTests.swift; sourceTree = ""; }; @@ -5274,6 +5276,7 @@ 8540BD5123D8C2220057FDD2 /* PreserveLoginsTests.swift */, 850559D123CF710C0055C0D5 /* WebCacheManagerTests.swift */, F198D7971E3A45D90088DA8A /* WKWebViewConfigurationExtensionTests.swift */, + 85AD49ED2B6149110085D2D1 /* CookieStorageTests.swift */, ); name = Web; sourceTree = ""; @@ -7015,6 +7018,7 @@ 8521FDE6238D414B00A44CC3 /* FileStoreTests.swift in Sources */, F14E491F1E391CE900DC037C /* URLExtensionTests.swift in Sources */, 85D2187424BF25CD004373D2 /* FaviconsTests.swift in Sources */, + 85AD49EE2B6149110085D2D1 /* CookieStorageTests.swift in Sources */, CBCCF96828885DEE006F4A71 /* AppPrivacyConfigurationTests.swift in Sources */, 310742AB2848E6FD0012660B /* BackForwardMenuHistoryItemURLSanitizerTests.swift in Sources */, 22CB1ED8203DDD2C00D2C724 /* AppDeepLinksTests.swift in Sources */, diff --git a/DuckDuckGoTests/CookieStorageTests.swift b/DuckDuckGoTests/CookieStorageTests.swift new file mode 100644 index 0000000000..79b0dc2751 --- /dev/null +++ b/DuckDuckGoTests/CookieStorageTests.swift @@ -0,0 +1,188 @@ +// +// CookieStorageTests.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 XCTest +@testable import Core +import WebKit + +public class CookieStorageTests: XCTestCase { + + var storage: CookieStorage! + + // This is updated by the `make` function which preserves any cookies added as part of this test + let logins = PreserveLogins.shared + + static let userDefaultsSuiteName = "test" + + public override func setUp() { + super.setUp() + let defaults = UserDefaults(suiteName: Self.userDefaultsSuiteName)! + defaults.removePersistentDomain(forName: Self.userDefaultsSuiteName) + storage = CookieStorage(userDefaults: defaults) + logins.clearAll() + } + + func testWhenUpdatedThenDuckDuckGoCookiesAreNotRemoved() { + storage.updateCookies([ + make("duckduckgo.com", name: "x", value: "1"), + ], keepingPreservedLogins: logins) + + XCTAssertEqual(1, storage.cookies.count) + + storage.updateCookies([ + make("test.com", name: "x", value: "1"), + ], keepingPreservedLogins: logins) + + XCTAssertEqual(2, storage.cookies.count) + + } + + func testWhenUpdatedThenCookiesWithFutureExpirationAreNotRemoved() { + storage.updateCookies([ + make("test.com", name: "x", value: "1", expires: .distantFuture), + ], keepingPreservedLogins: logins) + + storage.updateCookies([ + make("example.com", name: "x", value: "1"), + ], keepingPreservedLogins: logins) + + XCTAssertEqual(2, storage.cookies.count) + XCTAssertTrue(storage.cookies.contains(where: { $0.domain == "test.com" })) + XCTAssertTrue(storage.cookies.contains(where: { $0.domain == "example.com" })) + + } + + func testWhenUpdatingThenExistingExpiredCookiesAreRemoved() { + storage.cookies = [ + make("test.com", name: "x", value: "1", expires: Date(timeIntervalSinceNow: -100)), + ] + XCTAssertEqual(1, storage.cookies.count) + + storage.updateCookies([ + make("example.com", name: "x", value: "1"), + ], keepingPreservedLogins: logins) + + XCTAssertEqual(1, storage.cookies.count) + XCTAssertFalse(storage.cookies.contains(where: { $0.domain == "test.com" })) + XCTAssertTrue(storage.cookies.contains(where: { $0.domain == "example.com" })) + + } + + func testWhenExpiredCookieIsAddedThenItIsNotPersisted() { + + storage.updateCookies([ + make("example.com", name: "x", value: "1", expires: Date(timeIntervalSinceNow: -100)), + ], keepingPreservedLogins: logins) + + XCTAssertEqual(0, storage.cookies.count) + + } + + func testWhenUpdatedThenNoLongerPreservedDomainsAreCleared() { + storage.updateCookies([ + make("test.com", name: "x", value: "1"), + make("example.com", name: "x", value: "1"), + ], keepingPreservedLogins: logins) + + logins.remove(domain: "test.com") + + storage.updateCookies([ + make("example.com", name: "x", value: "1"), + ], keepingPreservedLogins: logins) + + XCTAssertEqual(1, storage.cookies.count) + XCTAssertFalse(storage.cookies.contains(where: { $0.domain == "test.com" })) + XCTAssertTrue(storage.cookies.contains(where: { $0.domain == "example.com" })) + } + + func testWhenStorageInitialiedThenItIsEmptyAndConsumedIsFalse() { + XCTAssertEqual(0, storage.cookies.count) + XCTAssertEqual(false, storage.isConsumed) + } + + func testWhenStorageIsUpdatedThenConsumedIsResetToFalse() { + storage.isConsumed = true + XCTAssertTrue(storage.isConsumed) + storage.updateCookies([ + make("test.com", name: "x", value: "1") + ], keepingPreservedLogins: logins) + XCTAssertFalse(storage.isConsumed) + } + + func testWhenStorageIsReinstanciatedThenUsesStoredData() { + storage.updateCookies([ + make("test.com", name: "x", value: "1") + ], keepingPreservedLogins: logins) + storage.isConsumed = true + + let otherStorage = CookieStorage(userDefaults: UserDefaults(suiteName: Self.userDefaultsSuiteName)!) + XCTAssertEqual(1, otherStorage.cookies.count) + XCTAssertTrue(otherStorage.isConsumed) + } + + func testWhenStorageIsUpdatedThenUpdatingAddsNewCookies() { + storage.updateCookies([ + make("test.com", name: "x", value: "1") + ], keepingPreservedLogins: logins) + XCTAssertEqual(1, storage.cookies.count) + } + + func testWhenStorageIsUpdatedThenExistingCookiesAreUnaffected() { + storage.updateCookies([ + make("test.com", name: "x", value: "1"), + make("example.com", name: "x", value: "1"), + ], keepingPreservedLogins: logins) + + storage.updateCookies([ + make("example.com", name: "x", value: "2"), + ], keepingPreservedLogins: logins) + + XCTAssertEqual(2, storage.cookies.count) + XCTAssertTrue(storage.cookies.contains(where: { $0.domain == "test.com" && $0.name == "x" && $0.value == "1" })) + XCTAssertTrue(storage.cookies.contains(where: { $0.domain == "example.com" && $0.name == "x" && $0.value == "2" })) + } + + func testWhenStorageHasMatchingDOmainThenUpdatingReplacesCookies() { + storage.updateCookies([ + make("test.com", name: "x", value: "1") + ], keepingPreservedLogins: logins) + + storage.updateCookies([ + make("test.com", name: "x", value: "2"), + make("test.com", name: "y", value: "3"), + ], keepingPreservedLogins: logins) + + XCTAssertEqual(2, storage.cookies.count) + XCTAssertFalse(storage.cookies.contains(where: { $0.domain == "test.com" && $0.name == "x" && $0.value == "1" })) + XCTAssertTrue(storage.cookies.contains(where: { $0.domain == "test.com" && $0.name == "x" && $0.value == "2" })) + XCTAssertTrue(storage.cookies.contains(where: { $0.domain == "test.com" && $0.name == "y" && $0.value == "3" })) + } + + func make(_ domain: String, name: String, value: String, expires: Date? = nil) -> HTTPCookie { + logins.addToAllowed(domain: domain) + return HTTPCookie(properties: [ + .domain: domain, + .name: name, + .value: value, + .path: "/", + .expires: expires as Any + ])! + } + +} diff --git a/DuckDuckGoTests/FireButtonReferenceTests.swift b/DuckDuckGoTests/FireButtonReferenceTests.swift index 2d81f4d26d..2f04f6fbc3 100644 --- a/DuckDuckGoTests/FireButtonReferenceTests.swift +++ b/DuckDuckGoTests/FireButtonReferenceTests.swift @@ -23,9 +23,6 @@ import WebKit @testable import Core final class FireButtonReferenceTests: XCTestCase { - private var referenceTests = [Test]() - private let preservedLogins = PreserveLogins.shared - private let dataStore = WKWebsiteDataStore.default() private enum Resource { static let tests = "privacy-reference-tests/storage-clearing/tests.json" @@ -36,19 +33,6 @@ final class FireButtonReferenceTests: XCTestCase { // swiftlint:disable:next force_try return try! JSONDecoder().decode(TestData.self, from: testJSON) }() - - override func tearDownWithError() throws { - try super.tearDownWithError() - - // Remove fireproofed sites - for site in testData.fireButtonFireproofing.fireproofedSites { - let sanitizedSite = sanitizedSite(site) - os_log("Removing %s from fireproofed sites", sanitizedSite) - PreserveLogins.shared.remove(domain: sanitizedSite) - } - - referenceTests.removeAll() - } private func sanitizedSite(_ site: String) -> String { let url: URL @@ -60,67 +44,42 @@ final class FireButtonReferenceTests: XCTestCase { return url.host! } - func testFireproofing() throws { - // Setup fireproofed sites + func testCookieStorage() { + let preservedLogins = PreserveLogins.shared + preservedLogins.clearAll() + for site in testData.fireButtonFireproofing.fireproofedSites { let sanitizedSite = sanitizedSite(site) os_log("Adding %s to fireproofed sites", sanitizedSite) preservedLogins.addToAllowed(domain: sanitizedSite) - - } - - referenceTests = testData.fireButtonFireproofing.tests.filter { - $0.exceptPlatforms.contains("ios-browser") == false } - let testsExecuted = expectation(description: "tests executed") - testsExecuted.expectedFulfillmentCount = referenceTests.count - - runReferenceTests(onTestExecuted: testsExecuted) - waitForExpectations(timeout: 30, handler: nil) - } - - private func runReferenceTests(onTestExecuted: XCTestExpectation) { - guard let test = referenceTests.popLast() else { - return - } - - guard let cookie = cookie(for: test) else { - XCTFail("Cookie should exist for test \(test.name)") - return + let referenceTests = testData.fireButtonFireproofing.tests.filter { + $0.exceptPlatforms.contains("ios-browser") == false } - - dataStore.cookieStore?.setCookie(cookie, completionHandler: { - let dataStoreIdManager = DataStoreIdManager() - WebCacheManager.shared.clear(logins: self.preservedLogins, dataStoreIdManager: dataStoreIdManager) { + let cookieStorage = CookieStorage() + for test in referenceTests { + guard let cookie = cookie(for: test) else { + XCTFail("Cookie should exist for test \(test.name)") + return + } + + cookieStorage.updateCookies([ + cookie + ], keepingPreservedLogins: preservedLogins) + + let testCookie = cookieStorage.cookies.filter { $0.name == test.cookieName }.first - self.dataStore.cookieStore?.getAllCookies { hotCookies in - let testCookie = hotCookies.filter { $0.name == test.cookieName }.first - - if test.expectCookieRemoved { - XCTAssertNil(testCookie, "Cookie should not exist for test: \(test.name)") - } else { - XCTAssertNotNil(testCookie, "Cookie should exist for test: \(test.name)") - } - - - // Remove all cookies from this test - let group = DispatchGroup() - for cookie in hotCookies { - group.enter() - self.dataStore.cookieStore?.delete(cookie, completionHandler: { - group.leave() - }) - } - - group.notify(queue: .main) { - onTestExecuted.fulfill() - self.runReferenceTests(onTestExecuted: onTestExecuted) - } - } + if test.expectCookieRemoved { + XCTAssertNil(testCookie, "Cookie should not exist for test: \(test.name)") + } else { + XCTAssertNotNil(testCookie, "Cookie should exist for test: \(test.name)") } - }) + + // Reset cache + cookieStorage.cookies = [] + } } private func cookie(for test: Test) -> HTTPCookie? { From 4a8e499bd9494ee954718642ae483dd3909dd815 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacek=20=C5=81yp?= Date: Thu, 25 Jan 2024 15:49:56 +0100 Subject: [PATCH 39/70] Coldfix release changes --- scripts/prepare_release.sh | 92 +++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 40 deletions(-) diff --git a/scripts/prepare_release.sh b/scripts/prepare_release.sh index cfc87c38dc..d643f1c0bf 100755 --- a/scripts/prepare_release.sh +++ b/scripts/prepare_release.sh @@ -56,7 +56,6 @@ print_usage_and_exit() { Options: -h Make hotfix release. Requires the version to be the one to hotfix, and a branch with the fix as the second parameter - -c Make coldfix release (i.e. a new build number on an existing release). Requires the version to be the one to coldfix, and a branch with the fix as the second parameter -v Enable verbose mode EOF @@ -81,22 +80,15 @@ read_command_line_arguments() { fi fi - branch_name="release" + release_branch_prefix="release" - while getopts 'hcv' option; do + while getopts 'hv' option; do case "${option}" in - h) + h) # hotfix is_hotfix=1 - branch_name="hotfix" - fix_type_name="hotfix" + release_branch_prefix="hotfix" ;; - c) - is_hotfix=1 - is_coldfix=1 - branch_name="coldfix" - fix_type_name="coldfix" - ;; - v) + v) # verbose mute= ;; *) @@ -105,6 +97,13 @@ read_command_line_arguments() { esac done + release_branch="${release_branch_prefix}/${version}" + changes_branch="${release_branch}-changes" + + if release_branch_exists; then + is_subsequent_release=1 + fi + shift $((OPTIND-1)) if [[ $is_hotfix ]]; then @@ -113,15 +112,18 @@ read_command_line_arguments() { fi version_to_hotfix=${version} - if ! [[ $is_coldfix ]]; then - IFS='.' read -ra arrIN <<< "$version" - patch_number=$((arrIN[2] + 1)) - version="${arrIN[0]}.${arrIN[1]}.$patch_number" - fi + IFS='.' read -ra arrIN <<< "$version" + patch_number=$((arrIN[2] + 1)) + version="${arrIN[0]}.${arrIN[1]}.$patch_number" fi +} - release_branch="${branch_name}/${version}" - changes_branch="${release_branch}-changes" +release_branch_exists() { + if git show-ref --verify --quiet "refs/heads/$release_branch"; then + return 0 + else + return 1 + fi } stash() { @@ -140,10 +142,7 @@ assert_clean_state() { } assert_hotfix_tag_exists_if_necessary() { - if [[ ! $is_hotfix ]]; then - return - fi - printf '%s' "Checking tag to ${fix_type_name} ... " + printf '%s' "Checking tag to hotfix ... " # Make sure tag is available locally if it exists eval git fetch origin "+refs/tags/${tag}:refs/tags/${tag}" "$mute" @@ -155,10 +154,9 @@ assert_hotfix_tag_exists_if_necessary() { fi } -create_release_branch() { +create_release_and_changes_branches() { if [[ ${is_hotfix} ]]; then - printf '%s' "Creating ${fix_type_name} branch ... " - + printf '%s' "Creating hotfix branch ... " eval git checkout "${hotfix_branch_parent}" "$mute" else printf '%s' "Creating release branch ... " @@ -170,10 +168,15 @@ create_release_branch() { echo "✅" } +create_changes_branch() { + printf '%s' "Creating changes branch ... " + eval git checkout "${release_branch}" "$mute" + eval git pull "$mute" + eval git checkout -b "${changes_branch}" "$mute" + echo "✅" +} + update_marketing_version() { - if [[ $is_coldfix ]]; then - return - fi printf '%s' "Setting app version ... " "$script_dir/set_version.sh" "${version}" git add "${base_dir}/Configuration/Version.xcconfig" \ @@ -241,7 +244,9 @@ merge_fix_branch_if_necessary() { create_pull_request() { printf '%s' "Creating PR ... " - eval git push origin "${release_branch}" "$mute" + if [[ ! $is_subsequent_release ]]; then + eval git push origin "${release_branch}" "$mute" + fi eval git push origin "${changes_branch}" "$mute" eval gh pr create --title \"Release "${version}"\" --base "${release_branch}" --assignee @me "$mute" --body-file "${script_dir}/assets/prepare-release-description" eval gh pr view --web "$mute" @@ -256,20 +261,27 @@ main() { read_command_line_arguments "$@" stash - assert_clean_state - assert_hotfix_tag_exists_if_necessary - create_release_branch - - update_marketing_version - update_build_version - if ! [[ $is_hotfix ]]; then + if [[ $is_subsequent_release ]]; then + create_changes_branch + elif [[ $is_hotfix ]]; then + assert_clean_state + assert_hotfix_tag_exists_if_necessary + create_release_and_changes_branches + update_marketing_version + else + assert_clean_state + create_release_and_changes_branches + update_marketing_version update_embedded_files fi + + update_build_version + update_release_notes merge_fix_branch_if_necessary - create_pull_request + #create_pull_request } -main "$@" +main "$@" \ No newline at end of file From 3c68b88b28b299f4f0416685fcdbaacc6b0b4ab2 Mon Sep 17 00:00:00 2001 From: bwaresiak Date: Thu, 25 Jan 2024 17:01:55 +0100 Subject: [PATCH 40/70] Release 7.106.0-1 (#2386) --- DuckDuckGo.xcodeproj/project.pbxproj | 56 ++++++++++++++-------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 21186a57c8..eca51a2c21 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -8163,7 +8163,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProvider.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -8200,7 +8200,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8292,7 +8292,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8320,7 +8320,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8470,7 +8470,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8496,7 +8496,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; INFOPLIST_FILE = DuckDuckGo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8561,7 +8561,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -8596,7 +8596,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8630,7 +8630,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8661,7 +8661,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8948,7 +8948,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8979,7 +8979,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -9008,7 +9008,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -9042,7 +9042,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -9073,7 +9073,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProvider.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -9106,11 +9106,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 0; + DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9348,7 +9348,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGoAlpha.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9375,7 +9375,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9408,7 +9408,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9446,7 +9446,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9482,7 +9482,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9517,11 +9517,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 0; + DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9695,11 +9695,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 0; + DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9728,10 +9728,10 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 0; + DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; From 4c64dfe2b48c7a2d48aceccff05933cae2ee3f24 Mon Sep 17 00:00:00 2001 From: Diego Rey Mendez Date: Thu, 25 Jan 2024 17:51:27 +0100 Subject: [PATCH 41/70] Remove connection status awaiter (#2387) Task/Issue URL: https://app.asana.com/0/0/1206443966711576/f macOS PR: https://github.com/duckduckgo/macos-browser/pull/2121 BSK PR: https://github.com/duckduckgo/BrowserServicesKit/pull/635 ## Description Updates BSK to the latest version, with no changes for iOS. --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- LocalPackages/DuckUI/Package.swift | 2 +- LocalPackages/SyncUI/Package.swift | 2 +- LocalPackages/Waitlist/Package.swift | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 3c21818caf..a835b697ba 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -9978,7 +9978,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 102.0.0; + version = 102.0.1; }; }; C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index b09013884d..9f1e2134cb 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "02ef8a68c623cd993adbebbf97646ff05d40593b", - "version" : "102.0.0" + "revision" : "93fa38a7412fd665ed2f72df0fd26f69e61c9fe1", + "version" : "102.0.1" } }, { diff --git a/LocalPackages/DuckUI/Package.swift b/LocalPackages/DuckUI/Package.swift index e1418d52bf..8ab8ba2024 100644 --- a/LocalPackages/DuckUI/Package.swift +++ b/LocalPackages/DuckUI/Package.swift @@ -31,7 +31,7 @@ let package = Package( targets: ["DuckUI"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "102.0.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "102.0.1"), ], targets: [ .target( diff --git a/LocalPackages/SyncUI/Package.swift b/LocalPackages/SyncUI/Package.swift index f9583eb012..e025df8011 100644 --- a/LocalPackages/SyncUI/Package.swift +++ b/LocalPackages/SyncUI/Package.swift @@ -33,7 +33,7 @@ let package = Package( ], dependencies: [ .package(path: "../DuckUI"), - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "102.0.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "102.0.1"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ diff --git a/LocalPackages/Waitlist/Package.swift b/LocalPackages/Waitlist/Package.swift index e74a9dfab1..19bdc911b9 100644 --- a/LocalPackages/Waitlist/Package.swift +++ b/LocalPackages/Waitlist/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["Waitlist", "WaitlistMocks"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "102.0.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "102.0.1"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ From eb90ec65c2138a483454a169b0e8da747cea0991 Mon Sep 17 00:00:00 2001 From: Anh Do <18567+quanganhdo@users.noreply.github.com> Date: Thu, 25 Jan 2024 20:32:31 -0500 Subject: [PATCH 42/70] Show last NetP disconnect error in Debug view (#2390) --- .../Feedback/VPNMetadataCollector.swift | 2 +- ...NetworkProtectionDebugViewController.swift | 28 +++++++++++++++++++ DuckDuckGo/en.lproj/Localizable.strings | 12 -------- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/DuckDuckGo/Feedback/VPNMetadataCollector.swift b/DuckDuckGo/Feedback/VPNMetadataCollector.swift index 3ab9f25421..d1daf056bb 100644 --- a/DuckDuckGo/Feedback/VPNMetadataCollector.swift +++ b/DuckDuckGo/Feedback/VPNMetadataCollector.swift @@ -195,7 +195,7 @@ final class DefaultVPNMetadataCollector: VPNMetadataCollector { connectedServerIP: connectedServerIP) } - private func lastDisconnectError() async -> String { + public func lastDisconnectError() async -> String { if #available(iOS 16, *) { guard let tunnelManager = try? await NETunnelProviderManager.loadAllFromPreferences().first else { return "none" diff --git a/DuckDuckGo/NetworkProtectionDebugViewController.swift b/DuckDuckGo/NetworkProtectionDebugViewController.swift index e50c45e17f..55c4445f64 100644 --- a/DuckDuckGo/NetworkProtectionDebugViewController.swift +++ b/DuckDuckGo/NetworkProtectionDebugViewController.swift @@ -43,6 +43,7 @@ final class NetworkProtectionDebugViewController: UITableViewController { Sections.registrationKey: "Registration Key", Sections.notifications: "Notifications", Sections.networkPath: "Network Path", + Sections.lastDisconnectError: "Last Disconnect Error", Sections.connectionTest: "Connection Test", Sections.vpnConfiguration: "VPN Configuration" @@ -56,6 +57,7 @@ final class NetworkProtectionDebugViewController: UITableViewController { case notifications case connectionTest case networkPath + case lastDisconnectError case vpnConfiguration } @@ -90,6 +92,10 @@ final class NetworkProtectionDebugViewController: UITableViewController { case networkPath } + enum LastDisconnectErrorRows: Int, CaseIterable { + case lastDisconnectError + } + enum ConnectionTestRows: Int, CaseIterable { case runConnectionTest } @@ -106,6 +112,7 @@ final class NetworkProtectionDebugViewController: UITableViewController { private let pathMonitor = NWPathMonitor() private var currentNetworkPath: String? + private var lastDisconnectError: String? private var baseConfigurationData: String? private var fullProtocolConfigurationData: String? @@ -138,6 +145,7 @@ final class NetworkProtectionDebugViewController: UITableViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + loadLastDisconnectError() loadConfigurationData() startPathMonitor() } @@ -188,6 +196,9 @@ final class NetworkProtectionDebugViewController: UITableViewController { case .networkPath: configure(cell, forNetworkPathRow: indexPath.row) + case .lastDisconnectError: + configure(cell, forLastDisconnectErrorRow: indexPath.row) + case .connectionTest: configure(cell, forConnectionTestRow: indexPath.row) @@ -209,6 +220,7 @@ final class NetworkProtectionDebugViewController: UITableViewController { case .registrationKey: return RegistrationKeyRows.allCases.count case .notifications: return NotificationsRows.allCases.count case .networkPath: return NetworkPathRows.allCases.count + case .lastDisconnectError: return LastDisconnectErrorRows.allCases.count case .connectionTest: return ConnectionTestRows.allCases.count + connectionTestResults.count case .vpnConfiguration: return ConfigurationRows.allCases.count case .none: return 0 @@ -235,6 +247,8 @@ final class NetworkProtectionDebugViewController: UITableViewController { didSelectTestNotificationAction(at: indexPath) case .networkPath: break + case .lastDisconnectError: + break case .connectionTest: if indexPath.row == connectionTestResults.count { Task { @@ -394,6 +408,20 @@ final class NetworkProtectionDebugViewController: UITableViewController { pathMonitor.start(queue: .main) } + // MARK: Last disconnect error + + private func configure(_ cell: UITableViewCell, forLastDisconnectErrorRow row: Int) { + cell.textLabel?.font = .monospacedSystemFont(ofSize: 13.0, weight: .regular) + cell.textLabel?.text = lastDisconnectError ?? "Loading Last Disconnect Error..." + } + + private func loadLastDisconnectError() { + Task { @MainActor in + lastDisconnectError = await DefaultVPNMetadataCollector().lastDisconnectError() + tableView.reloadData() + } + } + // MARK: Connection Test private func configure(_ cell: UITableViewCell, forConnectionTestRow row: Int) { diff --git a/DuckDuckGo/en.lproj/Localizable.strings b/DuckDuckGo/en.lproj/Localizable.strings index 91a2b13227..815afd61b6 100644 --- a/DuckDuckGo/en.lproj/Localizable.strings +++ b/DuckDuckGo/en.lproj/Localizable.strings @@ -2067,18 +2067,6 @@ But if you *do* want a peek under the hood, you can find more information about /* Subscription Expiration Data */ "subscription.subscription.active.caption" = "Your Privacy Pro subscription renews on %@"; -/* Cancel action for the existing subscription dialog */ -"subscription.subscription.found.cancel" = "Cancel"; - -/* Restore action for the existing subscription dialog */ -"subscription.subscription.found.restore" = "Restore"; - -/* Message for the existing subscription dialog */ -"subscription.subscription.found.text" = "We found a subscription associated with this Apple ID."; - -/* Title for the existing subscription dialog */ -"subscription.subscription.found.title" = "Subscription Found"; - /* Message confirming that recovery code was copied to clipboard */ "sync.code.copied" = "Recovery code copied to clipboard"; From 7c96384ef799b6226f5892122f393e284b9ef765 Mon Sep 17 00:00:00 2001 From: Dominik Kapusta Date: Fri, 26 Jan 2024 14:57:44 +0100 Subject: [PATCH 43/70] Don't report CancellationError from BookmarksFaviconsFetcher (#2385) Task/Issue URL: https://app.asana.com/0/1201493110486074/1206407973669779/f Description: Keep cancelling operations when requested, but without reporting the cancellation to errorEvents, which could have caused them being reported as Pixels. --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- LocalPackages/DuckUI/Package.swift | 2 +- LocalPackages/SyncUI/Package.swift | 2 +- LocalPackages/Waitlist/Package.swift | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index a835b697ba..37e55d037b 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -9978,7 +9978,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 102.0.1; + version = 102.0.2; }; }; C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 9f1e2134cb..946e0c3b86 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "93fa38a7412fd665ed2f72df0fd26f69e61c9fe1", - "version" : "102.0.1" + "revision" : "072df96b846e3350b23ab32a61b6c1a0983b5e85", + "version" : "102.0.2" } }, { diff --git a/LocalPackages/DuckUI/Package.swift b/LocalPackages/DuckUI/Package.swift index 8ab8ba2024..604624a245 100644 --- a/LocalPackages/DuckUI/Package.swift +++ b/LocalPackages/DuckUI/Package.swift @@ -31,7 +31,7 @@ let package = Package( targets: ["DuckUI"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "102.0.1"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "102.0.2"), ], targets: [ .target( diff --git a/LocalPackages/SyncUI/Package.swift b/LocalPackages/SyncUI/Package.swift index e025df8011..727d00e286 100644 --- a/LocalPackages/SyncUI/Package.swift +++ b/LocalPackages/SyncUI/Package.swift @@ -33,7 +33,7 @@ let package = Package( ], dependencies: [ .package(path: "../DuckUI"), - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "102.0.1"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "102.0.2"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ diff --git a/LocalPackages/Waitlist/Package.swift b/LocalPackages/Waitlist/Package.swift index 19bdc911b9..70220d0c31 100644 --- a/LocalPackages/Waitlist/Package.swift +++ b/LocalPackages/Waitlist/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["Waitlist", "WaitlistMocks"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "102.0.1"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "102.0.2"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ From 56da83904f8c9c666b199201274f458a8e9b33d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mariusz=20=C5=9Apiewak?= Date: Fri, 26 Jan 2024 15:34:26 +0100 Subject: [PATCH 44/70] Update emailprotection e2e test (#2392) --- .maestro/release_tests/emailprotection.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.maestro/release_tests/emailprotection.yaml b/.maestro/release_tests/emailprotection.yaml index e4cc61fba4..4b2526dbbf 100644 --- a/.maestro/release_tests/emailprotection.yaml +++ b/.maestro/release_tests/emailprotection.yaml @@ -18,7 +18,9 @@ tags: - scroll - assertVisible: Email Protection - tapOn: Email Protection +- assertVisible: Email privacy, simplified. - assertVisible: id: searchEntry +- tapOn: + id: "searchEntry" - assertVisible: https://duckduckgo.com/email/ -- assertVisible: Email privacy, simplified. \ No newline at end of file From 3847665b5eae317a199978f2bcb4a364758d562e Mon Sep 17 00:00:00 2001 From: Dominik Kapusta Date: Mon, 29 Jan 2024 10:27:46 +0100 Subject: [PATCH 45/70] Skip 'Make Release Build' GHA job for Dependabot PRs (#2399) Task/Issue URL: https://app.asana.com/0/1203301625297703/1206459737235162 --- .github/workflows/pr.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index e145b145a3..bdbdb037cc 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -128,6 +128,9 @@ jobs: name: Make Release Build + # Dependabot doesn't have access to all secrets, so we skip this job + if: github.actor != 'dependabot[bot]' + runs-on: macos-13-xlarge timeout-minutes: 30 From dfee5121163d2c93832fd9718ba2679a5b1a94d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 11:13:33 +0100 Subject: [PATCH 46/70] Bump submodules/privacy-reference-tests from `a3acc21` to `6b7ad1e` (#2382) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- submodules/privacy-reference-tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/privacy-reference-tests b/submodules/privacy-reference-tests index a3acc21947..6b7ad1e7f1 160000 --- a/submodules/privacy-reference-tests +++ b/submodules/privacy-reference-tests @@ -1 +1 @@ -Subproject commit a3acc2194758bec0f01f57dd0c5f106de01a354e +Subproject commit 6b7ad1e7f15270f9dfeb58a272199f4d57c3eb22 From 75e98e4d0032eb744add8f9ce363ea2894de682f Mon Sep 17 00:00:00 2001 From: Fernando Bunn Date: Mon, 29 Jan 2024 11:06:35 +0000 Subject: [PATCH 47/70] Fix voice search settings (#2401) Task/Issue URL: https://app.asana.com/0/414709148257752/1206454436350674/f Cherry picked from: #2394 Description: Fix voice-search toggle in Settings See parent task for steps to reproduce the issue --- DuckDuckGo/SettingsCustomizeView.swift | 2 +- DuckDuckGo/SettingsState.swift | 4 ++-- DuckDuckGo/SettingsViewModel.swift | 16 ++++++++++------ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/DuckDuckGo/SettingsCustomizeView.swift b/DuckDuckGo/SettingsCustomizeView.swift index 034b268090..6570427420 100644 --- a/DuckDuckGo/SettingsCustomizeView.swift +++ b/DuckDuckGo/SettingsCustomizeView.swift @@ -37,7 +37,7 @@ struct SettingsCustomizeView: View { SettingsCellView(label: UserText.settingsAutocomplete, accesory: .toggle(isOn: viewModel.autocompleteBinding)) - if viewModel.state.speechRecognitionEnabled { + if viewModel.state.speechRecognitionAvailable { SettingsCellView(label: UserText.settingsVoiceSearch, accesory: .toggle(isOn: viewModel.voiceSearchEnabledBinding)) } diff --git a/DuckDuckGo/SettingsState.swift b/DuckDuckGo/SettingsState.swift index 9d9ff181a5..003db608a0 100644 --- a/DuckDuckGo/SettingsState.swift +++ b/DuckDuckGo/SettingsState.swift @@ -78,7 +78,7 @@ struct SettingsState { // Features var debugModeEnabled: Bool var voiceSearchEnabled: Bool - var speechRecognitionEnabled: Bool + var speechRecognitionAvailable: Bool // Returns if the device has speech recognition available var loginsEnabled: Bool // Network Protection properties @@ -108,7 +108,7 @@ struct SettingsState { version: "0.0.0.0", debugModeEnabled: false, voiceSearchEnabled: false, - speechRecognitionEnabled: false, + speechRecognitionAvailable: false, loginsEnabled: false, networkProtection: NetworkProtection(enabled: false, status: ""), subscription: Subscription(enabled: false, canPurchase: false, diff --git a/DuckDuckGo/SettingsViewModel.swift b/DuckDuckGo/SettingsViewModel.swift index bff53aae81..d1cb74af05 100644 --- a/DuckDuckGo/SettingsViewModel.swift +++ b/DuckDuckGo/SettingsViewModel.swift @@ -43,6 +43,7 @@ final class SettingsViewModel: ObservableObject { private var legacyViewProvider: SettingsLegacyViewProvider private lazy var versionProvider: AppVersion = AppVersion.shared private var accountManager: AccountManager + private let voiceSearchHelper: VoiceSearchHelperProtocol #if NETWORK_PROTECTION private let connectionObserver = ConnectionStatusObserverThroughSession() @@ -148,7 +149,7 @@ final class SettingsViewModel: ObservableObject { self.enableVoiceSearch { [weak self] result in DispatchQueue.main.async { self?.state.voiceSearchEnabled = result - self?.appSettings.voiceSearchEnabled = result + self?.voiceSearchHelper.enableVoiceSearch(true) if !result { // Permission is denied self?.shouldShowNoMicrophonePermissionAlert = true @@ -156,7 +157,7 @@ final class SettingsViewModel: ObservableObject { } } } else { - self.appSettings.voiceSearchEnabled = false + self.voiceSearchHelper.enableVoiceSearch(false) self.state.voiceSearchEnabled = false } } @@ -183,10 +184,14 @@ final class SettingsViewModel: ObservableObject { } // MARK: Default Init - init(state: SettingsState? = nil, legacyViewProvider: SettingsLegacyViewProvider, accountManager: AccountManager) { + init(state: SettingsState? = nil, + legacyViewProvider: SettingsLegacyViewProvider, + accountManager: AccountManager, + voiceSearchHelper: VoiceSearchHelperProtocol = AppDependencyProvider.shared.voiceSearchHelper) { self.state = SettingsState.defaults self.legacyViewProvider = legacyViewProvider self.accountManager = accountManager + self.voiceSearchHelper = voiceSearchHelper } } @@ -213,8 +218,8 @@ extension SettingsViewModel { activeWebsiteAccount: nil, version: versionProvider.versionAndBuildNumber, debugModeEnabled: featureFlagger.isFeatureOn(.debugMenu) || isDebugBuild, - voiceSearchEnabled: AppDependencyProvider.shared.voiceSearchHelper.isSpeechRecognizerAvailable, - speechRecognitionEnabled: AppDependencyProvider.shared.voiceSearchHelper.isSpeechRecognizerAvailable, + voiceSearchEnabled: AppDependencyProvider.shared.voiceSearchHelper.isVoiceSearchEnabled, + speechRecognitionAvailable: AppDependencyProvider.shared.voiceSearchHelper.isSpeechRecognizerAvailable, loginsEnabled: featureFlagger.isFeatureOn(.autofillAccessCredentialManagement), networkProtection: getNetworkProtectionState(), subscription: getSubscriptionState(), @@ -283,7 +288,6 @@ extension SettingsViewModel { completion(false) return } - AppDependencyProvider.shared.voiceSearchHelper.enableVoiceSearch(true) completion(true) } } From 6fb51361566308fa19b1c3b291f94a81fc0210a9 Mon Sep 17 00:00:00 2001 From: Christopher Brind Date: Mon, 29 Jan 2024 11:09:11 +0000 Subject: [PATCH 48/70] cherry pick fix for clearing data (#2400) --- .../xcshareddata/swiftpm/Package.resolved | 2 +- DuckDuckGo/MainViewController.swift | 48 +++++++++++++------ 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index cf42ab48df..71bfa012a6 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -156,7 +156,7 @@ { "identity" : "trackerradarkit", "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/TrackerRadarKit.git", + "location" : "https://github.com/duckduckgo/TrackerRadarKit", "state" : { "revision" : "a6b7ba151d9dc6684484f3785293875ec01cc1ff", "version" : "1.2.2" diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 47528d7e39..756187202d 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -139,6 +139,9 @@ class MainViewController: UIViewController { private var skipSERPFlow = true private var keyboardHeight: CGFloat = 0.0 + + var postClear: (() -> Void)? + var clearInProgress = false required init?(coder: NSCoder) { fatalError("Use init?(code:") @@ -773,22 +776,30 @@ class MainViewController: UIViewController { } func loadUrlInNewTab(_ url: URL, reuseExisting: Bool = false, inheritedAttribution: AdClickAttributionLogic.State?) { - allowContentUnderflow = false - viewCoordinator.navigationBarContainer.alpha = 1 - loadViewIfNeeded() - if reuseExisting, let existing = tabManager.first(withUrl: url) { - selectTab(existing) - return - } else if reuseExisting, let existing = tabManager.firstHomeTab() { - tabManager.selectTab(existing) - loadUrl(url) + func worker() { + allowContentUnderflow = false + viewCoordinator.navigationBarContainer.alpha = 1 + loadViewIfNeeded() + if reuseExisting, let existing = tabManager.first(withUrl: url) { + selectTab(existing) + return + } else if reuseExisting, let existing = tabManager.firstHomeTab() { + tabManager.selectTab(existing) + loadUrl(url) + } else { + addTab(url: url, inheritedAttribution: inheritedAttribution) + } + refreshOmniBar() + refreshTabIcon() + refreshControls() + tabsBarController?.refresh(tabsModel: tabManager.model) + } + + if clearInProgress { + postClear = worker } else { - addTab(url: url, inheritedAttribution: inheritedAttribution) + worker() } - refreshOmniBar() - refreshTabIcon() - refreshControls() - tabsBarController?.refresh(tabsModel: tabManager.model) } func enterSearch() { @@ -2021,6 +2032,11 @@ extension MainViewController: AutoClearWorker { } func forgetData() { + guard !clearInProgress else { + assertionFailure("Shouldn't get called multiple times") + return + } + clearInProgress = true URLSession.shared.configuration.urlCache?.removeAllCachedResponses() let pixel = TimedPixel(.forgetAllDataCleared) @@ -2035,6 +2051,10 @@ extension MainViewController: AutoClearWorker { } self.refreshUIAfterClear() + self.clearInProgress = false + + self.postClear?() + self.postClear = nil } } From 720279e26889947eae752774e2a612489911941f Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Mon, 29 Jan 2024 11:21:43 +0000 Subject: [PATCH 49/70] Adding lastSentDate to broken site report (#2320) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task/Issue URL: https://app.asana.com/0/1163321984198618/1205860617092609/f **Description**: Goal - See tech design → [✓ Tech design: Report previously submitted site breakage reports](https://app.asana.com/0/481882893211075/1205812132848029/f) - For each reported website store the first 6 characters of the hash of the domain (not eTLD+1) and last reported date (not timestamp). - For an existing reported website update the last reported date of the corresponding hash. - Last reported days are cleared once they're more than 30 days old. --- .gitignore | 2 + DuckDuckGo.xcodeproj/project.pbxproj | 14 +- .../xcshareddata/swiftpm/Package.resolved | 8 +- DuckDuckGo/MainViewController+Segues.swift | 7 +- .../PrivacyDashboard/BrokenSiteInfo.swift | 130 ------------------ .../PrivacyDashboardViewController.swift | 89 ++++++++++-- DuckDuckGo/TabViewController.swift | 29 ++-- .../BrokenSiteReportingTests.swift | 90 +++++------- Gemfile.lock | 6 +- LocalPackages/DuckUI/Package.swift | 2 +- LocalPackages/SyncUI/Package.swift | 2 +- LocalPackages/Waitlist/Package.swift | 2 +- submodules/privacy-reference-tests | 2 +- 13 files changed, 153 insertions(+), 230 deletions(-) delete mode 100644 DuckDuckGo/PrivacyDashboard/BrokenSiteInfo.swift diff --git a/.gitignore b/.gitignore index 07ebc5f934..d460333497 100644 --- a/.gitignore +++ b/.gitignore @@ -74,3 +74,5 @@ fastlane/test_output Configuration/ExternalDeveloper.xcconfig scripts/assets + +DuckDuckGoTests/NetworkProtectionVPNLocationViewModelTests.swift*.plist diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 37e55d037b..0b4c62ca31 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -577,7 +577,6 @@ 987130C7294AAB9F00AB05E0 /* MenuBookmarksViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 987130C1294AAB9E00AB05E0 /* MenuBookmarksViewModelTests.swift */; }; 987130C8294AAB9F00AB05E0 /* BookmarksTestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 987130C2294AAB9E00AB05E0 /* BookmarksTestHelpers.swift */; }; 987130C9294AAB9F00AB05E0 /* BookmarkUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 987130C3294AAB9E00AB05E0 /* BookmarkUtilsTests.swift */; }; - 98728E822417E3300033960E /* BrokenSiteInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98728E812417E3300033960E /* BrokenSiteInfo.swift */; }; 9872D205247DCAC100CEF398 /* TabPreviewsSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9872D204247DCAC100CEF398 /* TabPreviewsSource.swift */; }; 9874F9EE2187AFCE00CAF33D /* Themable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9874F9ED2187AFCE00CAF33D /* Themable.swift */; }; 9875E00722316B8400B1373F /* Instruments.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9875E00622316B8400B1373F /* Instruments.swift */; }; @@ -879,6 +878,7 @@ F1134ED21F40EF3A00B73467 /* JsonTestDataLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1134ECF1F40EBE200B73467 /* JsonTestDataLoader.swift */; }; F1134ED61F40F29F00B73467 /* StatisticsUserDefaultsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1134ED41F40F15800B73467 /* StatisticsUserDefaultsTests.swift */; }; F114C55B1E66EB020018F95F /* NibLoading.swift in Sources */ = {isa = PBXBuildFile; fileRef = F114C55A1E66EB020018F95F /* NibLoading.swift */; }; + F115ED9C2B4EFC8E001A0453 /* TestUtils in Frameworks */ = {isa = PBXBuildFile; productRef = F115ED9B2B4EFC8E001A0453 /* TestUtils */; }; F130D73A1E5776C500C45811 /* OmniBarDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F130D7391E5776C500C45811 /* OmniBarDelegate.swift */; }; F1386BA41E6846C40062FC3C /* TabDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1386BA31E6846C40062FC3C /* TabDelegate.swift */; }; F13B4BC01F180D8A00814661 /* TabsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F13B4BBF1F180D8A00814661 /* TabsModel.swift */; }; @@ -2130,7 +2130,6 @@ 987130C1294AAB9E00AB05E0 /* MenuBookmarksViewModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuBookmarksViewModelTests.swift; sourceTree = ""; }; 987130C2294AAB9E00AB05E0 /* BookmarksTestHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksTestHelpers.swift; sourceTree = ""; }; 987130C3294AAB9E00AB05E0 /* BookmarkUtilsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarkUtilsTests.swift; sourceTree = ""; }; - 98728E812417E3300033960E /* BrokenSiteInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrokenSiteInfo.swift; sourceTree = ""; }; 9872D204247DCAC100CEF398 /* TabPreviewsSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabPreviewsSource.swift; sourceTree = ""; }; 9874F9ED2187AFCE00CAF33D /* Themable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Themable.swift; sourceTree = ""; }; 9875E00622316B8400B1373F /* Instruments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Instruments.swift; sourceTree = ""; }; @@ -2711,6 +2710,7 @@ files = ( F486D3362506A037002D07D7 /* OHHTTPStubs in Frameworks */, F486D3382506A225002D07D7 /* OHHTTPStubsSwift in Frameworks */, + F115ED9C2B4EFC8E001A0453 /* TestUtils in Frameworks */, EEFAB4672A73C230008A38E4 /* NetworkProtectionTestUtils in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -5513,7 +5513,6 @@ F1DF09502B039E6E008CC908 /* PrivacyDashboard */ = { isa = PBXGroup; children = ( - 98728E812417E3300033960E /* BrokenSiteInfo.swift */, 1E87615828A1517200C7C5CE /* PrivacyDashboardViewController.swift */, 984147B924F0268D00362052 /* PrivacyDashboard.storyboard */, ); @@ -5754,6 +5753,7 @@ F486D3352506A037002D07D7 /* OHHTTPStubs */, F486D3372506A225002D07D7 /* OHHTTPStubsSwift */, EEFAB4662A73C230008A38E4 /* NetworkProtectionTestUtils */, + F115ED9B2B4EFC8E001A0453 /* TestUtils */, ); productName = DuckDuckGoTests; productReference = 84E341A61E2F7EFB00BDBA6F /* UnitTests.xctest */; @@ -6908,7 +6908,6 @@ 02EC02C429AFA33000557F1A /* AppTPBreakageFormView.swift in Sources */, F15D43201E706CC500BF2CDC /* AutocompleteViewController.swift in Sources */, BD862E092B30F63E0073E2EE /* VPNMetadataCollector.swift in Sources */, - 98728E822417E3300033960E /* BrokenSiteInfo.swift in Sources */, D6E83C682B23B6A3006C8AFB /* FontSettings.swift in Sources */, 31EF52E1281B3BDC0034796E /* AutofillLoginListItemViewModel.swift in Sources */, 1E4FAA6627D8DFC800ADC5B3 /* CompleteDownloadRowViewModel.swift in Sources */, @@ -9978,7 +9977,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 102.0.2; + version = 103.0.0; }; }; C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { @@ -10209,6 +10208,11 @@ package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = NetworkProtectionTestUtils; }; + F115ED9B2B4EFC8E001A0453 /* TestUtils */ = { + isa = XCSwiftPackageProductDependency; + package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = TestUtils; + }; F42D541C29DCA40B004C4FF1 /* DesignResourcesKit */ = { isa = XCSwiftPackageProductDependency; package = F42D541B29DCA40B004C4FF1 /* XCRemoteSwiftPackageReference "DesignResourcesKit" */; diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 946e0c3b86..1ccd2c196c 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "072df96b846e3350b23ab32a61b6c1a0983b5e85", - "version" : "102.0.2" + "revision" : "5af8fbcff0913aa543ba3eea60cc24e706a3a4e5", + "version" : "103.0.0" } }, { @@ -104,8 +104,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/privacy-dashboard", "state" : { - "revision" : "38336a574e13090764ba09a6b877d15ee514e371", - "version" : "3.1.1" + "revision" : "c67d268bf234760f49034a0fe7a6137a1b216b05", + "version" : "3.2.0" } }, { diff --git a/DuckDuckGo/MainViewController+Segues.swift b/DuckDuckGo/MainViewController+Segues.swift index 7002be5231..0d994f5699 100644 --- a/DuckDuckGo/MainViewController+Segues.swift +++ b/DuckDuckGo/MainViewController+Segues.swift @@ -124,7 +124,6 @@ extension MainViewController { os_log(#function, log: .generalLog, type: .debug) hideAllHighlightsIfNeeded() - let brokenSiteInfo = currentTab?.getCurrentWebsiteInfo() guard let currentURL = currentTab?.url, let privacyInfo = currentTab?.makePrivacyInfo(url: currentURL) else { assertionFailure("Missing fundamental data") @@ -133,11 +132,12 @@ extension MainViewController { let storyboard = UIStoryboard(name: "PrivacyDashboard", bundle: nil) let controller = storyboard.instantiateInitialViewController { coder in - PrivacyDashboardViewController(coder: coder, + PrivacyDashboardViewController(coder: coder, privacyInfo: privacyInfo, privacyConfigurationManager: ContentBlocking.shared.privacyConfigurationManager, contentBlockingManager: ContentBlocking.shared.contentBlockingManager, - initMode: .reportBrokenSite) + initMode: .reportBrokenSite, + breakageAdditionalInfo: self.currentTab?.makeBreakageAdditionalInfo()) } guard let controller = controller else { @@ -147,7 +147,6 @@ extension MainViewController { currentTab?.privacyDashboard = controller controller.popoverPresentationController?.delegate = controller - controller.brokenSiteInfo = brokenSiteInfo if UIDevice.current.userInterfaceIdiom == .pad { controller.modalPresentationStyle = .formSheet diff --git a/DuckDuckGo/PrivacyDashboard/BrokenSiteInfo.swift b/DuckDuckGo/PrivacyDashboard/BrokenSiteInfo.swift deleted file mode 100644 index a6395d695d..0000000000 --- a/DuckDuckGo/PrivacyDashboard/BrokenSiteInfo.swift +++ /dev/null @@ -1,130 +0,0 @@ -// -// BrokenSiteInfo.swift -// DuckDuckGo -// -// Copyright © 2020 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 Core - -public struct BrokenSiteInfo { - - static let allowedQueryReservedCharacters = CharacterSet(charactersIn: ",") - - private struct Keys { - static let url = "siteUrl" - static let category = "category" - static let reportFlow = "reportFlow" - static let description = "description" - static let upgradedHttps = "upgradedHttps" - static let tds = "tds" - static let blockedTrackers = "blockedTrackers" - static let surrogates = "surrogates" - static let atb = "atb" - static let os = "os" - static let manufacturer = "manufacturer" - static let model = "model" - static let siteType = "siteType" - static let gpc = "gpc" - static let ampUrl = "ampUrl" - static let urlParametersRemoved = "urlParametersRemoved" - static let protectionsState = "protectionsState" - } - - public enum Source: String { - case appMenu = "menu" - case dashboard - } - - private let url: URL? - private let httpsUpgrade: Bool - private let blockedTrackerDomains: [String] - private let installedSurrogates: [String] - private let isDesktop: Bool - private let tdsETag: String? - private let ampUrl: String? - private let urlParametersRemoved: Bool - private let model: String - private let manufacturer: String - private let systemVersion: String - private let gpc: Bool - private let protectionsState: Bool - - public init(url: URL?, httpsUpgrade: Bool, - blockedTrackerDomains: [String], - installedSurrogates: [String], - isDesktop: Bool, - tdsETag: String?, - ampUrl: String?, - urlParametersRemoved: Bool, - protectionsState: Bool, - model: String = UIDevice.current.model, - manufacturer: String = "Apple", - systemVersion: String = UIDevice.current.systemVersion, - gpc: Bool? = nil) { - self.url = url - self.httpsUpgrade = httpsUpgrade - self.blockedTrackerDomains = blockedTrackerDomains - self.installedSurrogates = installedSurrogates - self.isDesktop = isDesktop - self.tdsETag = tdsETag - self.ampUrl = ampUrl - self.urlParametersRemoved = urlParametersRemoved - - self.model = model - self.manufacturer = manufacturer - self.systemVersion = systemVersion - self.protectionsState = protectionsState - - if let gpcParam = gpc { - self.gpc = gpcParam - } else { - self.gpc = AppDependencyProvider.shared.appSettings.sendDoNotSell - } - } - - func send(with category: String?, description: String, source: Source) { - - let parameters: [String: String] = [ - Keys.url: normalize(url), - Keys.category: category ?? "", - Keys.description: description, - Keys.reportFlow: source.rawValue, - Keys.upgradedHttps: httpsUpgrade ? "true" : "false", - Keys.siteType: isDesktop ? "desktop" : "mobile", - Keys.tds: tdsETag?.trimmingCharacters(in: CharacterSet(charactersIn: "\"")) ?? "", - Keys.blockedTrackers: blockedTrackerDomains.joined(separator: ","), - Keys.surrogates: installedSurrogates.joined(separator: ","), - Keys.atb: StatisticsUserDefaults().atb ?? "", - Keys.os: systemVersion, - Keys.manufacturer: manufacturer, - Keys.model: model, - Keys.gpc: gpc ? "true" : "false", - Keys.ampUrl: ampUrl ?? "", - Keys.urlParametersRemoved: urlParametersRemoved ? "true" : "false", - Keys.protectionsState: protectionsState ? "true" : "false" - ] - - Pixel.fire(pixel: .brokenSiteReport, - withAdditionalParameters: parameters, - allowedQueryReservedCharacters: BrokenSiteInfo.allowedQueryReservedCharacters) - } - - private func normalize(_ url: URL?) -> String { - return url?.normalized()?.absoluteString ?? "" - } - -} diff --git a/DuckDuckGo/PrivacyDashboard/PrivacyDashboardViewController.swift b/DuckDuckGo/PrivacyDashboard/PrivacyDashboardViewController.swift index c4a2e67877..48a5b0d6e0 100644 --- a/DuckDuckGo/PrivacyDashboard/PrivacyDashboardViewController.swift +++ b/DuckDuckGo/PrivacyDashboard/PrivacyDashboardViewController.swift @@ -23,6 +23,7 @@ import Combine import Core import BrowserServicesKit import PrivacyDashboard +import Common /// View controller used for `Privacy Dasboard` or `Report broken site`, the web content is chosen at init time setting the correct `initMode` class PrivacyDashboardViewController: UIViewController { @@ -39,22 +40,31 @@ class PrivacyDashboardViewController: UIViewController { private let privacyDashboardController: PrivacyDashboardController private let privacyConfigurationManager: PrivacyConfigurationManaging private let contentBlockingManager: ContentBlockerRulesManager - public var brokenSiteInfo: BrokenSiteInfo? + public var breakageAdditionalInfo: BreakageAdditionalInfo? - var source: BrokenSiteInfo.Source { + var source: WebsiteBreakage.Source { initMode == .reportBrokenSite ? .appMenu : .dashboard } + private let websiteBreakageReporter: WebsiteBreakageReporter = { + WebsiteBreakageReporter(pixelHandler: { parameters in + Pixel.fire(pixel: .brokenSiteReport, + withAdditionalParameters: parameters, + allowedQueryReservedCharacters: WebsiteBreakage.allowedQueryReservedCharacters) + }, keyValueStoring: UserDefaults.standard) + }() + init?(coder: NSCoder, privacyInfo: PrivacyInfo?, privacyConfigurationManager: PrivacyConfigurationManaging, contentBlockingManager: ContentBlockerRulesManager, - initMode: Mode) { + initMode: Mode, + breakageAdditionalInfo: BreakageAdditionalInfo?) { self.privacyDashboardController = PrivacyDashboardController(privacyInfo: privacyInfo) self.privacyConfigurationManager = privacyConfigurationManager self.contentBlockingManager = contentBlockingManager self.initMode = initMode - + self.breakageAdditionalInfo = breakageAdditionalInfo super.init(coder: coder) self.privacyDashboardController.privacyDashboardDelegate = self @@ -126,6 +136,8 @@ extension PrivacyDashboardViewController: Themable { } } +// MARK: - PrivacyDashboardControllerDelegate + extension PrivacyDashboardViewController: PrivacyDashboardControllerDelegate { func privacyDashboardController(_ privacyDashboardController: PrivacyDashboardController, didChangeProtectionSwitch protectionState: ProtectionState) { @@ -159,6 +171,8 @@ extension PrivacyDashboardViewController: PrivacyDashboardControllerDelegate { } } +// MARK: - PrivacyDashboardNavigationDelegate + extension PrivacyDashboardViewController: PrivacyDashboardNavigationDelegate { func privacyDashboardController(_ privacyDashboardController: PrivacyDashboard.PrivacyDashboardController, didSetHeight height: Int) { @@ -171,23 +185,74 @@ extension PrivacyDashboardViewController: PrivacyDashboardNavigationDelegate { } } +// MARK: - PrivacyDashboardReportBrokenSiteDelegate + extension PrivacyDashboardViewController: PrivacyDashboardReportBrokenSiteDelegate { - - func privacyDashboardController(_ privacyDashboardController: PrivacyDashboardController, reportBrokenSiteDidChangeProtectionSwitch protectionState: ProtectionState) { + + func privacyDashboardController(_ privacyDashboardController: PrivacyDashboardController, + reportBrokenSiteDidChangeProtectionSwitch protectionState: ProtectionState) { privacyDashboardProtectionSwitchChangeHandler(state: protectionState) } - func privacyDashboardController(_ privacyDashboardController: PrivacyDashboard.PrivacyDashboardController, didRequestSubmitBrokenSiteReportWithCategory category: String, description: String) { - - guard let brokenSiteInfo = brokenSiteInfo else { - assertionFailure("brokenSiteInfo not initialised") - return + func privacyDashboardController(_ privacyDashboardController: PrivacyDashboard.PrivacyDashboardController, + didRequestSubmitBrokenSiteReportWithCategory category: String, description: String) { + + do { + let breakageReport = try makeWebsiteBreakage(category: category, description: description) + try websiteBreakageReporter.report(breakage: breakageReport) + } catch { + os_log("Failed to generate or send the website breakage report: %@", type: .error, error.localizedDescription) } - brokenSiteInfo.send(with: category, description: description, source: source) ActionMessageView.present(message: UserText.feedbackSumbittedConfirmation) privacyDashboardCloseHandler() } } extension PrivacyDashboardViewController: UIPopoverPresentationControllerDelegate {} + +extension PrivacyDashboardViewController { + + struct BreakageAdditionalInfo { + let currentURL: URL + let httpsForced: Bool + let ampURLString: String + let urlParametersRemoved: Bool + let isDesktop: Bool + } + + enum WebsiteBreakageError: Error { + case failedToFetchTheCurrentWebsiteInfo + } + + private func makeWebsiteBreakage(category: String, description: String) throws -> WebsiteBreakage { + + guard let privacyInfo = privacyDashboardController.privacyInfo, + let breakageAdditionalInfo = breakageAdditionalInfo else { + throw WebsiteBreakageError.failedToFetchTheCurrentWebsiteInfo + } + + let blockedTrackerDomains = privacyInfo.trackerInfo.trackersBlocked.compactMap { $0.domain } + let configuration = ContentBlocking.shared.privacyConfigurationManager.privacyConfig + let protectionsState = configuration.isFeature(.contentBlocking, enabledForDomain: breakageAdditionalInfo.currentURL.host) + + return WebsiteBreakage(siteUrl: breakageAdditionalInfo.currentURL, + category: category, + description: description, + osVersion: "\(ProcessInfo().operatingSystemVersion.majorVersion)", + manufacturer: "Apple", + upgradedHttps: breakageAdditionalInfo.httpsForced, + tdsETag: ContentBlocking.shared.contentBlockingManager.currentMainRules?.etag ?? "", + blockedTrackerDomains: blockedTrackerDomains, + installedSurrogates: privacyInfo.trackerInfo.installedSurrogates.map { $0 }, + isGPCEnabled: AppDependencyProvider.shared.appSettings.sendDoNotSell, + ampURL: breakageAdditionalInfo.ampURLString, + urlParametersRemoved: breakageAdditionalInfo.urlParametersRemoved, + protectionsState: protectionsState, + reportFlow: source, + siteType: breakageAdditionalInfo.isDesktop ? .desktop : .mobile, + atb: StatisticsUserDefaults().atb ?? "", + model: UIDevice.current.model) + + } +} diff --git a/DuckDuckGo/TabViewController.swift b/DuckDuckGo/TabViewController.swift index f54d55883b..f0d388a281 100644 --- a/DuckDuckGo/TabViewController.swift +++ b/DuckDuckGo/TabViewController.swift @@ -722,7 +722,6 @@ class TabViewController: UIViewController { controller.popoverPresentationController?.sourceRect = iconView.bounds } privacyDashboard = controller - privacyDashboard?.brokenSiteInfo = getCurrentWebsiteInfo() } if let controller = segue.destination as? FullscreenDaxDialogViewController { @@ -749,7 +748,8 @@ class TabViewController: UIViewController { privacyInfo: privacyInfo, privacyConfigurationManager: ContentBlocking.shared.privacyConfigurationManager, contentBlockingManager: ContentBlocking.shared.contentBlockingManager, - initMode: .privacyDashboard) + initMode: .privacyDashboard, + breakageAdditionalInfo: makeBreakageAdditionalInfo()) } private func addTextSizeObserver() { @@ -912,22 +912,17 @@ class TabViewController: UIViewController { webView.removeObserver(self, forKeyPath: #keyPath(WKWebView.canGoBack)) webView.removeObserver(self, forKeyPath: #keyPath(WKWebView.title)) } + + public func makeBreakageAdditionalInfo() -> PrivacyDashboardViewController.BreakageAdditionalInfo? { - public func getCurrentWebsiteInfo() -> BrokenSiteInfo { - let blockedTrackerDomains = privacyInfo?.trackerInfo.trackersBlocked.compactMap { $0.domain } ?? [] - - let configuration = ContentBlocking.shared.privacyConfigurationManager.privacyConfig - let protectionsState = configuration.isFeature(.contentBlocking, enabledForDomain: url?.host) - - return BrokenSiteInfo(url: url, - httpsUpgrade: httpsForced, - blockedTrackerDomains: blockedTrackerDomains, - installedSurrogates: privacyInfo?.trackerInfo.installedSurrogates.map { $0 } ?? [], - isDesktop: tabModel.isDesktop, - tdsETag: ContentBlocking.shared.contentBlockingManager.currentMainRules?.etag ?? "", - ampUrl: linkProtection.lastAMPURLString, - urlParametersRemoved: linkProtection.urlParametersRemoved, - protectionsState: protectionsState) + guard let currentURL = url else { + return nil + } + return PrivacyDashboardViewController.BreakageAdditionalInfo(currentURL: currentURL, + httpsForced: httpsForced, + ampURLString: linkProtection.lastAMPURLString ?? "", + urlParametersRemoved: linkProtection.urlParametersRemoved, + isDesktop: tabModel.isDesktop) } public func print() { diff --git a/DuckDuckGoTests/BrokenSiteReportingTests.swift b/DuckDuckGoTests/BrokenSiteReportingTests.swift index 3959b8cbf3..40c578f113 100644 --- a/DuckDuckGoTests/BrokenSiteReportingTests.swift +++ b/DuckDuckGoTests/BrokenSiteReportingTests.swift @@ -23,8 +23,9 @@ import BrowserServicesKit import OHHTTPStubs import OHHTTPStubsSwift @testable import Core - +import PrivacyDashboard @testable import DuckDuckGo +import TestUtils final class BrokenSiteReportingTests: XCTestCase { private let data = JsonTestDataLoader() @@ -52,7 +53,6 @@ final class BrokenSiteReportingTests: XCTestCase { func testBrokenSiteReporting() throws { let testJSON = data.fromJsonFile(Resource.tests) - let testString = String(data: testJSON, encoding: .utf8) let testData = try JSONDecoder().decode(BrokenSiteReportingTestData.self, from: testJSON) referenceTests = testData.reportURL.tests.filter { @@ -61,13 +61,12 @@ final class BrokenSiteReportingTests: XCTestCase { let testsExecuted = expectation(description: "tests executed") testsExecuted.expectedFulfillmentCount = referenceTests.count - - runReferenceTests(onTestExecuted: testsExecuted) - waitForExpectations(timeout: 30, handler: nil) - + + try runReferenceTests(onTestExecuted: testsExecuted) + waitForExpectations(timeout: 10, handler: nil) } - private func runReferenceTests(onTestExecuted: XCTestExpectation) { + private func runReferenceTests(onTestExecuted: XCTestExpectation) throws { guard let test = referenceTests.popLast() else { return @@ -75,56 +74,41 @@ final class BrokenSiteReportingTests: XCTestCase { os_log("Testing [%s]", type: .info, test.name) - let brokenSiteInfo = BrokenSiteInfo(url: URL(string: test.siteURL), - httpsUpgrade: test.wasUpgraded, - blockedTrackerDomains: test.blockedTrackers, - installedSurrogates: test.surrogates, - isDesktop: true, - tdsETag: test.blocklistVersion, - ampUrl: nil, - urlParametersRemoved: false, - protectionsState: test.protectionsEnabled, - model: test.model ?? "", - manufacturer: test.manufacturer ?? "", - systemVersion: test.os ?? "", - gpc: test.gpcEnabled) + let websiteBreakage = WebsiteBreakage(siteUrl: URL(string: test.siteURL)!, + category: test.category, + description: "", + osVersion: test.os ?? "", + manufacturer: test.manufacturer ?? "", + upgradedHttps: test.wasUpgraded, + tdsETag: test.blocklistVersion, + blockedTrackerDomains: test.blockedTrackers, + installedSurrogates: test.surrogates, + isGPCEnabled: test.gpcEnabled ?? false, + ampURL: "", + urlParametersRemoved: false, + protectionsState: test.protectionsEnabled, + reportFlow: .dashboard, + siteType: .mobile, + atb: "", + model: test.model ?? "") - stub(condition: isHost(host)) { request -> HTTPStubsResponse in + let reporter = WebsiteBreakageReporter(pixelHandler: { params in - guard let requestURL = request.url else { - XCTFail("Couldn't create request URL") - return HTTPStubsResponse(data: Data(), statusCode: 200, headers: nil) - } - - let absoluteURL = requestURL.absoluteString - .replacingOccurrences(of: "%20", with: " ") - - if test.expectReportURLPrefix.count > 0 { - XCTAssertTrue(requestURL.absoluteString.contains(test.expectReportURLPrefix), "Prefix [\(test.expectReportURLPrefix)] not found") - } - - for param in test.expectReportURLParams { - let pattern = "[?&]\(param.name)=\(param.value)[&$]?" - - guard let regex = try? NSRegularExpression(pattern: pattern, - options: []) else { - XCTFail("Couldn't create regex") - return HTTPStubsResponse(data: Data(), statusCode: 200, headers: nil) + for expectedParam in test.expectReportURLParams { + + if let actualValue = params[expectedParam.name], + let expectedCleanValue = expectedParam.value.removingPercentEncoding { + if actualValue != expectedCleanValue { + XCTFail("Mismatching param: \(expectedParam.name) => \(expectedCleanValue) != \(actualValue)") + } + } else { + XCTFail("Missing param: \(expectedParam.name)") } - - let match = regex.matches(in: absoluteURL, range: NSRange(location: 0, length: absoluteURL.count)) - XCTAssertEqual(match.count, 1, "Param [\(param.name)] with value [\(param.value)] not found in [\(absoluteURL)]") - } - - DispatchQueue.main.async { - onTestExecuted.fulfill() - self.runReferenceTests(onTestExecuted: onTestExecuted) } - - return HTTPStubsResponse(data: Data(), statusCode: 200, headers: nil) - } - - brokenSiteInfo.send(with: test.category, description: "", source: .dashboard) + onTestExecuted.fulfill() + try? self.runReferenceTests(onTestExecuted: onTestExecuted) + }, keyValueStoring: MockKeyValueStore()) + try reporter.report(breakage: websiteBreakage) } } diff --git a/Gemfile.lock b/Gemfile.lock index d857156940..41d2cd26f5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -32,7 +32,8 @@ GEM declarative (0.0.20) digest-crc (0.6.5) rake (>= 12.0.0, < 14.0.0) - domain_name (0.6.20231109) + domain_name (0.5.20190701) + unf (>= 0.0.5, < 1.0.0) dotenv (2.8.1) emoji_regex (3.2.3) excon (0.104.0) @@ -188,6 +189,9 @@ GEM tty-spinner (0.9.3) tty-cursor (~> 0.7) uber (0.1.0) + unf (0.1.4) + unf_ext + unf_ext (0.0.9.1) unicode-display_width (2.5.0) webrick (1.8.1) word_wrap (1.0.0) diff --git a/LocalPackages/DuckUI/Package.swift b/LocalPackages/DuckUI/Package.swift index 604624a245..cf31671f45 100644 --- a/LocalPackages/DuckUI/Package.swift +++ b/LocalPackages/DuckUI/Package.swift @@ -31,7 +31,7 @@ let package = Package( targets: ["DuckUI"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "102.0.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "103.0.0"), ], targets: [ .target( diff --git a/LocalPackages/SyncUI/Package.swift b/LocalPackages/SyncUI/Package.swift index 727d00e286..6543b2f149 100644 --- a/LocalPackages/SyncUI/Package.swift +++ b/LocalPackages/SyncUI/Package.swift @@ -33,7 +33,7 @@ let package = Package( ], dependencies: [ .package(path: "../DuckUI"), - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "102.0.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "103.0.0"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ diff --git a/LocalPackages/Waitlist/Package.swift b/LocalPackages/Waitlist/Package.swift index 70220d0c31..598bd55103 100644 --- a/LocalPackages/Waitlist/Package.swift +++ b/LocalPackages/Waitlist/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["Waitlist", "WaitlistMocks"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "102.0.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "103.0.0"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ diff --git a/submodules/privacy-reference-tests b/submodules/privacy-reference-tests index 6b7ad1e7f1..a3acc21947 160000 --- a/submodules/privacy-reference-tests +++ b/submodules/privacy-reference-tests @@ -1 +1 @@ -Subproject commit 6b7ad1e7f15270f9dfeb58a272199f4d57c3eb22 +Subproject commit a3acc2194758bec0f01f57dd0c5f106de01a354e From 8ed7f511df21606e2495635ef54948703b430fcc Mon Sep 17 00:00:00 2001 From: bwaresiak Date: Mon, 29 Jan 2024 13:40:00 +0100 Subject: [PATCH 50/70] Release/7.106.0-2 (#2403) --- DuckDuckGo.xcodeproj/project.pbxproj | 56 ++++++++++----------- fastlane/metadata/default/release_notes.txt | 1 + 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index eca51a2c21..042be06694 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -8163,7 +8163,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProvider.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -8200,7 +8200,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8292,7 +8292,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8320,7 +8320,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8470,7 +8470,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8496,7 +8496,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; INFOPLIST_FILE = DuckDuckGo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8561,7 +8561,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -8596,7 +8596,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8630,7 +8630,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8661,7 +8661,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8948,7 +8948,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8979,7 +8979,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -9008,7 +9008,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -9042,7 +9042,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -9073,7 +9073,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProvider.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -9106,11 +9106,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; + DYLIB_CURRENT_VERSION = 2; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9348,7 +9348,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGoAlpha.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9375,7 +9375,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9408,7 +9408,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9446,7 +9446,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9482,7 +9482,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9517,11 +9517,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; + DYLIB_CURRENT_VERSION = 2; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9695,11 +9695,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; + DYLIB_CURRENT_VERSION = 2; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9728,10 +9728,10 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; + DYLIB_CURRENT_VERSION = 2; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; diff --git a/fastlane/metadata/default/release_notes.txt b/fastlane/metadata/default/release_notes.txt index e0c7a0d1ed..e41e1ebb14 100644 --- a/fastlane/metadata/default/release_notes.txt +++ b/fastlane/metadata/default/release_notes.txt @@ -1,3 +1,4 @@ - You can now pull the page down to reload it. +- Fixed settings not being saved in rare cases. - Bug fixes and other improvements. Join our fully distributed team and help raise the standard of trust online! https://duckduckgo.com/hiring From 4204ddb559868d96370d454e639f3c2b60900b53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacek=20=C5=81yp?= Date: Mon, 29 Jan 2024 14:03:39 +0100 Subject: [PATCH 51/70] Prepare release script updates (#2389) --- .github/workflows/release.yml | 6 +- .../xcshareddata/swiftpm/Package.resolved | 2 +- scripts/prepare_release.sh | 109 ++++++++++-------- 3 files changed, 65 insertions(+), 52 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dd02518132..49b135d79f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,15 +20,13 @@ on: branches: - release/** - hotfix/** - - coldfix/** - '!release/**-' # filter out PRs matching that pattern - '!hotfix/**-' - - '!coldfix/**-' types: [closed] jobs: make-release: - if: github.event.action == 0 || github.event.pull_request.merged == true # empty string returns 0; for case when workflow is triggered manually + if: github.event.action == 0 || (github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'Merge triggers release')) # empty string returns 0; for case when workflow is triggered manually runs-on: macos-13-xlarge name: Make App Store Connect Release @@ -98,7 +96,7 @@ jobs: - name: Upload debug symbols to Asana if: ${{ always() && github.event.inputs.asana-task-url }} - env: + env: ASANA_ACCESS_TOKEN: ${{ secrets.ASANA_ACCESS_TOKEN }} run: | if [[ -f ${{ env.dsyms_path }} ]]; then diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1ccd2c196c..299ba401f7 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -156,7 +156,7 @@ { "identity" : "trackerradarkit", "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/TrackerRadarKit", + "location" : "https://github.com/duckduckgo/TrackerRadarKit.git", "state" : { "revision" : "a6b7ba151d9dc6684484f3785293875ec01cc1ff", "version" : "1.2.2" diff --git a/scripts/prepare_release.sh b/scripts/prepare_release.sh index cfc87c38dc..a747eee336 100755 --- a/scripts/prepare_release.sh +++ b/scripts/prepare_release.sh @@ -4,6 +4,8 @@ set -eo pipefail mute=">/dev/null 2>&1" version="$1" +latest_build_number=$(agvtool what-version -terse) +next_build_number=$((latest_build_number + 1)) release_branch_parent="main" tag=${version} hotfix_branch_parent="tags/${tag}" @@ -56,7 +58,6 @@ print_usage_and_exit() { Options: -h Make hotfix release. Requires the version to be the one to hotfix, and a branch with the fix as the second parameter - -c Make coldfix release (i.e. a new build number on an existing release). Requires the version to be the one to coldfix, and a branch with the fix as the second parameter -v Enable verbose mode EOF @@ -81,22 +82,15 @@ read_command_line_arguments() { fi fi - branch_name="release" + release_branch_prefix="release" - while getopts 'hcv' option; do + while getopts 'hv' option; do case "${option}" in - h) + h) # hotfix is_hotfix=1 - branch_name="hotfix" - fix_type_name="hotfix" + release_branch_prefix="hotfix" ;; - c) - is_hotfix=1 - is_coldfix=1 - branch_name="coldfix" - fix_type_name="coldfix" - ;; - v) + v) # verbose mute= ;; *) @@ -105,6 +99,14 @@ read_command_line_arguments() { esac done + release_branch="${release_branch_prefix}/${version}" + build_branch="${release_branch}-build-0" + + if release_branch_exists; then + is_subsequent_release=1 + build_branch="${release_branch}-build-${next_build_number}" + fi + shift $((OPTIND-1)) if [[ $is_hotfix ]]; then @@ -113,15 +115,18 @@ read_command_line_arguments() { fi version_to_hotfix=${version} - if ! [[ $is_coldfix ]]; then - IFS='.' read -ra arrIN <<< "$version" - patch_number=$((arrIN[2] + 1)) - version="${arrIN[0]}.${arrIN[1]}.$patch_number" - fi + IFS='.' read -ra arrIN <<< "$version" + patch_number=$((arrIN[2] + 1)) + version="${arrIN[0]}.${arrIN[1]}.$patch_number" fi +} - release_branch="${branch_name}/${version}" - changes_branch="${release_branch}-changes" +release_branch_exists() { + if git show-ref --verify --quiet "refs/heads/$release_branch"; then + return 0 + else + return 1 + fi } stash() { @@ -134,16 +139,13 @@ assert_clean_state() { if git show-ref --quiet "refs/heads/${release_branch}"; then die "💥 Error: Branch ${release_branch} already exists" fi - if git show-ref --quiet "refs/heads/${changes_branch}"; then - die "💥 Error: Branch ${changes_branch} already exists" + if git show-ref --quiet "refs/heads/${build_branch}"; then + die "💥 Error: Branch ${build_branch} already exists" fi } -assert_hotfix_tag_exists_if_necessary() { - if [[ ! $is_hotfix ]]; then - return - fi - printf '%s' "Checking tag to ${fix_type_name} ... " +assert_hotfix_tag_exists() { + printf '%s' "Checking tag to hotfix ... " # Make sure tag is available locally if it exists eval git fetch origin "+refs/tags/${tag}:refs/tags/${tag}" "$mute" @@ -155,25 +157,29 @@ assert_hotfix_tag_exists_if_necessary() { fi } -create_release_branch() { +create_release_and_build_branches() { if [[ ${is_hotfix} ]]; then - printf '%s' "Creating ${fix_type_name} branch ... " - + printf '%s' "Creating hotfix branch ... " eval git checkout "${hotfix_branch_parent}" "$mute" else printf '%s' "Creating release branch ... " eval git checkout ${release_branch_parent} "$mute" eval git pull "$mute" fi - eval git checkout -b "${release_branch}" "$mute" - eval git checkout -b "${changes_branch}" "$mute" + eval git checkout -b "${release_branch}" --track "origin/${release_branch}" "$mute" + eval git checkout -b "${build_branch}" --track "origin/${build_branch}" "$mute" + echo "✅" +} + +create_build_branch() { + printf '%s' "Creating build branch ... " + eval git checkout "${release_branch}" "$mute" + eval git pull "$mute" + eval git checkout -b "${build_branch}" --track "origin/${build_branch}" "$mute" echo "✅" } update_marketing_version() { - if [[ $is_coldfix ]]; then - return - fi printf '%s' "Setting app version ... " "$script_dir/set_version.sh" "${version}" git add "${base_dir}/Configuration/Version.xcconfig" \ @@ -234,16 +240,18 @@ merge_fix_branch_if_necessary() { eval git checkout "${fix_branch}" "$mute" eval git pull "$mute" - eval git checkout "${changes_branch}" "$mute" + eval git checkout "${build_branch}" "$mute" eval git merge "${fix_branch}" "$mute" echo "✅" } create_pull_request() { printf '%s' "Creating PR ... " - eval git push origin "${release_branch}" "$mute" - eval git push origin "${changes_branch}" "$mute" - eval gh pr create --title \"Release "${version}"\" --base "${release_branch}" --assignee @me "$mute" --body-file "${script_dir}/assets/prepare-release-description" + if [[ ! $is_subsequent_release ]]; then + eval git push -u origin "${release_branch}" "$mute" + fi + eval git push -u origin "${build_branch}" "$mute" + eval gh pr create --title \"Release "${version}-${next_build_number}"\" --base "${release_branch}" --label "Merge triggers release" --assignee @me "$mute" --body-file "${script_dir}/assets/prepare-release-description" eval gh pr view --web "$mute" echo "✅" } @@ -256,20 +264,27 @@ main() { read_command_line_arguments "$@" stash - assert_clean_state - assert_hotfix_tag_exists_if_necessary - create_release_branch - - update_marketing_version - update_build_version - if ! [[ $is_hotfix ]]; then + if [[ $is_subsequent_release ]]; then + create_build_branch + elif [[ $is_hotfix ]]; then + assert_clean_state + assert_hotfix_tag_exists + create_release_and_build_branches + update_marketing_version + else # regular release + assert_clean_state + create_release_and_build_branches + update_marketing_version update_embedded_files fi + + update_build_version + update_release_notes merge_fix_branch_if_necessary create_pull_request } -main "$@" +main "$@" \ No newline at end of file From c1b97c960f3bf885947a7e83094fb57958d67aa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mariusz=20=C5=9Apiewak?= Date: Mon, 29 Jan 2024 16:41:40 +0100 Subject: [PATCH 52/70] Fix `site:` queries escaping with iOS 17 SDK (#640) (#2402) --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- LocalPackages/DuckUI/Package.swift | 2 +- LocalPackages/SyncUI/Package.swift | 2 +- LocalPackages/Waitlist/Package.swift | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 0b4c62ca31..33af0bdcc4 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -9977,7 +9977,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 103.0.0; + version = 103.0.1; }; }; C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 299ba401f7..46dfe6def6 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "5af8fbcff0913aa543ba3eea60cc24e706a3a4e5", - "version" : "103.0.0" + "revision" : "055cc2d86c0ac085d032fc28665c3115ea3f325a", + "version" : "103.0.1" } }, { diff --git a/LocalPackages/DuckUI/Package.swift b/LocalPackages/DuckUI/Package.swift index cf31671f45..3e534b5f97 100644 --- a/LocalPackages/DuckUI/Package.swift +++ b/LocalPackages/DuckUI/Package.swift @@ -31,7 +31,7 @@ let package = Package( targets: ["DuckUI"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "103.0.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "103.0.1"), ], targets: [ .target( diff --git a/LocalPackages/SyncUI/Package.swift b/LocalPackages/SyncUI/Package.swift index 6543b2f149..88f36dd10e 100644 --- a/LocalPackages/SyncUI/Package.swift +++ b/LocalPackages/SyncUI/Package.swift @@ -33,7 +33,7 @@ let package = Package( ], dependencies: [ .package(path: "../DuckUI"), - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "103.0.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "103.0.1"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ diff --git a/LocalPackages/Waitlist/Package.swift b/LocalPackages/Waitlist/Package.swift index 598bd55103..cd163e3f2b 100644 --- a/LocalPackages/Waitlist/Package.swift +++ b/LocalPackages/Waitlist/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["Waitlist", "WaitlistMocks"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "103.0.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "103.0.1"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ From 2e96952aba6740e40d6323eadf2fed6fc62270b7 Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Mon, 29 Jan 2024 09:18:57 -0800 Subject: [PATCH 53/70] Fix the alpha build VPN extension embed script (#2396) Task/Issue URL: https://app.asana.com/0/414235014887631/1206457898054445/f Tech Design URL: CC: Description: This PR fixes a typo in the VPN extension embed script that was causing it to not be included in the nightly alpha. --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 33af0bdcc4..65a995e7d8 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -6411,7 +6411,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# Conditionally embeds PacketTunnelProvider extension for Debug and Alpha builds.\n\n# Conditionally embeds the PacketTunnelProvider extension for debug builds.\\n# To be moved to the Embed App Extensions phase on release.\n\nif [ \"${CONFIGURATION}\" = \"Debug\" ] || [ \"${CONFIGURATION}\" = \"Release\" ] || [ \"${CONFIGURATION}\" = \"Alpha\" || [ \"${CONFIGURATION}\" = \"Alpha Debug\" ]; then\n# Copy the extension \n rsync -r --copy-links \"${CONFIGURATION_BUILD_DIR}/PacketTunnelProvider.appex\" \"${CONFIGURATION_BUILD_DIR}/${PLUGINS_FOLDER_PATH}\"\nfi\n"; + shellScript = "# Conditionally embeds PacketTunnelProvider extension for Debug and Alpha builds.\n\n# Conditionally embeds the PacketTunnelProvider extension for debug builds.\\n# To be moved to the Embed App Extensions phase on release.\n\nif [ \"${CONFIGURATION}\" = \"Debug\" ] || [ \"${CONFIGURATION}\" = \"Release\" ] || [ \"${CONFIGURATION}\" = \"Alpha\" ] || [ \"${CONFIGURATION}\" = \"Alpha Debug\" ]; then\n# Copy the extension \n rsync -r --copy-links \"${CONFIGURATION_BUILD_DIR}/PacketTunnelProvider.appex\" \"${CONFIGURATION_BUILD_DIR}/${PLUGINS_FOLDER_PATH}\"\nfi\n"; }; /* End PBXShellScriptBuildPhase section */ From 968ed383d98d47a588e7e93c5573a826e1c27e48 Mon Sep 17 00:00:00 2001 From: Brian Hall Date: Mon, 29 Jan 2024 13:19:28 -0600 Subject: [PATCH 54/70] Bump BrowserServicesKit to 103.0.2 (#2393) --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 8 ++++---- LocalPackages/DuckUI/Package.swift | 2 +- LocalPackages/SyncUI/Package.swift | 2 +- LocalPackages/Waitlist/Package.swift | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 65a995e7d8..15116bfef5 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -9977,7 +9977,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 103.0.1; + version = 103.0.2; }; }; C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 46dfe6def6..380a78393d 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "055cc2d86c0ac085d032fc28665c3115ea3f325a", - "version" : "103.0.1" + "revision" : "1f7a1b5fe1c404d331fe34e55c99296828107c1d", + "version" : "103.0.2" } }, { @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/content-scope-scripts", "state" : { - "revision" : "0b68b0d404d8d4f32296cd84fa160b18b0aeaf44", - "version" : "4.59.1" + "revision" : "38ee7284bac7fa12d822fcaf0677ea3969d15fb1", + "version" : "4.59.2" } }, { diff --git a/LocalPackages/DuckUI/Package.swift b/LocalPackages/DuckUI/Package.swift index 3e534b5f97..7ce1b6c05c 100644 --- a/LocalPackages/DuckUI/Package.swift +++ b/LocalPackages/DuckUI/Package.swift @@ -31,7 +31,7 @@ let package = Package( targets: ["DuckUI"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "103.0.1"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "103.0.2"), ], targets: [ .target( diff --git a/LocalPackages/SyncUI/Package.swift b/LocalPackages/SyncUI/Package.swift index 88f36dd10e..01b217c311 100644 --- a/LocalPackages/SyncUI/Package.swift +++ b/LocalPackages/SyncUI/Package.swift @@ -33,7 +33,7 @@ let package = Package( ], dependencies: [ .package(path: "../DuckUI"), - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "103.0.1"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "103.0.2"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ diff --git a/LocalPackages/Waitlist/Package.swift b/LocalPackages/Waitlist/Package.swift index cd163e3f2b..077881488e 100644 --- a/LocalPackages/Waitlist/Package.swift +++ b/LocalPackages/Waitlist/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["Waitlist", "WaitlistMocks"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "103.0.1"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "103.0.2"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ From 590c58676595c72459429773a7d7debf89147440 Mon Sep 17 00:00:00 2001 From: Graeme Arthur Date: Mon, 29 Jan 2024 23:19:33 +0100 Subject: [PATCH 55/70] Reenable toggle on disallowing vpn (#2404) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task/Issue URL: https://app.asana.com/0/0/1206463994034099/f Description: When “Don’t Allow” is tapped, the dialog is dismissed and the toggle is greyed out. We have to back out of the screen and reopen the VPN screen to give it another attempt. Reenable the button again when the VPN permission is denied. This can be solved by simply reenabling the toggle after it’s been disabled for 2 seconds, something we were already sort of doing to stop spamming. This just slightly reworks the logic to cover more scenarios. --- DuckDuckGo/NetworkProtectionStatusViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo/NetworkProtectionStatusViewModel.swift b/DuckDuckGo/NetworkProtectionStatusViewModel.swift index 881d49e8b2..ab3ca1f57b 100644 --- a/DuckDuckGo/NetworkProtectionStatusViewModel.swift +++ b/DuckDuckGo/NetworkProtectionStatusViewModel.swift @@ -156,7 +156,7 @@ final class NetworkProtectionStatusViewModel: ObservableObject { // Set up a delayed publisher to fire just once that reenables the toggle // Each event cancels the previous delayed publisher - isLoadingPublisher + $shouldDisableToggle .filter { $0 } .map { Just(!$0) From bd2487c8476266adcae62ec1fa9ada93641aa034 Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Mon, 29 Jan 2024 17:46:48 -0800 Subject: [PATCH 56/70] Improve waitlist invite code checks (#2398) Task/Issue URL: https://app.asana.com/0/1199333091098016/1206460327917918/f Tech Design URL: CC: Description: This is the iOS PR for duckduckgo/BrowserServicesKit#639. Steps to test this PR: Check that NetP works and that you can toggle it off and on as expected Ideally wipe your NetP state clean and then try to set it up again with a fresh invite code --- Core/PixelEvent.swift | 2 ++ DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 ++-- DuckDuckGo/AppDelegate+Waitlists.swift | 2 +- .../EventMapping+NetworkProtectionError.swift | 4 ++++ DuckDuckGo/KeychainItemsDebugViewController.swift | 10 +++++++++- DuckDuckGo/MainViewController.swift | 4 ++-- .../NetworkProtectionConvenienceInitialisers.swift | 3 ++- DuckDuckGo/NetworkProtectionInviteViewModel.swift | 13 ------------- DuckDuckGo/NetworkProtectionRootView.swift | 2 +- LocalPackages/DuckUI/Package.swift | 2 +- LocalPackages/SyncUI/Package.swift | 2 +- LocalPackages/Waitlist/Package.swift | 2 +- .../NetworkProtectionPacketTunnelProvider.swift | 4 ++++ 14 files changed, 31 insertions(+), 25 deletions(-) diff --git a/Core/PixelEvent.swift b/Core/PixelEvent.swift index 8e449ec840..92abe4c7ac 100644 --- a/Core/PixelEvent.swift +++ b/Core/PixelEvent.swift @@ -312,6 +312,7 @@ extension Pixel { case networkProtectionKeychainErrorFailedToCastKeychainValueToData case networkProtectionKeychainReadError case networkProtectionKeychainWriteError + case networkProtectionKeychainUpdateError case networkProtectionKeychainDeleteError case networkProtectionWireguardErrorCannotLocateTunnelFileDescriptor @@ -810,6 +811,7 @@ extension Pixel.Event { case .networkProtectionKeychainErrorFailedToCastKeychainValueToData: return "m_netp_keychain_error_failed_to_cast_keychain_value_to_data" case .networkProtectionKeychainReadError: return "m_netp_keychain_error_read_failed" case .networkProtectionKeychainWriteError: return "m_netp_keychain_error_write_failed" + case .networkProtectionKeychainUpdateError: return "m_netp_keychain_error_update_failed" case .networkProtectionKeychainDeleteError: return "m_netp_keychain_error_delete_failed" case .networkProtectionWireguardErrorCannotLocateTunnelFileDescriptor: return "m_netp_wireguard_error_cannot_locate_tunnel_file_descriptor" case .networkProtectionWireguardErrorInvalidState: return "m_netp_wireguard_error_invalid_state" diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 15116bfef5..9d2f358bca 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -9977,7 +9977,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 103.0.2; + version = 104.0.0; }; }; C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 380a78393d..b0872e7a5c 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "1f7a1b5fe1c404d331fe34e55c99296828107c1d", - "version" : "103.0.2" + "revision" : "872090e651ad8e26ca9903a95f3d094b21d48e23", + "version" : "104.0.0" } }, { diff --git a/DuckDuckGo/AppDelegate+Waitlists.swift b/DuckDuckGo/AppDelegate+Waitlists.swift index 598131d836..9370756149 100644 --- a/DuckDuckGo/AppDelegate+Waitlists.swift +++ b/DuckDuckGo/AppDelegate+Waitlists.swift @@ -51,7 +51,7 @@ extension AppDelegate { VPNWaitlist.shared.fetchInviteCodeIfAvailable { [weak self] error in guard error == nil else { #if !DEBUG - if error == .alreadyHasInviteCode { + if error == .alreadyHasInviteCode, UIApplication.shared.applicationState == .active { // If the user already has an invite code but their auth token has gone missing, attempt to redeem it again. let tokenStore = NetworkProtectionKeychainTokenStore() let waitlistStorage = VPNWaitlist.shared.waitlistStorage diff --git a/DuckDuckGo/EventMapping+NetworkProtectionError.swift b/DuckDuckGo/EventMapping+NetworkProtectionError.swift index a139b4c9a2..2f465c4113 100644 --- a/DuckDuckGo/EventMapping+NetworkProtectionError.swift +++ b/DuckDuckGo/EventMapping+NetworkProtectionError.swift @@ -61,6 +61,10 @@ extension EventMapping where Event == NetworkProtectionError { pixelEvent = .networkProtectionKeychainWriteError params[PixelParameters.keychainFieldName] = field params[PixelParameters.keychainErrorCode] = String(status) + case .keychainUpdateError(field: let field, status: let status): + pixelEvent = .networkProtectionKeychainUpdateError + params[PixelParameters.keychainFieldName] = field + params[PixelParameters.keychainErrorCode] = String(status) case .keychainDeleteError(status: let status): pixelEvent = .networkProtectionKeychainDeleteError params[PixelParameters.keychainErrorCode] = String(status) diff --git a/DuckDuckGo/KeychainItemsDebugViewController.swift b/DuckDuckGo/KeychainItemsDebugViewController.swift index 8caa29ec13..86bdf02dd0 100644 --- a/DuckDuckGo/KeychainItemsDebugViewController.swift +++ b/DuckDuckGo/KeychainItemsDebugViewController.swift @@ -26,6 +26,7 @@ private struct KeychainItem { let secClass: SecClass let service: String? let account: String? + let accessGroup: String? let valueData: Data? let creationDate: Any? let modificationDate: Any? @@ -39,6 +40,7 @@ private struct KeychainItem { return """ Service: \(service ?? "nil") Account: \(account ?? "nil") + Access Group: \(accessGroup ?? "nil") Value as String: \(value ?? "nil") Value data: \(String(describing: valueData)) Creation date: \(String(describing: creationDate)) @@ -54,7 +56,8 @@ private enum SecClass: CaseIterable { case classCertificate case classKey case classIdentity - + case accessGroup + var secClassCFString: CFString { switch self { case .internetPassword: @@ -67,6 +70,8 @@ private enum SecClass: CaseIterable { return kSecClassKey case .classIdentity: return kSecClassIdentity + case .accessGroup: + return kSecAttrAccessGroup } } @@ -82,6 +87,8 @@ private enum SecClass: CaseIterable { return "kSecClassKey" case .classIdentity: return "kSecClassIdentity" + case .accessGroup: + return "kSecAttrAccessGroup" } } @@ -106,6 +113,7 @@ private enum SecClass: CaseIterable { KeychainItem(secClass: self, service: $0[kSecAttrService as String] as? String, account: $0[kSecAttrAccount as String] as? String, + accessGroup: $0[kSecAttrAccessGroup as String] as? String, valueData: $0[kSecValueData as String] as? Data, creationDate: $0[kSecAttrCreationDate as String, default: "no creation"], modificationDate: $0[kSecAttrModificationDate as String, default: "no modification"]) diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index ea83e4a139..85a9d33e09 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -252,7 +252,7 @@ class MainViewController: UIViewController { addLaunchTabNotificationObserver() subscribeToEmailProtectionStatusNotifications() -#if NETWORK_PROTECTION +#if NETWORK_PROTECTION && SUBSCRIPTION subscribeToNetworkProtectionSubscriptionEvents() #endif @@ -1238,7 +1238,7 @@ class MainViewController: UIViewController { .store(in: &emailCancellables) } -#if NETWORK_PROTECTION +#if NETWORK_PROTECTION && SUBSCRIPTION private func subscribeToNetworkProtectionSubscriptionEvents() { NotificationCenter.default.publisher(for: .accountDidSignIn) .receive(on: DispatchQueue.main) diff --git a/DuckDuckGo/NetworkProtectionConvenienceInitialisers.swift b/DuckDuckGo/NetworkProtectionConvenienceInitialisers.swift index 18e83a83a9..8698a6df68 100644 --- a/DuckDuckGo/NetworkProtectionConvenienceInitialisers.swift +++ b/DuckDuckGo/NetworkProtectionConvenienceInitialisers.swift @@ -53,11 +53,12 @@ extension NetworkProtectionKeychainTokenStore { } extension NetworkProtectionCodeRedemptionCoordinator { - convenience init() { + convenience init(isManualCodeRedemptionFlow: Bool = false) { let settings = VPNSettings(defaults: .networkProtectionGroupDefaults) self.init( environment: settings.selectedEnvironment, tokenStore: NetworkProtectionKeychainTokenStore(), + isManualCodeRedemptionFlow: isManualCodeRedemptionFlow, errorEvents: .networkProtectionAppDebugEvents ) } diff --git a/DuckDuckGo/NetworkProtectionInviteViewModel.swift b/DuckDuckGo/NetworkProtectionInviteViewModel.swift index 62b82becb7..0ee06a582e 100644 --- a/DuckDuckGo/NetworkProtectionInviteViewModel.swift +++ b/DuckDuckGo/NetworkProtectionInviteViewModel.swift @@ -84,19 +84,6 @@ final class NetworkProtectionInviteViewModel: ObservableObject { completion() } - // MARK: Dev only. Will be removed during https://app.asana.com/0/0/1205084446087078/f - - @MainActor - func clear() async { - errorText = "" - do { - try NetworkProtectionKeychainTokenStore().deleteToken() - updateAuthenticatedText() - } catch { - errorText = "Could not clear token" - } - } - @Published var redeemedText: String? private func updateAuthenticatedText() { diff --git a/DuckDuckGo/NetworkProtectionRootView.swift b/DuckDuckGo/NetworkProtectionRootView.swift index 0ec4d60535..0be2ed7b31 100644 --- a/DuckDuckGo/NetworkProtectionRootView.swift +++ b/DuckDuckGo/NetworkProtectionRootView.swift @@ -29,7 +29,7 @@ struct NetworkProtectionRootView: View { var body: some View { let inviteViewModel = NetworkProtectionInviteViewModel( - redemptionCoordinator: NetworkProtectionCodeRedemptionCoordinator(), + redemptionCoordinator: NetworkProtectionCodeRedemptionCoordinator(isManualCodeRedemptionFlow: true), completion: inviteCompletion ) switch model.initialViewKind { diff --git a/LocalPackages/DuckUI/Package.swift b/LocalPackages/DuckUI/Package.swift index 7ce1b6c05c..5444c96dd6 100644 --- a/LocalPackages/DuckUI/Package.swift +++ b/LocalPackages/DuckUI/Package.swift @@ -31,7 +31,7 @@ let package = Package( targets: ["DuckUI"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "103.0.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "104.0.0"), ], targets: [ .target( diff --git a/LocalPackages/SyncUI/Package.swift b/LocalPackages/SyncUI/Package.swift index 01b217c311..010012fb5c 100644 --- a/LocalPackages/SyncUI/Package.swift +++ b/LocalPackages/SyncUI/Package.swift @@ -33,7 +33,7 @@ let package = Package( ], dependencies: [ .package(path: "../DuckUI"), - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "103.0.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "104.0.0"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ diff --git a/LocalPackages/Waitlist/Package.swift b/LocalPackages/Waitlist/Package.swift index 077881488e..e3ce289fce 100644 --- a/LocalPackages/Waitlist/Package.swift +++ b/LocalPackages/Waitlist/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["Waitlist", "WaitlistMocks"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "103.0.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "104.0.0"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ diff --git a/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift b/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift index 180342ab9c..f127d65a7d 100644 --- a/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift +++ b/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift @@ -140,6 +140,10 @@ final class NetworkProtectionPacketTunnelProvider: PacketTunnelProvider { pixelEvent = .networkProtectionKeychainWriteError params[PixelParameters.keychainFieldName] = field params[PixelParameters.keychainErrorCode] = String(status) + case .keychainUpdateError(let field, let status): + pixelEvent = .networkProtectionKeychainUpdateError + params[PixelParameters.keychainFieldName] = field + params[PixelParameters.keychainErrorCode] = String(status) case .keychainDeleteError(let status): // TODO: Check whether field needed here pixelEvent = .networkProtectionKeychainDeleteError params[PixelParameters.keychainErrorCode] = String(status) From 6c58c3c14a24afd75e6851ac3f7ac191330f9442 Mon Sep 17 00:00:00 2001 From: bwaresiak Date: Tue, 30 Jan 2024 10:06:38 +0100 Subject: [PATCH 57/70] Fix tab loading (#2410) Task/Issue URL: https://app.asana.com/0/414709148257752/1206457470836092/f Tech Design URL: CC: Description: Fix tab loading on first navigation by ensuring assets are processed from the moment app is created. --- DuckDuckGo/AppDelegate.swift | 2 ++ DuckDuckGo/ContentBlockingUpdating.swift | 4 ++-- DuckDuckGo/MainViewController.swift | 3 --- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index 2040715f88..0129f3c481 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -108,6 +108,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } ContentBlocking.shared.onCriticalError = presentPreemptiveCrashAlert + // Explicitly prepare ContentBlockingUpdating instance before Tabs are created + _ = ContentBlockingUpdating.shared // Can be removed after a couple of versions cleanUpMacPromoExperiment2() diff --git a/DuckDuckGo/ContentBlockingUpdating.swift b/DuckDuckGo/ContentBlockingUpdating.swift index 4c9d70121b..ef1fb29629 100644 --- a/DuckDuckGo/ContentBlockingUpdating.swift +++ b/DuckDuckGo/ContentBlockingUpdating.swift @@ -37,7 +37,7 @@ extension ContentBlockerRulesIdentifier.Difference { } public final class ContentBlockingUpdating { - fileprivate static let shared = ContentBlockingUpdating() + static let shared = ContentBlockingUpdating() private typealias Update = ContentBlockerRulesManager.UpdateEvent struct NewContent: UserContentControllerNewContent { @@ -55,7 +55,7 @@ public final class ContentBlockingUpdating { private(set) var userContentBlockingAssets: AnyPublisher! - init(appSettings: AppSettings = AppDependencyProvider.shared.appSettings, + init(appSettings: AppSettings = AppUserDefaults(), contentBlockerRulesManager: ContentBlockerRulesManagerProtocol = ContentBlocking.shared.contentBlockingManager, privacyConfigurationManager: PrivacyConfigurationManaging = ContentBlocking.shared.privacyConfigurationManager) { diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 756187202d..7522fcf2a8 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -847,9 +847,6 @@ class MainViewController: UIViewController { if tabManager.current(createIfNeeded: true) == nil { fatalError("failed to create tab") } - - // Likely this hasn't happened yet so the publishers won't be loaded and will block the webview from loading - _ = ContentBlocking.shared.contentBlockingManager.scheduleCompilation() } guard let tab = currentTab else { fatalError("no tab") } From 056bb912d0988f56475f720129f8e9585834a70d Mon Sep 17 00:00:00 2001 From: bwaresiak Date: Tue, 30 Jan 2024 10:21:32 +0100 Subject: [PATCH 58/70] Release/7.106.0-3 changes (#2411) --- DuckDuckGo.xcodeproj/project.pbxproj | 56 ++++++++++----------- fastlane/metadata/default/release_notes.txt | 1 + 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 042be06694..b157a2bf12 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -8163,7 +8163,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProvider.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -8200,7 +8200,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8292,7 +8292,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8320,7 +8320,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8470,7 +8470,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8496,7 +8496,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; INFOPLIST_FILE = DuckDuckGo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8561,7 +8561,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -8596,7 +8596,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8630,7 +8630,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8661,7 +8661,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8948,7 +8948,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8979,7 +8979,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -9008,7 +9008,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -9042,7 +9042,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -9073,7 +9073,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProvider.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -9106,11 +9106,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 2; + DYLIB_CURRENT_VERSION = 3; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9348,7 +9348,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGoAlpha.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9375,7 +9375,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9408,7 +9408,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9446,7 +9446,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9482,7 +9482,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9517,11 +9517,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 2; + DYLIB_CURRENT_VERSION = 3; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9695,11 +9695,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 2; + DYLIB_CURRENT_VERSION = 3; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9728,10 +9728,10 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 2; + DYLIB_CURRENT_VERSION = 3; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; diff --git a/fastlane/metadata/default/release_notes.txt b/fastlane/metadata/default/release_notes.txt index e41e1ebb14..872a9f87b8 100644 --- a/fastlane/metadata/default/release_notes.txt +++ b/fastlane/metadata/default/release_notes.txt @@ -1,4 +1,5 @@ - You can now pull the page down to reload it. - Fixed settings not being saved in rare cases. +- Fixed a bug that sometimes prevented tabs from loading websites. - Bug fixes and other improvements. Join our fully distributed team and help raise the standard of trust online! https://duckduckgo.com/hiring From 3ba3de5f162b51c099b09c02e017677f9ae30f24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacek=20=C5=81yp?= Date: Tue, 30 Jan 2024 11:41:33 +0100 Subject: [PATCH 59/70] Changes to hotfix process (#2406) Task/Issue URL: https://app.asana.com/0/1203301625297703/1206468110851824/f CC: @bwaresiak Description: Make hotfixing easier. --- scripts/prepare_release.sh | 180 ++++++++++++++++--------------------- 1 file changed, 79 insertions(+), 101 deletions(-) diff --git a/scripts/prepare_release.sh b/scripts/prepare_release.sh index d643f1c0bf..7544315c28 100755 --- a/scripts/prepare_release.sh +++ b/scripts/prepare_release.sh @@ -3,10 +3,8 @@ set -eo pipefail mute=">/dev/null 2>&1" -version="$1" release_branch_parent="main" -tag=${version} -hotfix_branch_parent="tags/${tag}" +build_number=0 # Get the directory where the script is stored script_dir=$(dirname "$(readlink -f "$0")") @@ -16,6 +14,7 @@ base_dir="${script_dir}/.." # Output passed arguments to stderr and exit. # die() { + echo "" cat >&2 <<< "$*" exit 1 } @@ -51,44 +50,39 @@ print_usage_and_exit() { cat <<- EOF Usage: - $ $(basename "$0") [-h] [-v] + $ $(basename "$0") [-v] Current version: $(cut -d' ' -f3 < "${base_dir}/Configuration/Version.xcconfig") Options: - -h Make hotfix release. Requires the version to be the one to hotfix, and a branch with the fix as the second parameter -v Enable verbose mode + Arguments: + Specify either a version number or a hotfix branch name. + EOF die "${reason}" } read_command_line_arguments() { - number_of_arguments="$#" + local input="$1" + local version_regexp="^[0-9]+(\.[0-9]+)*$" - local regexp="^[0-9]+(\.[0-9]+)*$" - if [[ ! "$1" =~ $regexp ]]; then - print_usage_and_exit "💥 Error: Wrong app version specified" + if [ -z "$input" ]; then + print_usage_and_exit "💥 Error: Missing argument" fi - if [[ "$#" -ne 1 ]]; then - if [[ "$2" == -* ]]; then - shift 1 - else - fix_branch=$2 - shift 2 - fi + if [[ $input =~ $version_regexp ]]; then + process_release "$input" + else + process_hotfix "$input" fi - release_branch_prefix="release" + shift 1 - while getopts 'hv' option; do + while getopts 'v' option; do case "${option}" in - h) # hotfix - is_hotfix=1 - release_branch_prefix="hotfix" - ;; - v) # verbose + v) mute= ;; *) @@ -96,34 +90,37 @@ read_command_line_arguments() { ;; esac done +} + +process_release() { + version="$1" + release_branch="release/${version}" - release_branch="${release_branch_prefix}/${version}" - changes_branch="${release_branch}-changes" + echo "Processing version number: $version" if release_branch_exists; then is_subsequent_release=1 fi +} - shift $((OPTIND-1)) +process_hotfix() { + local input="$1" + echo "Processing hotfix branch name: $input" - if [[ $is_hotfix ]]; then - if [[ $number_of_arguments -ne 3 ]]; then - print_usage_and_exit "💥 Error: Wrong number of arguments. Did you specify a fix branch?" - fi + is_hotfix=1 + release_branch="$input" - version_to_hotfix=${version} - IFS='.' read -ra arrIN <<< "$version" - patch_number=$((arrIN[2] + 1)) - version="${arrIN[0]}.${arrIN[1]}.$patch_number" + if ! release_branch_exists; then + die "💥 Error: Hotfix branch ${release_branch} does not exist" fi } release_branch_exists() { - if git show-ref --verify --quiet "refs/heads/$release_branch"; then - return 0 - else - return 1 - fi + if git show-ref --verify --quiet "refs/heads/${release_branch}"; then + return 0 + else + return 1 + fi } stash() { @@ -132,52 +129,47 @@ stash() { echo "✅" } -assert_clean_state() { - if git show-ref --quiet "refs/heads/${release_branch}"; then - die "💥 Error: Branch ${release_branch} already exists" - fi - if git show-ref --quiet "refs/heads/${changes_branch}"; then - die "💥 Error: Branch ${changes_branch} already exists" - fi -} - -assert_hotfix_tag_exists_if_necessary() { - printf '%s' "Checking tag to hotfix ... " - - # Make sure tag is available locally if it exists - eval git fetch origin "+refs/tags/${tag}:refs/tags/${tag}" "$mute" +create_release_branch() { + printf '%s' "Creating release branch ... " + eval git checkout "${release_branch_parent}" "$mute" + eval git pull "$mute" - if [[ $(git tag -l "$version_to_hotfix" "$mute") ]]; then - echo "✅" - else - die "💥 Error: Tag ${version_to_hotfix} does not exist" + if [[ ! $is_subsequent_release && ! $is_hotfix ]]; then + if git show-ref --quiet "refs/heads/${release_branch}"; then + die "💥 Error: Branch ${release_branch} already exists" + fi fi -} -create_release_and_changes_branches() { - if [[ ${is_hotfix} ]]; then - printf '%s' "Creating hotfix branch ... " - eval git checkout "${hotfix_branch_parent}" "$mute" - else - printf '%s' "Creating release branch ... " - eval git checkout ${release_branch_parent} "$mute" - eval git pull "$mute" - fi eval git checkout -b "${release_branch}" "$mute" - eval git checkout -b "${changes_branch}" "$mute" echo "✅" } -create_changes_branch() { - printf '%s' "Creating changes branch ... " +create_build_branch() { + printf '%s' "Creating build branch ... " eval git checkout "${release_branch}" "$mute" eval git pull "$mute" - eval git checkout -b "${changes_branch}" "$mute" + + local latest_build_number + latest_build_number=$(agvtool what-version -terse) + build_number=$((latest_build_number + 1)) + build_branch="${release_branch}-build-${build_number}" + + if git show-ref --quiet "refs/heads/${build_branch}"; then + die "💥 Error: Branch ${build_branch} already exists" + fi + + eval git checkout -b "${build_branch}" "$mute" echo "✅" } update_marketing_version() { printf '%s' "Setting app version ... " + + version=$(cut -d' ' -f3 < "${base_dir}/Configuration/Version.xcconfig") + if [[ $is_hotfix ]]; then + version=$(bump_patch_number "$version") + fi + "$script_dir/set_version.sh" "${version}" git add "${base_dir}/Configuration/Version.xcconfig" \ "${base_dir}/DuckDuckGo/Settings.bundle/Root.plist" @@ -185,6 +177,12 @@ update_marketing_version() { echo "✅" } +bump_patch_number() { + IFS='.' read -ra arrIN <<< "$1" + local patch_number=$((arrIN[2] + 1)) + echo "${arrIN[0]}.${arrIN[1]}.$patch_number" +} + update_build_version() { echo "Setting build version ..." local username @@ -228,27 +226,13 @@ update_release_notes() { fi } -merge_fix_branch_if_necessary() { - if [[ ! $is_hotfix ]]; then - return - fi - - printf '%s' "Merging fix branch ... " - eval git checkout "${fix_branch}" "$mute" - eval git pull "$mute" - - eval git checkout "${changes_branch}" "$mute" - eval git merge "${fix_branch}" "$mute" - echo "✅" -} - create_pull_request() { printf '%s' "Creating PR ... " - if [[ ! $is_subsequent_release ]]; then - eval git push origin "${release_branch}" "$mute" + if [[ ! $is_subsequent_release && ! $is_hotfix ]]; then + eval git push -u origin "${release_branch}" "$mute" fi - eval git push origin "${changes_branch}" "$mute" - eval gh pr create --title \"Release "${version}"\" --base "${release_branch}" --assignee @me "$mute" --body-file "${script_dir}/assets/prepare-release-description" + eval git push -u origin "${build_branch}" "$mute" + eval gh pr create --title \"Release "${version}-${build_number}"\" --base "${release_branch}" --label \"Merge triggers release\" --assignee @me "$mute" --body-file "${script_dir}/assets/prepare-release-description" eval gh pr view --web "$mute" echo "✅" } @@ -259,29 +243,23 @@ main() { assert_gh_installed_and_authenticated read_command_line_arguments "$@" - stash if [[ $is_subsequent_release ]]; then - create_changes_branch + create_build_branch elif [[ $is_hotfix ]]; then - assert_clean_state - assert_hotfix_tag_exists_if_necessary - create_release_and_changes_branches + create_build_branch update_marketing_version - else - assert_clean_state - create_release_and_changes_branches + else # regular release + create_release_branch + create_build_branch update_marketing_version update_embedded_files fi update_build_version - update_release_notes - merge_fix_branch_if_necessary - - #create_pull_request + create_pull_request } main "$@" \ No newline at end of file From 636b6a1bb7b84b26bef33313eb2b5cabef4c51cd Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Tue, 30 Jan 2024 02:51:42 -0800 Subject: [PATCH 60/70] Add VPN redemption retry event (#2409) Task/Issue URL: https://app.asana.com/0/414235014887631/1206472504459349/f Tech Design URL: CC: Description: This PR adds a pixel that sends when the VPN invite code retry logic kicks in. --- Core/PixelEvent.swift | 2 ++ DuckDuckGo/AppDelegate+Waitlists.swift | 14 +++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Core/PixelEvent.swift b/Core/PixelEvent.swift index 92abe4c7ac..87dbdbce26 100644 --- a/Core/PixelEvent.swift +++ b/Core/PixelEvent.swift @@ -342,6 +342,7 @@ extension Pixel { case networkProtectionWaitlistTermsAccepted case networkProtectionWaitlistNotificationShown case networkProtectionWaitlistNotificationLaunched + case networkProtectionWaitlistRetriedInviteCodeRedemption case networkProtectionGeoswitchingOpened case networkProtectionGeoswitchingSetNearest @@ -835,6 +836,7 @@ extension Pixel.Event { case .networkProtectionWaitlistTermsAccepted: return "m_netp_waitlist_terms_accepted" case .networkProtectionWaitlistNotificationShown: return "m_netp_waitlist_notification_shown" case .networkProtectionWaitlistNotificationLaunched: return "m_netp_waitlist_notification_launched" + case .networkProtectionWaitlistRetriedInviteCodeRedemption: return "m_netp_waitlist_retried_invite_code_redemption" case .networkProtectionGeoswitchingOpened: return "m_netp_imp_geoswitching" case .networkProtectionGeoswitchingSetNearest: return "m_netp_ev_geoswitching_set_nearest" diff --git a/DuckDuckGo/AppDelegate+Waitlists.swift b/DuckDuckGo/AppDelegate+Waitlists.swift index 9370756149..b377d170b7 100644 --- a/DuckDuckGo/AppDelegate+Waitlists.swift +++ b/DuckDuckGo/AppDelegate+Waitlists.swift @@ -56,6 +56,18 @@ extension AppDelegate { let tokenStore = NetworkProtectionKeychainTokenStore() let waitlistStorage = VPNWaitlist.shared.waitlistStorage if let inviteCode = waitlistStorage.getWaitlistInviteCode(), !tokenStore.isFeatureActivated { + let pixel: Pixel.Event = .networkProtectionWaitlistRetriedInviteCodeRedemption + + do { + if let token = try tokenStore.fetchToken() { + DailyPixel.fireDailyAndCount(pixel: pixel, withAdditionalParameters: [ "tokenState": "found" ]) + } else { + DailyPixel.fireDailyAndCount(pixel: pixel, withAdditionalParameters: [ "tokenState": "nil" ]) + } + } catch { + DailyPixel.fireDailyAndCount(pixel: pixel, error: error, withAdditionalParameters: [ "tokenState": "error" ]) + } + self?.fetchVPNWaitlistAuthToken(inviteCode: inviteCode) } } @@ -96,7 +108,7 @@ extension AppDelegate { try await NetworkProtectionCodeRedemptionCoordinator().redeem(inviteCode) VPNWaitlist.shared.sendInviteCodeAvailableNotification() - DailyPixel.fire(pixel: .networkProtectionWaitlistNotificationShown) + DailyPixel.fireDailyAndCount(pixel: .networkProtectionWaitlistNotificationShown) } catch {} } } From dac366d2d42009f86f0e13172baf0032bb62ce72 Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Tue, 30 Jan 2024 02:59:05 -0800 Subject: [PATCH 61/70] Don't set dryRun for alpha builds (#2412) Task/Issue URL: https://app.asana.com/0/414235014887631/1206474118520097/f Tech Design URL: CC: Description: This PR makes sure that alpha builds can send pixels. --- DuckDuckGo/AppDelegate.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index 0129f3c481..e997dcd121 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -101,11 +101,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } #endif - if isDebugBuild { - Pixel.isDryRun = true - } else { - Pixel.isDryRun = false - } +#if DEBUG && !ALPHA + Pixel.isDryRun = true +#else + Pixel.isDryRun = false +#endif ContentBlocking.shared.onCriticalError = presentPreemptiveCrashAlert // Explicitly prepare ContentBlockingUpdating instance before Tabs are created From d76bf546da46e7e876aeb07ea4fd8aece6214cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacek=20=C5=81yp?= Date: Tue, 30 Jan 2024 14:28:54 +0100 Subject: [PATCH 62/70] Add error codes to site breakage reports (#2413) Task/Issue URL: https://app.asana.com/0/72649045549333/1205982573174191/f Tech Design URL: https://app.asana.com/0/0/1206225370438701/f Description: Send error and http status codes within reports --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 6 +++--- .../PrivacyDashboardViewController.swift | 7 +++++-- DuckDuckGo/TabViewController.swift | 16 ++++++++++------ DuckDuckGoTests/BrokenSiteReportingTests.swift | 6 ++++-- LocalPackages/DuckUI/Package.swift | 2 +- LocalPackages/SyncUI/Package.swift | 2 +- LocalPackages/Waitlist/Package.swift | 2 +- 8 files changed, 26 insertions(+), 17 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 5d643bac6a..415c2f063b 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -9977,7 +9977,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 104.0.0; + version = 104.0.2; }; }; C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index bafadaff66..31faa8367e 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "872090e651ad8e26ca9903a95f3d094b21d48e23", - "version" : "104.0.0" + "revision" : "381cd794bd00b388e8db4722af0a78cb8c729ef9", + "version" : "104.0.2" } }, { @@ -156,7 +156,7 @@ { "identity" : "trackerradarkit", "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/TrackerRadarKit", + "location" : "https://github.com/duckduckgo/TrackerRadarKit.git", "state" : { "revision" : "a6b7ba151d9dc6684484f3785293875ec01cc1ff", "version" : "1.2.2" diff --git a/DuckDuckGo/PrivacyDashboard/PrivacyDashboardViewController.swift b/DuckDuckGo/PrivacyDashboard/PrivacyDashboardViewController.swift index 48a5b0d6e0..586c8e99d9 100644 --- a/DuckDuckGo/PrivacyDashboard/PrivacyDashboardViewController.swift +++ b/DuckDuckGo/PrivacyDashboard/PrivacyDashboardViewController.swift @@ -219,6 +219,8 @@ extension PrivacyDashboardViewController { let ampURLString: String let urlParametersRemoved: Bool let isDesktop: Bool + let error: Error? + let httpStatusCode: Int? } enum WebsiteBreakageError: Error { @@ -252,7 +254,8 @@ extension PrivacyDashboardViewController { reportFlow: source, siteType: breakageAdditionalInfo.isDesktop ? .desktop : .mobile, atb: StatisticsUserDefaults().atb ?? "", - model: UIDevice.current.model) - + model: UIDevice.current.model, + error: breakageAdditionalInfo.error, + httpStatusCode: breakageAdditionalInfo.httpStatusCode) } } diff --git a/DuckDuckGo/TabViewController.swift b/DuckDuckGo/TabViewController.swift index f0d388a281..0e58ca1c50 100644 --- a/DuckDuckGo/TabViewController.swift +++ b/DuckDuckGo/TabViewController.swift @@ -110,6 +110,7 @@ class TabViewController: UIViewController { private var httpsForced: Bool = false private var lastUpgradedURL: URL? private var lastError: Error? + private var lastHttpStatusCode: Int? private var shouldReloadOnError = false private var failingUrls = Set() private var urlProvidedBasicAuthCredential: (credential: URLCredential, url: URL)? @@ -919,15 +920,17 @@ class TabViewController: UIViewController { return nil } return PrivacyDashboardViewController.BreakageAdditionalInfo(currentURL: currentURL, - httpsForced: httpsForced, - ampURLString: linkProtection.lastAMPURLString ?? "", - urlParametersRemoved: linkProtection.urlParametersRemoved, - isDesktop: tabModel.isDesktop) + httpsForced: httpsForced, + ampURLString: linkProtection.lastAMPURLString ?? "", + urlParametersRemoved: linkProtection.urlParametersRemoved, + isDesktop: tabModel.isDesktop, + error: lastError, + httpStatusCode: lastHttpStatusCode) } - + public func print() { let printFormatter = webView.viewPrintFormatter() - + let printInfo = UIPrintInfo(dictionary: nil) printInfo.jobName = Bundle.main.infoDictionary!["CFBundleName"] as? String ?? "DuckDuckGo" printInfo.outputType = .general @@ -1062,6 +1065,7 @@ extension TabViewController: WKNavigationDelegate { let httpResponse = navigationResponse.response as? HTTPURLResponse let isSuccessfulResponse = httpResponse?.isSuccessfulResponse ?? false + lastHttpStatusCode = httpResponse?.statusCode let didMarkAsInternal = internalUserDecider.markUserAsInternalIfNeeded(forUrl: webView.url, response: httpResponse) if didMarkAsInternal { diff --git a/DuckDuckGoTests/BrokenSiteReportingTests.swift b/DuckDuckGoTests/BrokenSiteReportingTests.swift index 40c578f113..838b3478bf 100644 --- a/DuckDuckGoTests/BrokenSiteReportingTests.swift +++ b/DuckDuckGoTests/BrokenSiteReportingTests.swift @@ -90,8 +90,10 @@ final class BrokenSiteReportingTests: XCTestCase { reportFlow: .dashboard, siteType: .mobile, atb: "", - model: test.model ?? "") - + model: test.model ?? "", + error: nil, + httpStatusCode: nil) + let reporter = WebsiteBreakageReporter(pixelHandler: { params in for expectedParam in test.expectReportURLParams { diff --git a/LocalPackages/DuckUI/Package.swift b/LocalPackages/DuckUI/Package.swift index 5444c96dd6..2b87e6ee97 100644 --- a/LocalPackages/DuckUI/Package.swift +++ b/LocalPackages/DuckUI/Package.swift @@ -31,7 +31,7 @@ let package = Package( targets: ["DuckUI"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "104.0.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "104.0.2"), ], targets: [ .target( diff --git a/LocalPackages/SyncUI/Package.swift b/LocalPackages/SyncUI/Package.swift index 010012fb5c..76d3fa59b6 100644 --- a/LocalPackages/SyncUI/Package.swift +++ b/LocalPackages/SyncUI/Package.swift @@ -33,7 +33,7 @@ let package = Package( ], dependencies: [ .package(path: "../DuckUI"), - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "104.0.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "104.0.2"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ diff --git a/LocalPackages/Waitlist/Package.swift b/LocalPackages/Waitlist/Package.swift index e3ce289fce..3a6e951abd 100644 --- a/LocalPackages/Waitlist/Package.swift +++ b/LocalPackages/Waitlist/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["Waitlist", "WaitlistMocks"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "104.0.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "104.0.2"), .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ From 5dbcc43c4b374e73d06bbfbf0ba88430fe3221ec Mon Sep 17 00:00:00 2001 From: Graeme Arthur Date: Tue, 30 Jan 2024 16:19:38 +0100 Subject: [PATCH 63/70] Don't crash when AppRatingPromptEntity fetch errors (#2388) --- Core/DailyPixel.swift | 15 ++++++- Core/PixelEvent.swift | 4 ++ DuckDuckGo/AppRatingPrompt.swift | 42 ++++++++++++------- .../AppRatingPromptStorageTests.swift | 3 +- DuckDuckGoTests/AppRatingPromptTests.swift | 18 +++++--- 5 files changed, 59 insertions(+), 23 deletions(-) diff --git a/Core/DailyPixel.swift b/Core/DailyPixel.swift index af326479de..ef878eae28 100644 --- a/Core/DailyPixel.swift +++ b/Core/DailyPixel.swift @@ -71,10 +71,16 @@ public final class DailyPixel { public static func fireDailyAndCount(pixel: Pixel.Event, error: Swift.Error? = nil, withAdditionalParameters params: [String: String] = [:], + includedParameters: [Pixel.QueryParameters] = [.atb, .appVersion], onDailyComplete: @escaping (Swift.Error?) -> Void = { _ in }, onCountComplete: @escaping (Swift.Error?) -> Void = { _ in }) { if !pixel.hasBeenFiredToday(dailyPixelStorage: storage) { - Pixel.fire(pixelNamed: pixel.name + "_d", withAdditionalParameters: params, onComplete: onDailyComplete) + Pixel.fire( + pixelNamed: pixel.name + "_d", + withAdditionalParameters: params, + includedParameters: includedParameters, + onComplete: onDailyComplete + ) } else { onDailyComplete(Error.alreadyFired) } @@ -83,7 +89,12 @@ public final class DailyPixel { if let error { newParams.appendErrorPixelParams(error: error) } - Pixel.fire(pixelNamed: pixel.name + "_c", withAdditionalParameters: newParams, onComplete: onCountComplete) + Pixel.fire( + pixelNamed: pixel.name + "_c", + withAdditionalParameters: newParams, + includedParameters: includedParameters, + onComplete: onCountComplete + ) } private static func updatePixelLastFireDate(pixel: Pixel.Event) { diff --git a/Core/PixelEvent.swift b/Core/PixelEvent.swift index 87dbdbce26..babb0b9179 100644 --- a/Core/PixelEvent.swift +++ b/Core/PixelEvent.swift @@ -523,6 +523,8 @@ extension Pixel { case emailIncontextModalExitEarlyContinue case compilationFailed + + case appRatingPromptFetchError } } @@ -1019,6 +1021,8 @@ extension Pixel.Event { // MARK: - Return user measurement case .debugReturnUserAddATB: return "m_debug_return_user_add_atb" case .debugReturnUserUpdateATB: return "m_debug_return_user_update_atb" + + case .appRatingPromptFetchError: return "m_d_app_rating_prompt_fetch_error" } } diff --git a/DuckDuckGo/AppRatingPrompt.swift b/DuckDuckGo/AppRatingPrompt.swift index 7c85994358..203cf9c864 100644 --- a/DuckDuckGo/AppRatingPrompt.swift +++ b/DuckDuckGo/AppRatingPrompt.swift @@ -25,8 +25,8 @@ protocol AppRatingPromptStorage { var lastAccess: Date? { get set } - var uniqueAccessDays: Int { get set } - + var uniqueAccessDays: Int? { get set } + var lastShown: Date? { get set } } @@ -40,8 +40,8 @@ class AppRatingPrompt { } func registerUsage(onDate date: Date = Date()) { - if !date.isSameDay(storage.lastAccess) { - storage.uniqueAccessDays += 1 + if !date.isSameDay(storage.lastAccess), let currentUniqueAccessDays = storage.uniqueAccessDays { + storage.uniqueAccessDays = currentUniqueAccessDays + 1 } storage.lastAccess = date } @@ -60,33 +60,39 @@ class AppRatingPromptCoreDataStorage: AppRatingPromptStorage { var lastAccess: Date? { get { - return ratingPromptEntity().lastAccess + return ratingPromptEntity()?.lastAccess } set { - ratingPromptEntity().lastAccess = newValue + ratingPromptEntity()?.lastAccess = newValue try? context.save() } } - var uniqueAccessDays: Int { + var uniqueAccessDays: Int? { get { - return Int(ratingPromptEntity().uniqueAccessDays) + guard let ratingPromptEntity = ratingPromptEntity() else { + return nil + } + return Int(ratingPromptEntity.uniqueAccessDays) } set { - ratingPromptEntity().uniqueAccessDays = Int64(newValue) + guard let newValue else { + return + } + ratingPromptEntity()?.uniqueAccessDays = Int64(newValue) try? context.save() } } var lastShown: Date? { get { - return ratingPromptEntity().lastShown + return ratingPromptEntity()?.lastShown } set { - ratingPromptEntity().lastShown = newValue + ratingPromptEntity()?.lastShown = newValue try? context.save() } } @@ -95,14 +101,20 @@ class AppRatingPromptCoreDataStorage: AppRatingPromptStorage { public init() { } - func ratingPromptEntity() -> AppRatingPromptEntity { + func ratingPromptEntity() -> AppRatingPromptEntity? { let fetchRequest: NSFetchRequest = AppRatingPromptEntity.fetchRequest() - - guard let results = try? context.fetch(fetchRequest) else { - fatalError("Error fetching AppRatingPromptEntity") + + let results: [AppRatingPromptEntity] + + do { + results = try context.fetch(fetchRequest) + } catch { + DailyPixel.fireDailyAndCount(pixel: .appRatingPromptFetchError, error: error, includedParameters: [.appVersion]) + return nil } + if let result = results.first { return result } else { diff --git a/DuckDuckGoTests/AppRatingPromptStorageTests.swift b/DuckDuckGoTests/AppRatingPromptStorageTests.swift index 1016401122..e304b9257f 100644 --- a/DuckDuckGoTests/AppRatingPromptStorageTests.swift +++ b/DuckDuckGoTests/AppRatingPromptStorageTests.swift @@ -52,7 +52,8 @@ class AppRatingPromptStorageTests: XCTestCase { private func reset() { let storage = AppRatingPromptCoreDataStorage() - storage.context.delete(storage.ratingPromptEntity()) + guard let ratingPromptEntity = storage.ratingPromptEntity() else { return } + storage.context.delete(ratingPromptEntity) try? storage.context.save() } diff --git a/DuckDuckGoTests/AppRatingPromptTests.swift b/DuckDuckGoTests/AppRatingPromptTests.swift index 582a2ba310..899034329a 100644 --- a/DuckDuckGoTests/AppRatingPromptTests.swift +++ b/DuckDuckGoTests/AppRatingPromptTests.swift @@ -93,9 +93,18 @@ class AppRatingPromptTests: XCTestCase { appRatingPrompt.registerUsage(onDate: Date().inDays(fromNow: 1)) appRatingPrompt.registerUsage(onDate: Date().inDays(fromNow: 2)) XCTAssertTrue(appRatingPrompt.shouldPrompt(onDate: Date().inDays(fromNow: 2))) - } - + + func testWhenUniqueAccessDaysIsNilDueToFetchErrorThenShouldNotPrompt() { + let storage = AppRatingPromptStorageStub() + storage.uniqueAccessDays = nil + let appRatingPrompt = AppRatingPrompt(storage: storage) + appRatingPrompt.registerUsage(onDate: Date().inDays(fromNow: 0)) + appRatingPrompt.registerUsage(onDate: Date().inDays(fromNow: 1)) + appRatingPrompt.registerUsage(onDate: Date().inDays(fromNow: 2)) + XCTAssertFalse(appRatingPrompt.shouldPrompt(onDate: Date().inDays(fromNow: 2))) + } + func testWhenUserAccessSecondUniqueDayThenShouldNotPrompt() { let stub = AppRatingPromptStorageStub() @@ -117,15 +126,14 @@ class AppRatingPromptTests: XCTestCase { func testWhenUserAccessFirstDayThenShouldNotPrompt() { XCTAssertFalse(AppRatingPrompt(storage: AppRatingPromptStorageStub()).shouldPrompt(onDate: Date().inDays(fromNow: 0))) } - } private class AppRatingPromptStorageStub: AppRatingPromptStorage { var lastAccess: Date? - var uniqueAccessDays: Int = 0 - + var uniqueAccessDays: Int? = 0 + var lastShown: Date? } From 942386c56d65b4bbc346b67e4591e294fc71ea5f Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Tue, 30 Jan 2024 10:29:36 -0800 Subject: [PATCH 64/70] Reenable toggle on disallowing vpn (#2404) (#2416) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task/Issue URL: https://app.asana.com/0/0/1206463994034099/f Description: When “Don’t Allow” is tapped, the dialog is dismissed and the toggle is greyed out. We have to back out of the screen and reopen the VPN screen to give it another attempt. Reenable the button again when the VPN permission is denied. This can be solved by simply reenabling the toggle after it’s been disabled for 2 seconds, something we were already sort of doing to stop spamming. This just slightly reworks the logic to cover more scenarios. --- DuckDuckGo/NetworkProtectionStatusViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo/NetworkProtectionStatusViewModel.swift b/DuckDuckGo/NetworkProtectionStatusViewModel.swift index 881d49e8b2..ab3ca1f57b 100644 --- a/DuckDuckGo/NetworkProtectionStatusViewModel.swift +++ b/DuckDuckGo/NetworkProtectionStatusViewModel.swift @@ -156,7 +156,7 @@ final class NetworkProtectionStatusViewModel: ObservableObject { // Set up a delayed publisher to fire just once that reenables the toggle // Each event cancels the previous delayed publisher - isLoadingPublisher + $shouldDisableToggle .filter { $0 } .map { Just(!$0) From e3a42141be8a6f0898b51c0e60f4606f93c7dfc2 Mon Sep 17 00:00:00 2001 From: bwaresiak Date: Tue, 30 Jan 2024 20:11:07 +0100 Subject: [PATCH 65/70] Release/7.106.0-4 (#2417) --- DuckDuckGo.xcodeproj/project.pbxproj | 56 ++++++++++++++-------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index b157a2bf12..32d378f740 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -8163,7 +8163,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProvider.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -8200,7 +8200,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8292,7 +8292,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8320,7 +8320,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8470,7 +8470,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8496,7 +8496,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; INFOPLIST_FILE = DuckDuckGo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8561,7 +8561,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -8596,7 +8596,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8630,7 +8630,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8661,7 +8661,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8948,7 +8948,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8979,7 +8979,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -9008,7 +9008,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -9042,7 +9042,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -9073,7 +9073,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProvider.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -9106,11 +9106,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 3; + DYLIB_CURRENT_VERSION = 4; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9348,7 +9348,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGoAlpha.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9375,7 +9375,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9408,7 +9408,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9446,7 +9446,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9482,7 +9482,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9517,11 +9517,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 3; + DYLIB_CURRENT_VERSION = 4; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9695,11 +9695,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 3; + DYLIB_CURRENT_VERSION = 4; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9728,10 +9728,10 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 4; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 3; + DYLIB_CURRENT_VERSION = 4; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; From 74980dc3d0c1d26f1d83463899053dd7bf83648b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacek=20=C5=81yp?= Date: Tue, 30 Jan 2024 23:31:01 +0100 Subject: [PATCH 66/70] More release script fixes (#2418) Task/Issue URL: https://app.asana.com/0/1203301625297703/1206468110851824/f Description: fixes upstream branches (again). makes sure we fetch build number from TF and not locally. --- fastlane/Fastfile | 16 +++++++++++++++- fastlane/README.md | 8 ++++++++ scripts/prepare_release.sh | 17 +++++++++-------- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 841515862f..061eb92d21 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -140,9 +140,23 @@ lane :release_alpha do |options| ) end +desc 'Latest build number for version' +lane :latest_build_number_for_version do |options| + if options[:app_identifier] + app_identifier = options[:app_identifier] + end + build_number = latest_testflight_build_number( + api_key: get_api_key, + version: options[:version], + initial_build_number: 0, + username: get_username(options)) + if options[:file_name] + File.write(options[:file_name], build_number) + end +end + desc 'Increment build number based on version in App Store Connect' lane :increment_build_number_for_version do |options| - app_identifier = "com.duckduckgo.mobile.ios" if options[:app_identifier] app_identifier = options[:app_identifier] end diff --git a/fastlane/README.md b/fastlane/README.md index 5ca5e832ea..ebb133c3c9 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -77,6 +77,14 @@ Makes App Store release build and uploads it to TestFlight Makes Alpha release build and uploads it to TestFlight +### latest_build_number_for_version + +```sh +[bundle exec] fastlane latest_build_number_for_version +``` + +Latest build number for version + ### increment_build_number_for_version ```sh diff --git a/scripts/prepare_release.sh b/scripts/prepare_release.sh index 7544315c28..3909a5e23f 100755 --- a/scripts/prepare_release.sh +++ b/scripts/prepare_release.sh @@ -141,6 +141,7 @@ create_release_branch() { fi eval git checkout -b "${release_branch}" "$mute" + eval git push -u origin "${release_branch}" "$mute" echo "✅" } @@ -149,8 +150,12 @@ create_build_branch() { eval git checkout "${release_branch}" "$mute" eval git pull "$mute" + local temp_file local latest_build_number - latest_build_number=$(agvtool what-version -terse) + + temp_file=$(mktemp) + bundle exec fastlane latest_build_number_for_version version:"$version" file_name:"$temp_file" + latest_build_number="$(<"$temp_file")" build_number=$((latest_build_number + 1)) build_branch="${release_branch}-build-${build_number}" @@ -159,6 +164,7 @@ create_build_branch() { fi eval git checkout -b "${build_branch}" "$mute" + eval git push -u origin "${build_branch}" "$mute" echo "✅" } @@ -185,9 +191,7 @@ bump_patch_number() { update_build_version() { echo "Setting build version ..." - local username - username="$(git config user.email 2>&1)" - (cd "$base_dir" && bundle exec fastlane increment_build_number_for_version version:"${version}" username:"$username") + (cd "$base_dir" && bundle exec fastlane increment_build_number_for_version version:"${version}") git add "${base_dir}/DuckDuckGo.xcodeproj/project.pbxproj" if [[ "$(git diff --cached)" ]]; then eval git commit -m \"Update build number\" "$mute" @@ -228,10 +232,7 @@ update_release_notes() { create_pull_request() { printf '%s' "Creating PR ... " - if [[ ! $is_subsequent_release && ! $is_hotfix ]]; then - eval git push -u origin "${release_branch}" "$mute" - fi - eval git push -u origin "${build_branch}" "$mute" + eval git push origin "${build_branch}" "$mute" eval gh pr create --title \"Release "${version}-${build_number}"\" --base "${release_branch}" --label \"Merge triggers release\" --assignee @me "$mute" --body-file "${script_dir}/assets/prepare-release-description" eval gh pr view --web "$mute" echo "✅" From 3426f4a8f4af9fb12dd4d81533563d8956e912dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacek=20=C5=81yp?= Date: Tue, 30 Jan 2024 23:50:28 +0100 Subject: [PATCH 67/70] More script fixes (#2419) Task/Issue URL: https://app.asana.com/0/1203301625297703/1206468110851824/f Description: - set initial build number to -1 since we always increment it, - set version only for hotfix branch, otherwise we get it from the user input --- fastlane/Fastfile | 2 +- scripts/prepare_release.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 061eb92d21..751b5b0df9 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -148,7 +148,7 @@ lane :latest_build_number_for_version do |options| build_number = latest_testflight_build_number( api_key: get_api_key, version: options[:version], - initial_build_number: 0, + initial_build_number: -1, username: get_username(options)) if options[:file_name] File.write(options[:file_name], build_number) diff --git a/scripts/prepare_release.sh b/scripts/prepare_release.sh index 3909a5e23f..4a5bde66d3 100755 --- a/scripts/prepare_release.sh +++ b/scripts/prepare_release.sh @@ -171,8 +171,8 @@ create_build_branch() { update_marketing_version() { printf '%s' "Setting app version ... " - version=$(cut -d' ' -f3 < "${base_dir}/Configuration/Version.xcconfig") if [[ $is_hotfix ]]; then + version=$(cut -d' ' -f3 < "${base_dir}/Configuration/Version.xcconfig") version=$(bump_patch_number "$version") fi From fa753550fa595afe9fcecf1041f124b8c60235bc Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Wed, 31 Jan 2024 09:31:43 +0000 Subject: [PATCH 68/70] Release 7.107.0-0 (#2421) --- Configuration/Version.xcconfig | 2 +- .../AppPrivacyConfigurationDataProvider.swift | 4 +- Core/ios-config.json | 49 ++++++++++++++-- DuckDuckGo.xcodeproj/project.pbxproj | 56 +++++++++---------- DuckDuckGo/Settings.bundle/Root.plist | 2 +- fastlane/metadata/default/release_notes.txt | 3 - 6 files changed, 77 insertions(+), 39 deletions(-) diff --git a/Configuration/Version.xcconfig b/Configuration/Version.xcconfig index cccf98adf5..d31068acdf 100644 --- a/Configuration/Version.xcconfig +++ b/Configuration/Version.xcconfig @@ -1 +1 @@ -MARKETING_VERSION = 7.106.0 +MARKETING_VERSION = 7.107.0 diff --git a/Core/AppPrivacyConfigurationDataProvider.swift b/Core/AppPrivacyConfigurationDataProvider.swift index 4f4eb843c8..e9277bd629 100644 --- a/Core/AppPrivacyConfigurationDataProvider.swift +++ b/Core/AppPrivacyConfigurationDataProvider.swift @@ -23,8 +23,8 @@ import BrowserServicesKit final public class AppPrivacyConfigurationDataProvider: EmbeddedDataProvider { public struct Constants { - public static let embeddedDataETag = "\"4e984b6034f1e27fe85fdad5f4bf37c9\"" - public static let embeddedDataSHA = "d599888e7b447bbaeb2d9a7fd7ccf06956fce8976c316be2f497561a6832613e" + public static let embeddedDataETag = "\"d0ae514c42e1e632584aba7a025b8b92\"" + public static let embeddedDataSHA = "b304a2dbb2edc7443a4950bb2ba9f7604354cf32575dd5a9ca09acd5c4b78146" } public var embeddedDataEtag: String { diff --git a/Core/ios-config.json b/Core/ios-config.json index d884041033..311a1e6e36 100644 --- a/Core/ios-config.json +++ b/Core/ios-config.json @@ -1,6 +1,6 @@ { "readme": "https://github.com/duckduckgo/privacy-configuration", - "version": 1705931475791, + "version": 1706638025243, "features": { "adClickAttribution": { "readme": "https://help.duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/#3rd-party-tracker-loading-protection", @@ -4228,6 +4228,9 @@ { "domain": "tirerack.com" }, + { + "domain": "sephora.com" + }, { "domain": "earth.google.com" }, @@ -4253,7 +4256,7 @@ "privacy-test-pages.site" ] }, - "hash": "549a6e76edaf16c1fffced31b97e9553" + "hash": "c34f2a525dac6f93a6d87bad377dbe9d" }, "harmfulApis": { "settings": { @@ -4488,6 +4491,11 @@ "state": "disabled", "hash": "841fa92b9728c9754f050662678f82c7" }, + "notificationPermissions": { + "exceptions": [], + "state": "disabled", + "hash": "728493ef7a1488e4781656d3f9db84aa" + }, "privacyDashboard": { "exceptions": [], "features": { @@ -5440,6 +5448,12 @@ "triblive.com" ] }, + { + "rule": "doubleclick.net/pixel", + "domains": [ + "sbs.com.au" + ] + }, { "rule": "doubleclick.net", "domains": [ @@ -5543,6 +5557,12 @@ "domains": [ "" ] + }, + { + "rule": "ezodn.com", + "domains": [ + "reisezoom.com" + ] } ] }, @@ -5847,7 +5867,8 @@ "pandora.com", "paper-io.com", "rawstory.com", - "usatoday.com" + "usatoday.com", + "washingtonpost.com" ] } ] @@ -6375,6 +6396,16 @@ } ] }, + "mailerlite.com": { + "rules": [ + { + "rule": "mailerlite.com", + "domains": [ + "" + ] + } + ] + }, "maxymiser.net": { "rules": [ { @@ -7216,6 +7247,16 @@ } ] }, + "tremorhub.com": { + "rules": [ + { + "rule": "tremorhub.com/getTVID", + "domains": [ + "sbs.com.au" + ] + } + ] + }, "trustpilot.com": { "rules": [ { @@ -7530,7 +7571,7 @@ "domain": "sundancecatalog.com" } ], - "hash": "f918a51b5651f2e3a99945ba530f3264" + "hash": "374040a08f2e59051d7618509b9f65d4" }, "trackingCookies1p": { "settings": { diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 415c2f063b..2641a1ca42 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -8202,7 +8202,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProvider.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -8239,7 +8239,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8331,7 +8331,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8359,7 +8359,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8509,7 +8509,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8535,7 +8535,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; INFOPLIST_FILE = DuckDuckGo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8600,7 +8600,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -8635,7 +8635,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8669,7 +8669,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8700,7 +8700,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8987,7 +8987,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9018,7 +9018,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -9047,7 +9047,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -9081,7 +9081,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -9112,7 +9112,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProvider.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -9145,11 +9145,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 3; + DYLIB_CURRENT_VERSION = 0; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9387,7 +9387,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGoAlpha.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9414,7 +9414,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9447,7 +9447,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9485,7 +9485,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9521,7 +9521,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9556,11 +9556,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 3; + DYLIB_CURRENT_VERSION = 0; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9734,11 +9734,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 3; + DYLIB_CURRENT_VERSION = 0; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9767,10 +9767,10 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 0; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 3; + DYLIB_CURRENT_VERSION = 0; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; diff --git a/DuckDuckGo/Settings.bundle/Root.plist b/DuckDuckGo/Settings.bundle/Root.plist index f6b1bab836..bcd44a47ed 100644 --- a/DuckDuckGo/Settings.bundle/Root.plist +++ b/DuckDuckGo/Settings.bundle/Root.plist @@ -6,7 +6,7 @@ DefaultValue - 7.106.0 + 7.107.0 Key version Title diff --git a/fastlane/metadata/default/release_notes.txt b/fastlane/metadata/default/release_notes.txt index 872a9f87b8..fe8f05ed3b 100644 --- a/fastlane/metadata/default/release_notes.txt +++ b/fastlane/metadata/default/release_notes.txt @@ -1,5 +1,2 @@ -- You can now pull the page down to reload it. -- Fixed settings not being saved in rare cases. -- Fixed a bug that sometimes prevented tabs from loading websites. - Bug fixes and other improvements. Join our fully distributed team and help raise the standard of trust online! https://duckduckgo.com/hiring From 94163ac8ef51fcf3170544859f7e46a94127ab12 Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Wed, 31 Jan 2024 09:51:03 +0000 Subject: [PATCH 69/70] metadata updated --- fastlane/metadata/en-CA/description.txt | 2 +- fastlane/metadata/en-GB/description.txt | 2 +- fastlane/metadata/en-US/description.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fastlane/metadata/en-CA/description.txt b/fastlane/metadata/en-CA/description.txt index cd8bcb0d2e..38da5c167b 100644 --- a/fastlane/metadata/en-CA/description.txt +++ b/fastlane/metadata/en-CA/description.txt @@ -1,4 +1,4 @@ -The DuckDuckGo app provides the most comprehensive online privacy protection with the push of a button. With one free download, you get an everyday private Internet browser that offers seamless protection while you search and browse, and even access to tracking protection for emails you receive. +The DuckDuckGo browser provides the most comprehensive online privacy protection in one app. Unlike Chrome and other browsers, our free, go-to browser comes with over a dozen powerful privacy protections built-in, including our search engine that replaces Google and doesn't track your search history. Protecting your privacy online is like protecting your home. Locking the front door won’t stop the most determined folks from getting inside, especially if you’ve left the back door and windows unlocked and an extra key under the doormat. That’s why we offer multiple types of privacy protection, most of which aren’t offered in most popular browsers by default.   diff --git a/fastlane/metadata/en-GB/description.txt b/fastlane/metadata/en-GB/description.txt index 618b4332c9..2a4f366f2c 100644 --- a/fastlane/metadata/en-GB/description.txt +++ b/fastlane/metadata/en-GB/description.txt @@ -1,4 +1,4 @@ -The DuckDuckGo app provides the most comprehensive online privacy protection with the push of a button. With one download, you get a new everyday browser that offers seamless protection while you search and browse, and even access to tracking protection for emails you receive. +The DuckDuckGo browser provides the most comprehensive online privacy protection in one app. Unlike Chrome and other browsers, our free, go-to browser comes with over a dozen powerful privacy protections built-in, including our search engine that replaces Google and doesn't track your search history. Protecting your privacy online is like protecting your home. Locking the front door won’t stop the most determined folks from getting inside, especially if you’ve left the back door and windows unlocked and an extra key under the doormat. That’s why we offer multiple types of privacy protection, most of which aren’t offered in most popular browsers by default. diff --git a/fastlane/metadata/en-US/description.txt b/fastlane/metadata/en-US/description.txt index 3c4ab95397..b7d3ef9bcd 100644 --- a/fastlane/metadata/en-US/description.txt +++ b/fastlane/metadata/en-US/description.txt @@ -1,4 +1,4 @@ -The DuckDuckGo app provides the most comprehensive online privacy protection with the push of a button. With one free download, you get an everyday private Internet browser that offers seamless protection while you search and browse, and even access to tracking protection for emails you receive. +The DuckDuckGo browser provides the most comprehensive online privacy protection in one app. Unlike Chrome and other browsers, our free, go-to browser comes with over a dozen powerful privacy protections built-in, including our search engine that replaces Google and doesn't track your search history. Protecting your privacy online is like protecting your home. Locking the front door won’t stop the most determined folks from getting inside, especially if you’ve left the back door and windows unlocked and an extra key under the doormat. That’s why we offer multiple types of privacy protection, most of which aren’t offered in most popular browsers by default.   From e4aeb591804ab9e9e9c4bdd70bf72015424b0c64 Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Wed, 31 Jan 2024 15:11:08 +0000 Subject: [PATCH 70/70] Metadata reverted https://app.asana.com/0/0/1206476969305202/1206481077004998/f --- fastlane/metadata/en-CA/description.txt | 2 +- fastlane/metadata/en-GB/description.txt | 2 +- fastlane/metadata/en-US/description.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fastlane/metadata/en-CA/description.txt b/fastlane/metadata/en-CA/description.txt index 38da5c167b..cd8bcb0d2e 100644 --- a/fastlane/metadata/en-CA/description.txt +++ b/fastlane/metadata/en-CA/description.txt @@ -1,4 +1,4 @@ -The DuckDuckGo browser provides the most comprehensive online privacy protection in one app. Unlike Chrome and other browsers, our free, go-to browser comes with over a dozen powerful privacy protections built-in, including our search engine that replaces Google and doesn't track your search history. +The DuckDuckGo app provides the most comprehensive online privacy protection with the push of a button. With one free download, you get an everyday private Internet browser that offers seamless protection while you search and browse, and even access to tracking protection for emails you receive. Protecting your privacy online is like protecting your home. Locking the front door won’t stop the most determined folks from getting inside, especially if you’ve left the back door and windows unlocked and an extra key under the doormat. That’s why we offer multiple types of privacy protection, most of which aren’t offered in most popular browsers by default.   diff --git a/fastlane/metadata/en-GB/description.txt b/fastlane/metadata/en-GB/description.txt index 2a4f366f2c..618b4332c9 100644 --- a/fastlane/metadata/en-GB/description.txt +++ b/fastlane/metadata/en-GB/description.txt @@ -1,4 +1,4 @@ -The DuckDuckGo browser provides the most comprehensive online privacy protection in one app. Unlike Chrome and other browsers, our free, go-to browser comes with over a dozen powerful privacy protections built-in, including our search engine that replaces Google and doesn't track your search history. +The DuckDuckGo app provides the most comprehensive online privacy protection with the push of a button. With one download, you get a new everyday browser that offers seamless protection while you search and browse, and even access to tracking protection for emails you receive. Protecting your privacy online is like protecting your home. Locking the front door won’t stop the most determined folks from getting inside, especially if you’ve left the back door and windows unlocked and an extra key under the doormat. That’s why we offer multiple types of privacy protection, most of which aren’t offered in most popular browsers by default. diff --git a/fastlane/metadata/en-US/description.txt b/fastlane/metadata/en-US/description.txt index b7d3ef9bcd..3c4ab95397 100644 --- a/fastlane/metadata/en-US/description.txt +++ b/fastlane/metadata/en-US/description.txt @@ -1,4 +1,4 @@ -The DuckDuckGo browser provides the most comprehensive online privacy protection in one app. Unlike Chrome and other browsers, our free, go-to browser comes with over a dozen powerful privacy protections built-in, including our search engine that replaces Google and doesn't track your search history. +The DuckDuckGo app provides the most comprehensive online privacy protection with the push of a button. With one free download, you get an everyday private Internet browser that offers seamless protection while you search and browse, and even access to tracking protection for emails you receive. Protecting your privacy online is like protecting your home. Locking the front door won’t stop the most determined folks from getting inside, especially if you’ve left the back door and windows unlocked and an extra key under the doormat. That’s why we offer multiple types of privacy protection, most of which aren’t offered in most popular browsers by default.