diff --git a/.maestro/data_clearing_tests/02_duckduckgo_settings.yml b/.maestro/data_clearing_tests/02_duckduckgo_settings.yml index e2da0f3e3a..fa68b0c05a 100644 --- a/.maestro/data_clearing_tests/02_duckduckgo_settings.yml +++ b/.maestro/data_clearing_tests/02_duckduckgo_settings.yml @@ -40,6 +40,11 @@ tags: id: "alert.forget-data.confirm" - assertVisible: id: "searchEntry" +- runFlow: + when: + visible: "Cancel" + commands: + - tapOn: "Cancel" - tapOn: "Close Tabs and Clear Data" - tapOn: id: "alert.forget-data.confirm" diff --git a/.swiftlint.yml b/.swiftlint.yml index 5808536458..9047bcb725 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -7,6 +7,7 @@ disabled_rules: - todo - unused_capture_list - trailing_comma + - cyclomatic_complexity opt_in_rules: - closure_end_indentation diff --git a/Core/BookmarksCachingSearch.swift b/Core/BookmarksCachingSearch.swift index af6ea20826..a874a05284 100644 --- a/Core/BookmarksCachingSearch.swift +++ b/Core/BookmarksCachingSearch.swift @@ -60,19 +60,7 @@ public class CoreDataBookmarksSearchStore: BookmarksSearchStore { public func bookmarksAndFavorites(completion: @escaping ([BookmarksCachingSearch.ScoredBookmark]) -> Void) { let context = bookmarksStore.makeContext(concurrencyType: .privateQueueConcurrencyType) - - let fetchRequest = NSFetchRequest(entityName: "BookmarkEntity") - fetchRequest.predicate = NSPredicate( - format: "%K = false AND %K == NO AND (%K == NO OR %K == nil)", - #keyPath(BookmarkEntity.isFolder), - #keyPath(BookmarkEntity.isPendingDeletion), - #keyPath(BookmarkEntity.isStub), #keyPath(BookmarkEntity.isStub) - ) - fetchRequest.resultType = .dictionaryResultType - fetchRequest.propertiesToFetch = [#keyPath(BookmarkEntity.title), - #keyPath(BookmarkEntity.url), - #keyPath(BookmarkEntity.objectID)] - fetchRequest.relationshipKeyPathsForPrefetching = [#keyPath(BookmarkEntity.favoriteFolders)] + let fetchRequest = Self.shallowBookmarksFetchRequest(context: context) context.perform { let result = try? context.fetch(fetchRequest) as? [[String: Any]] @@ -97,6 +85,38 @@ public class CoreDataBookmarksSearchStore: BookmarksSearchStore { externalContext.persistentStoreCoordinator == bookmarksStore.coordinator else { return } subject.send() } + + /// Creates an `NSFetchRequest` to retrieve each bookmark as a shallow dictionary. + /// + /// The dictionary contains + /// * `#keyPath(BookmarkEntity.title)` + /// * `#keyPath(BookmarkEntity.url)` + /// * `#keyPath(BookmarkEntity.objectID)` + /// * `#keyPath(BookmarkEntity.favoriteFolders)` + /// + /// Note that is `#keyPath(BookmarkEntity.favoriteFolders)` an `Int` representing the count of favorites folders this bookmark is contained in + public static func shallowBookmarksFetchRequest(context: NSManagedObjectContext) -> NSFetchRequest { + let favExpression = NSExpressionDescription() + favExpression.name = #keyPath(BookmarkEntity.favoriteFolders) + favExpression.expression = NSExpression(forFunction: "count:", + arguments: [NSExpression(forKeyPath: #keyPath(BookmarkEntity.favoriteFolders))]) + favExpression.expressionResultType = .integer64AttributeType + + let fetchRequest = NSFetchRequest(entityName: "BookmarkEntity") + fetchRequest.predicate = NSPredicate( + format: "%K = false AND %K == NO AND (%K == NO OR %K == nil)", + #keyPath(BookmarkEntity.isFolder), + #keyPath(BookmarkEntity.isPendingDeletion), + #keyPath(BookmarkEntity.isStub), #keyPath(BookmarkEntity.isStub) + ) + fetchRequest.resultType = .dictionaryResultType + fetchRequest.propertiesToFetch = [#keyPath(BookmarkEntity.title), + #keyPath(BookmarkEntity.url), + #keyPath(BookmarkEntity.objectID), + favExpression] + + return fetchRequest + } } public class BookmarksCachingSearch: BookmarksStringSearch { @@ -125,14 +145,16 @@ public class BookmarksCachingSearch: BookmarksStringSearch { guard let title = bookmark[#keyPath(BookmarkEntity.title)] as? String, let urlString = bookmark[#keyPath(BookmarkEntity.url)] as? String, let url = URL(string: urlString), - let objectID = bookmark[#keyPath(BookmarkEntity.objectID)] as? NSManagedObjectID else { + let objectID = bookmark[#keyPath(BookmarkEntity.objectID)] as? NSManagedObjectID, + let favoritesFolderCount = bookmark[#keyPath(BookmarkEntity.favoriteFolders)] as? Int + else { return nil } self.init(objectID: objectID, title: title, url: url, - isFavorite: (bookmark[#keyPath(BookmarkEntity.favoriteFolders)] as? Set)?.isEmpty != true) + isFavorite: favoritesFolderCount > 0) } public func togglingFavorite() -> BookmarksStringSearchResult { diff --git a/Core/Debounce.swift b/Core/Debounce.swift deleted file mode 100644 index 6994c3dda3..0000000000 --- a/Core/Debounce.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// Debounce.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 - -public class Debounce { - - private let queue: DispatchQueue - private let interval: TimeInterval - - private var currentWorkItem = DispatchWorkItem(block: {}) - - public init(queue: DispatchQueue, seconds: TimeInterval) { - self.queue = queue - self.interval = seconds - } - - public func schedule(_ block: @escaping (() -> Void)) { - currentWorkItem.cancel() - currentWorkItem = DispatchWorkItem(block: { block() }) - queue.asyncAfter(deadline: .now() + interval, execute: currentWorkItem) - } -} diff --git a/Core/DefaultVariantManager.swift b/Core/DefaultVariantManager.swift index b17b8de45d..98cbf59382 100644 --- a/Core/DefaultVariantManager.swift +++ b/Core/DefaultVariantManager.swift @@ -62,6 +62,10 @@ 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: "mc", weight: 1, isIncluded: When.inEnglish, features: []), + VariantIOS(name: "md", weight: 1, isIncluded: When.inEnglish, features: [.history]), + returningUser ] diff --git a/Core/FeatureFlag.swift b/Core/FeatureFlag.swift index 1246732321..f14ae54cbf 100644 --- a/Core/FeatureFlag.swift +++ b/Core/FeatureFlag.swift @@ -34,7 +34,6 @@ public enum FeatureFlag: String { case networkProtection case networkProtectionWaitlistAccess case networkProtectionWaitlistActive - case subscription case swipeTabs case autoconsentOnByDefault case history @@ -43,7 +42,7 @@ public enum FeatureFlag: String { extension FeatureFlag: FeatureFlagSourceProviding { public var source: FeatureFlagSource { switch self { - case .debugMenu, .appTrackingProtection, .subscription, .swipeTabs: + case .debugMenu, .appTrackingProtection, .swipeTabs: return .internalOnly case .sync: return .remoteReleasable(.subfeature(SyncSubfeature.level0ShowSync)) @@ -71,6 +70,7 @@ extension FeatureFlag: FeatureFlagSourceProviding { return .remoteReleasable(.subfeature(AutoconsentSubfeature.onByDefault)) case .history: return .remoteReleasable(.feature(.history)) + } } } diff --git a/Core/HistoryCapture.swift b/Core/HistoryCapture.swift index d63c7fd917..68e90a8953 100644 --- a/Core/HistoryCapture.swift +++ b/Core/HistoryCapture.swift @@ -40,17 +40,16 @@ public class HistoryCapture { public func webViewDidCommit(url: URL) { self.url = url - coordinator.addVisit(of: url.urlOrDuckDuckGoCleanQuery) + coordinator.addVisit(of: url) } public func titleDidChange(_ title: String?, forURL url: URL?) { - guard self.url == url else { + guard let url, self.url == url else { return } - guard let url = url?.urlOrDuckDuckGoCleanQuery, let title, !title.isEmpty else { - return - } + guard let title, !title.isEmpty else { return } + coordinator.updateTitleIfNeeded(title: title, url: url) coordinator.commitChanges(url: url) } diff --git a/Core/PixelEvent.swift b/Core/PixelEvent.swift index 472635c170..e03ecb6f1a 100644 --- a/Core/PixelEvent.swift +++ b/Core/PixelEvent.swift @@ -100,9 +100,14 @@ extension Pixel { case homeScreenEditFavorite case homeScreenDeleteFavorite - case autocompleteSelectedLocal - case autocompleteSelectedRemote - + case autocompleteEnabled + case autocompleteDisabled + case autocompleteClickPhrase + case autocompleteClickWebsite + case autocompleteClickBookmark + case autocompleteClickFavorite + case autocompleteClickHistory + case feedbackPositive case feedbackNegativePrefix(category: String) @@ -596,6 +601,16 @@ extension Pixel { case privacyProSubscriptionManagementEmail case privacyProSubscriptionManagementPlanBilling case privacyProSubscriptionManagementRemoval + + // Full site address setting + case settingsShowFullSiteAddressEnabled + case settingsShowFullSiteAddressDisabled + + // Web pixels + case privacyProOfferMonthlyPriceClick + case privacyProOfferYearlyPriceClick + case privacyProAddEmailSuccess + case privacyProWelcomeFAQClick } } @@ -677,9 +692,14 @@ extension Pixel.Event { case .homeScreenEditFavorite: return "mh_ef" case .homeScreenDeleteFavorite: return "mh_df" - case .autocompleteSelectedLocal: return "m_au_l" - case .autocompleteSelectedRemote: return "m_au_r" - + case .autocompleteEnabled: return "m_autocomplete_toggled_on" + case .autocompleteDisabled: return "m_autocomplete_toggled_off" + case .autocompleteClickPhrase: return "m_autocomplete_click_phrase" + case .autocompleteClickWebsite: return "m_autocomplete_click_website" + case .autocompleteClickBookmark: return "m_autocomplete_click_bookmark" + case .autocompleteClickFavorite: return "m_autocomplete_click_favorite" + case .autocompleteClickHistory: return "m_autocomplete_click_history" + case .feedbackPositive: return "mfbs_positive_submit" case .feedbackNegativePrefix(category: let category): return "mfbs_negative_\(category)" @@ -1133,7 +1153,7 @@ extension Pixel.Event { case .historyInsertVisitFailed: return "m_debug_history-insert-visit-failed" case .historyRemoveVisitsFailed: return "m_debug_history-remove-visits-failed" - // MARK: Privacy pro + // MARK: Privacy pro case .privacyProSubscriptionActive: return "m_privacy-pro_app_subscription_active" case .privacyProOfferScreenImpression: return "m_privacy-pro_offer_screen_impression" case .privacyProPurchaseAttempt: return "m_privacy-pro_terms-conditions_subscribe_click" @@ -1164,6 +1184,13 @@ extension Pixel.Event { case .privacyProSubscriptionManagementEmail: return "m_privacy-pro_manage-email_edit_click" case .privacyProSubscriptionManagementPlanBilling: return "m_privacy-pro_settings_change-plan-or-billing_click" case .privacyProSubscriptionManagementRemoval: return "m_privacy-pro_settings_remove-from-device_click" + case .settingsShowFullSiteAddressEnabled: return "m_settings_show_full_url_on" + case .settingsShowFullSiteAddressDisabled: return "m_settings_show_full_url_off" + // Web + case .privacyProOfferMonthlyPriceClick: return "m_privacy-pro_offer_monthly-price_click" + case .privacyProOfferYearlyPriceClick: return "m_privacy-pro_offer_yearly-price_click" + case .privacyProAddEmailSuccess: return "m_privacy-pro_app_add-email_success_u" + case .privacyProWelcomeFAQClick: return "m_privacy-pro_welcome_faq_click_u" } } } diff --git a/Core/UserDefaultsPropertyWrapper.swift b/Core/UserDefaultsPropertyWrapper.swift index bb95c04c97..5033bc231d 100644 --- a/Core/UserDefaultsPropertyWrapper.swift +++ b/Core/UserDefaultsPropertyWrapper.swift @@ -113,7 +113,7 @@ public struct UserDefaultsWrapper { case networkProtectionWaitlistTermsAndConditionsAccepted = "com.duckduckgo.ios.vpn.terms-and-conditions-accepted" case addressBarPosition = "com.duckduckgo.ios.addressbarposition" - case showFullSiteAddress = "com.duckduckgo.ios.showfullsiteaddress" + case showFullURLAddress = "com.duckduckgo.ios.showfullurladdress" case webContainerId = "com.duckduckgo.ios.webcontainer.id" diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 44e6f0ada3..91296b6d7f 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -260,8 +260,10 @@ 37FCAAC029930E26000E420A /* FailedAssertionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FCAABF29930E26000E420A /* FailedAssertionView.swift */; }; 37FD780F2A29E28B00B36DB1 /* SyncErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FD780E2A29E28B00B36DB1 /* SyncErrorHandler.swift */; }; 4B0295192537BC6700E00CEF /* ConfigurationDebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B0295182537BC6700E00CEF /* ConfigurationDebugViewController.swift */; }; + 4B0F3F502B9BFF2100392892 /* NetworkProtectionFAQView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B0F3F4F2B9BFF2100392892 /* NetworkProtectionFAQView.swift */; }; 4B274F602AFEAECC003F0745 /* NetworkProtectionWidgetRefreshModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B274F5F2AFEAECC003F0745 /* NetworkProtectionWidgetRefreshModel.swift */; }; 4B2754EC29E8C7DF00394032 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 4B2754EB29E8C7DF00394032 /* Lottie */; }; + 4B37E0502B928CA6009E81CA /* vpn-light-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = 4B37E04F2B928CA6009E81CA /* vpn-light-mode.json */; }; 4B470ED6299C49800086EBDC /* AppTrackingProtectionDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B470ED5299C49800086EBDC /* AppTrackingProtectionDatabase.swift */; }; 4B470ED9299C4AED0086EBDC /* AppTrackingProtectionModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 4B470ED7299C4AED0086EBDC /* AppTrackingProtectionModel.xcdatamodeld */; }; 4B470EDB299C4FB20086EBDC /* AppTrackingProtectionListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B470EDA299C4FB20086EBDC /* AppTrackingProtectionListViewModel.swift */; }; @@ -275,6 +277,7 @@ 4B60ACA1252EC0B100E8D219 /* FullScreenVideoUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B60ACA0252EC0B100E8D219 /* FullScreenVideoUserScript.swift */; }; 4B62C4BA25B930DD008912C6 /* AppConfigurationFetchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B62C4B925B930DD008912C6 /* AppConfigurationFetchTests.swift */; }; 4B6484F327FD1E350050A7A1 /* MenuControllerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B6484E927FD1E340050A7A1 /* MenuControllerView.swift */; }; + 4B6ED9452B992FE4007F5CAA /* vpn-dark-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = 4B6ED9442B992FE4007F5CAA /* vpn-dark-mode.json */; }; 4B75EA9226A266CB00018634 /* PrintingUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B75EA9126A266CB00018634 /* PrintingUserScript.swift */; }; 4B78074E2B183A1F009DB2CF /* SurveyURLBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B78074D2B183A1F009DB2CF /* SurveyURLBuilder.swift */; }; 4B83396C29AC0701003F7EA9 /* AppTrackingProtectionStoringModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B470EE2299C6DD10086EBDC /* AppTrackingProtectionStoringModel.swift */; }; @@ -293,6 +296,8 @@ 4BBBBA902B031B4200D965DA /* VPNWaitlist.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBBBA8C2B031B4200D965DA /* VPNWaitlist.swift */; }; 4BBBBA922B03291700D965DA /* VPNWaitlistUserText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBBBA912B03291700D965DA /* VPNWaitlistUserText.swift */; }; 4BC21A2F27238B7500229F0E /* RunLoopExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC21A2C272388BD00229F0E /* RunLoopExtensionTests.swift */; }; + 4BCBE45E2BA7E81F00FC75A1 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 4BCBE45D2BA7E81F00FC75A1 /* PrivacyInfo.xcprivacy */; }; + 4BCBE4602BA7E87100FC75A1 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 4BCBE45F2BA7E87100FC75A1 /* PrivacyInfo.xcprivacy */; }; 4BCD14632B05AF2B000B1E4C /* NetworkProtectionAccessController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BCD14622B05AF2B000B1E4C /* NetworkProtectionAccessController.swift */; }; 4BCD14672B05B682000B1E4C /* NetworkProtectionTermsAndConditionsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BCD14662B05B682000B1E4C /* NetworkProtectionTermsAndConditionsStore.swift */; }; 4BCD14692B05BDD5000B1E4C /* AppDelegate+Waitlists.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BCD14682B05BDD5000B1E4C /* AppDelegate+Waitlists.swift */; }; @@ -373,6 +378,7 @@ 851CD674244D7E6000331B98 /* UserDefaultsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85449F0023FEAF3000512AAF /* UserDefaultsExtension.swift */; }; 851DFD87212C39D300D95F20 /* TabSwitcherButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 851DFD86212C39D300D95F20 /* TabSwitcherButton.swift */; }; 851DFD8A212C5EE800D95F20 /* TabSwitcherButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 851DFD89212C5EE800D95F20 /* TabSwitcherButtonTests.swift */; }; + 851F74262B9A1BFD00747C42 /* Suggestions in Frameworks */ = {isa = PBXBuildFile; productRef = 851F74252B9A1BFD00747C42 /* Suggestions */; }; 85200FA11FBC5BB5001AF290 /* DDGPersistenceContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85200FA01FBC5BB5001AF290 /* DDGPersistenceContainer.swift */; }; 8521FDE6238D414B00A44CC3 /* FileStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8521FDE4238D411400A44CC3 /* FileStoreTests.swift */; }; 8524AAAC2A3888FE00EEC6D2 /* Waitlist.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8524AAAB2A3888FE00EEC6D2 /* Waitlist.xcassets */; }; @@ -424,6 +430,7 @@ 8551912724746EDC0010FDD0 /* SnapshotHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8551912624746EDC0010FDD0 /* SnapshotHelper.swift */; }; 85582E0029D7409700E9AE35 /* SyncSettingsViewController+PDFRendering.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85582DFF29D7409700E9AE35 /* SyncSettingsViewController+PDFRendering.swift */; }; 855D914D2063EF6A00C4B448 /* TabSwitcherTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 855D914C2063EF6A00C4B448 /* TabSwitcherTransition.swift */; }; + 8562CE152B9B645C00E1D399 /* CachedBookmarkSuggestions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8562CE142B9B645C00E1D399 /* CachedBookmarkSuggestions.swift */; }; 8563A03C1F9288D600F04442 /* BrowserChromeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8563A03B1F9288D600F04442 /* BrowserChromeManager.swift */; }; 8565A34B1FC8D96B00239327 /* LaunchTabNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8565A34A1FC8D96B00239327 /* LaunchTabNotification.swift */; }; 8565A34D1FC8DFE400239327 /* LaunchTabNotificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8565A34C1FC8DFE400239327 /* LaunchTabNotificationTests.swift */; }; @@ -500,7 +507,6 @@ 85DFEDF724CB1CAB00973FE7 /* ShareSheet.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 85DFEDF624CB1CAB00973FE7 /* ShareSheet.xcassets */; }; 85DFEDF924CF3D0E00973FE7 /* TabsBarCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85DFEDF824CF3D0E00973FE7 /* TabsBarCell.swift */; }; 85E242172AB1B54D000F3E28 /* ReturnUserMeasurement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E242162AB1B54D000F3E28 /* ReturnUserMeasurement.swift */; }; - 85E5603026541D9E00F4DC44 /* AutocompleteRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E5602E26541D1D00F4DC44 /* AutocompleteRequestTests.swift */; }; 85E58C2C28FDA94F006A801A /* FavoritesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E58C2B28FDA94F006A801A /* FavoritesViewController.swift */; }; 85EE7F55224667DD000FE757 /* WebContainer.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 85EE7F54224667DD000FE757 /* WebContainer.storyboard */; }; 85EE7F572246685B000FE757 /* WebContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85EE7F562246685B000FE757 /* WebContainerViewController.swift */; }; @@ -589,7 +595,6 @@ 988AC355257E47C100793C64 /* RequeryLogic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 988AC354257E47C100793C64 /* RequeryLogic.swift */; }; 988F3DCF237D5C0F00AEE34C /* SchemeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 988F3DCE237D5C0F00AEE34C /* SchemeHandler.swift */; }; 988F3DD3237DE8D900AEE34C /* ForgetDataAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 988F3DD2237DE8D900AEE34C /* ForgetDataAlert.swift */; }; - 98982B3422F8D8E400578AC9 /* Debounce.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98982B3322F8D8E400578AC9 /* Debounce.swift */; }; 98983096255B5019003339A2 /* BookmarksCachingSearchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98983095255B5019003339A2 /* BookmarksCachingSearchTests.swift */; }; 98999D5922FDA41500CBBE1B /* BasicAuthenticationAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98999D5822FDA41500CBBE1B /* BasicAuthenticationAlert.swift */; }; 989B337522D7EF2100437824 /* EmptyCollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 989B337422D7EF2100437824 /* EmptyCollectionReusableView.swift */; }; @@ -708,6 +713,11 @@ BDA583892B98BA7600732FDC /* AccountManagerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA583862B98B6C700732FDC /* AccountManagerExtension.swift */; }; BDC234F72B27F51100D3C798 /* UniquePixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDC234F62B27F51100D3C798 /* UniquePixel.swift */; }; BDD3B3552B8EF8DB005857A8 /* NetworkProtectionUNNotificationPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE3766DD2AC5945500AAB575 /* NetworkProtectionUNNotificationPresenter.swift */; }; + BDFF031D2BA3D2BD00F324C9 /* DefaultNetworkProtectionVisibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDFF031C2BA3D2BD00F324C9 /* DefaultNetworkProtectionVisibility.swift */; }; + BDFF03212BA3D3CF00F324C9 /* NetworkProtectionVisibilityForTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDFF03202BA3D3CF00F324C9 /* NetworkProtectionVisibilityForTunnelProvider.swift */; }; + BDFF03222BA3D8E200F324C9 /* NetworkProtectionFeatureVisibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDFF03192BA39C5A00F324C9 /* NetworkProtectionFeatureVisibility.swift */; }; + BDFF03232BA3D8E300F324C9 /* NetworkProtectionFeatureVisibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDFF03192BA39C5A00F324C9 /* NetworkProtectionFeatureVisibility.swift */; }; + BDFF03262BA3DA4900F324C9 /* NetworkProtectionFeatureVisibilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDFF03242BA3D92E00F324C9 /* NetworkProtectionFeatureVisibilityTests.swift */; }; C10CB5F32A1A5BDF0048E503 /* AutofillViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10CB5F22A1A5BDF0048E503 /* AutofillViews.swift */; }; C111B26927F579EF006558B1 /* BookmarkOrFolderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C111B26827F579EF006558B1 /* BookmarkOrFolderTests.swift */; }; C12726EE2A5FF88C00215B02 /* EmailSignupPromptView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C12726ED2A5FF88C00215B02 /* EmailSignupPromptView.swift */; }; @@ -872,9 +882,7 @@ EE8594992A44791C008A6D06 /* NetworkProtectionTunnelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE8594982A44791C008A6D06 /* NetworkProtectionTunnelController.swift */; }; EE8E568A2A56BCE400F11DCA /* NetworkProtection in Frameworks */ = {isa = PBXBuildFile; productRef = EE8E56892A56BCE400F11DCA /* NetworkProtection */; }; EE9D68D12AE00CF300B55EF4 /* NetworkProtectionVPNSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68D02AE00CF300B55EF4 /* NetworkProtectionVPNSettingsView.swift */; }; - EE9D68D52AE1526600B55EF4 /* NetworkProtectionVPNNotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68D42AE1526600B55EF4 /* NetworkProtectionVPNNotificationsView.swift */; }; EE9D68D82AE15AD600B55EF4 /* UIApplicationExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68D72AE15AD600B55EF4 /* UIApplicationExtension.swift */; }; - EE9D68DA2AE1659F00B55EF4 /* NetworkProtectionVPNNotificationsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68D92AE1659F00B55EF4 /* NetworkProtectionVPNNotificationsViewModel.swift */; }; EE9D68DC2AE16AE100B55EF4 /* NotificationsAuthorizationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68DB2AE16AE100B55EF4 /* NotificationsAuthorizationController.swift */; }; EE9D68DE2AE2A65600B55EF4 /* UserDefaults+NetworkProtection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68DD2AE2A65600B55EF4 /* UserDefaults+NetworkProtection.swift */; }; EEC02C142B0519DE0045CE11 /* NetworkProtectionVPNLocationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEC02C132B0519DE0045CE11 /* NetworkProtectionVPNLocationViewModel.swift */; }; @@ -929,7 +937,6 @@ F176699F1E40BC86003D3222 /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F176699D1E40BC86003D3222 /* Settings.storyboard */; }; F17669D71E43401C003D3222 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F17669D61E43401C003D3222 /* MainViewController.swift */; }; F17843E91F36226700390DCD /* MockFiles in Resources */ = {isa = PBXBuildFile; fileRef = F17843E81F36226700390DCD /* MockFiles */; }; - F17922DB1E717C8D006E3D97 /* Suggestion.swift in Sources */ = {isa = PBXBuildFile; fileRef = F17922DA1E717C8D006E3D97 /* Suggestion.swift */; }; F17922DE1E7192E6006E3D97 /* SuggestionTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F17922DD1E7192E6006E3D97 /* SuggestionTableViewCell.swift */; }; F17922E01E71BB59006E3D97 /* AutocompleteViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F17922DF1E71BB59006E3D97 /* AutocompleteViewControllerDelegate.swift */; }; F17922E21E71CD67006E3D97 /* NoSuggestionsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F17922E11E71CD67006E3D97 /* NoSuggestionsTableViewCell.swift */; }; @@ -941,7 +948,6 @@ F194FAFB1F14E622009B4DF8 /* UIFontExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F194FAFA1F14E622009B4DF8 /* UIFontExtensionTests.swift */; }; F198D78E1E39762C0088DA8A /* StringExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F198D78D1E39762C0088DA8A /* StringExtensionTests.swift */; }; F198D7981E3A45D90088DA8A /* WKWebViewConfigurationExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F198D7971E3A45D90088DA8A /* WKWebViewConfigurationExtensionTests.swift */; }; - F1A5683A1E70F98E0081082E /* AutocompleteRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1A568391E70F98E0081082E /* AutocompleteRequest.swift */; }; F1A886781F29394E0096251E /* WebCacheManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1A886771F29394E0096251E /* WebCacheManager.swift */; }; F1AE54E81F0425FC00D9A700 /* AuthenticationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1AE54E71F0425FC00D9A700 /* AuthenticationViewController.swift */; }; F1BE54581E69DE1000FCF649 /* TutorialSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1BE54571E69DE1000FCF649 /* TutorialSettings.swift */; }; @@ -1381,7 +1387,9 @@ 37FCAACB2993149A000E420A /* Waitlist */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Waitlist; sourceTree = ""; }; 37FD780E2A29E28B00B36DB1 /* SyncErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncErrorHandler.swift; sourceTree = ""; }; 4B0295182537BC6700E00CEF /* ConfigurationDebugViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationDebugViewController.swift; sourceTree = ""; }; + 4B0F3F4F2B9BFF2100392892 /* NetworkProtectionFAQView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionFAQView.swift; sourceTree = ""; }; 4B274F5F2AFEAECC003F0745 /* NetworkProtectionWidgetRefreshModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionWidgetRefreshModel.swift; sourceTree = ""; }; + 4B37E04F2B928CA6009E81CA /* vpn-light-mode.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "vpn-light-mode.json"; sourceTree = ""; }; 4B470ED5299C49800086EBDC /* AppTrackingProtectionDatabase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTrackingProtectionDatabase.swift; sourceTree = ""; }; 4B470ED8299C4AED0086EBDC /* AppTrackingProtectionModel.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = AppTrackingProtectionModel.xcdatamodel; sourceTree = ""; }; 4B470EDA299C4FB20086EBDC /* AppTrackingProtectionListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTrackingProtectionListViewModel.swift; sourceTree = ""; }; @@ -1394,6 +1402,7 @@ 4B60ACA0252EC0B100E8D219 /* FullScreenVideoUserScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenVideoUserScript.swift; sourceTree = ""; }; 4B62C4B925B930DD008912C6 /* AppConfigurationFetchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfigurationFetchTests.swift; sourceTree = ""; }; 4B6484E927FD1E340050A7A1 /* MenuControllerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuControllerView.swift; sourceTree = ""; }; + 4B6ED9442B992FE4007F5CAA /* vpn-dark-mode.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "vpn-dark-mode.json"; sourceTree = ""; }; 4B75EA9126A266CB00018634 /* PrintingUserScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrintingUserScript.swift; sourceTree = ""; }; 4B78074B2B1823C5009DB2CF /* VPNWaitlistActivationDateStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNWaitlistActivationDateStore.swift; sourceTree = ""; }; 4B78074D2B183A1F009DB2CF /* SurveyURLBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurveyURLBuilder.swift; sourceTree = ""; }; @@ -1408,6 +1417,8 @@ 4BBBBA8C2B031B4200D965DA /* VPNWaitlist.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VPNWaitlist.swift; sourceTree = ""; }; 4BBBBA912B03291700D965DA /* VPNWaitlistUserText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNWaitlistUserText.swift; sourceTree = ""; }; 4BC21A2C272388BD00229F0E /* RunLoopExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunLoopExtensionTests.swift; sourceTree = ""; }; + 4BCBE45D2BA7E81F00FC75A1 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; + 4BCBE45F2BA7E87100FC75A1 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 4BCD14622B05AF2B000B1E4C /* NetworkProtectionAccessController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionAccessController.swift; sourceTree = ""; }; 4BCD14662B05B682000B1E4C /* NetworkProtectionTermsAndConditionsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionTermsAndConditionsStore.swift; sourceTree = ""; }; 4BCD14682B05BDD5000B1E4C /* AppDelegate+Waitlists.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Waitlists.swift"; sourceTree = ""; }; @@ -1538,6 +1549,7 @@ 8551912624746EDC0010FDD0 /* SnapshotHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SnapshotHelper.swift; path = fastlane/SnapshotHelper.swift; sourceTree = SOURCE_ROOT; }; 85582DFF29D7409700E9AE35 /* SyncSettingsViewController+PDFRendering.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SyncSettingsViewController+PDFRendering.swift"; sourceTree = ""; }; 855D914C2063EF6A00C4B448 /* TabSwitcherTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabSwitcherTransition.swift; sourceTree = ""; }; + 8562CE142B9B645C00E1D399 /* CachedBookmarkSuggestions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CachedBookmarkSuggestions.swift; sourceTree = ""; }; 8563A03B1F9288D600F04442 /* BrowserChromeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowserChromeManager.swift; sourceTree = ""; }; 8565A34A1FC8D96B00239327 /* LaunchTabNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchTabNotification.swift; sourceTree = ""; }; 8565A34C1FC8DFE400239327 /* LaunchTabNotificationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchTabNotificationTests.swift; sourceTree = ""; }; @@ -1612,7 +1624,6 @@ 85DFEDF624CB1CAB00973FE7 /* ShareSheet.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = ShareSheet.xcassets; sourceTree = ""; }; 85DFEDF824CF3D0E00973FE7 /* TabsBarCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsBarCell.swift; sourceTree = ""; }; 85E242162AB1B54D000F3E28 /* ReturnUserMeasurement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReturnUserMeasurement.swift; sourceTree = ""; }; - 85E5602E26541D1D00F4DC44 /* AutocompleteRequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutocompleteRequestTests.swift; sourceTree = ""; }; 85E58C2B28FDA94F006A801A /* FavoritesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritesViewController.swift; sourceTree = ""; }; 85EE7F54224667DD000FE757 /* WebContainer.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = WebContainer.storyboard; sourceTree = ""; }; 85EE7F562246685B000FE757 /* WebContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebContainerViewController.swift; sourceTree = ""; }; @@ -2201,7 +2212,6 @@ 988F3DCE237D5C0F00AEE34C /* SchemeHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SchemeHandler.swift; sourceTree = ""; }; 988F3DD2237DE8D900AEE34C /* ForgetDataAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForgetDataAlert.swift; sourceTree = ""; }; 9896632322C56716007BE4FE /* EtagStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EtagStorage.swift; sourceTree = ""; }; - 98982B3322F8D8E400578AC9 /* Debounce.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Debounce.swift; sourceTree = ""; }; 98983095255B5019003339A2 /* BookmarksCachingSearchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksCachingSearchTests.swift; sourceTree = ""; }; 98987E6E251EAC3B006F75CD /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/InfoPlist.strings; sourceTree = ""; }; 98987E70251EAC3B006F75CD /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/InfoPlist.strings; sourceTree = ""; }; @@ -2362,6 +2372,10 @@ BD862E0A2B30F9300073E2EE /* VPNFeedbackFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNFeedbackFormView.swift; sourceTree = ""; }; BDA583862B98B6C700732FDC /* AccountManagerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountManagerExtension.swift; sourceTree = ""; }; BDC234F62B27F51100D3C798 /* UniquePixel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UniquePixel.swift; sourceTree = ""; }; + BDFF03192BA39C5A00F324C9 /* NetworkProtectionFeatureVisibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionFeatureVisibility.swift; sourceTree = ""; }; + BDFF031C2BA3D2BD00F324C9 /* DefaultNetworkProtectionVisibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultNetworkProtectionVisibility.swift; sourceTree = ""; }; + BDFF03202BA3D3CF00F324C9 /* NetworkProtectionVisibilityForTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVisibilityForTunnelProvider.swift; sourceTree = ""; }; + BDFF03242BA3D92E00F324C9 /* NetworkProtectionFeatureVisibilityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionFeatureVisibilityTests.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 = ""; }; C12726ED2A5FF88C00215B02 /* EmailSignupPromptView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailSignupPromptView.swift; sourceTree = ""; }; @@ -2541,9 +2555,7 @@ EE7A92862AC6DE4700832A36 /* NetworkProtectionNotificationIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionNotificationIdentifier.swift; sourceTree = ""; }; EE8594982A44791C008A6D06 /* NetworkProtectionTunnelController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionTunnelController.swift; sourceTree = ""; }; EE9D68D02AE00CF300B55EF4 /* NetworkProtectionVPNSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNSettingsView.swift; sourceTree = ""; }; - EE9D68D42AE1526600B55EF4 /* NetworkProtectionVPNNotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNNotificationsView.swift; sourceTree = ""; }; EE9D68D72AE15AD600B55EF4 /* UIApplicationExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIApplicationExtension.swift; sourceTree = ""; }; - EE9D68D92AE1659F00B55EF4 /* NetworkProtectionVPNNotificationsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNNotificationsViewModel.swift; sourceTree = ""; }; EE9D68DB2AE16AE100B55EF4 /* NotificationsAuthorizationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsAuthorizationController.swift; sourceTree = ""; }; EE9D68DD2AE2A65600B55EF4 /* UserDefaults+NetworkProtection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+NetworkProtection.swift"; sourceTree = ""; }; EEB8FDB92A990AEE00EBEDCF /* Configuration-Alpha.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Configuration-Alpha.xcconfig"; path = "Configuration/Configuration-Alpha.xcconfig"; sourceTree = ""; }; @@ -2626,7 +2638,6 @@ F176699E1E40BC86003D3222 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Settings.storyboard; sourceTree = ""; }; F17669D61E43401C003D3222 /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; F17843E81F36226700390DCD /* MockFiles */ = {isa = PBXFileReference; lastKnownFileType = folder; path = MockFiles; sourceTree = ""; }; - F17922DA1E717C8D006E3D97 /* Suggestion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Suggestion.swift; sourceTree = ""; }; F17922DD1E7192E6006E3D97 /* SuggestionTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SuggestionTableViewCell.swift; sourceTree = ""; }; F17922DF1E71BB59006E3D97 /* AutocompleteViewControllerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutocompleteViewControllerDelegate.swift; sourceTree = ""; }; F17922E11E71CD67006E3D97 /* NoSuggestionsTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NoSuggestionsTableViewCell.swift; sourceTree = ""; }; @@ -2639,7 +2650,6 @@ F197EA3B1E6885F20029BDC1 /* TextFieldWithInsets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TextFieldWithInsets.swift; path = ../Core/TextFieldWithInsets.swift; sourceTree = ""; }; F198D78D1E39762C0088DA8A /* StringExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringExtensionTests.swift; sourceTree = ""; }; F198D7971E3A45D90088DA8A /* WKWebViewConfigurationExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WKWebViewConfigurationExtensionTests.swift; sourceTree = ""; }; - F1A568391E70F98E0081082E /* AutocompleteRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutocompleteRequest.swift; sourceTree = ""; }; F1A886771F29394E0096251E /* WebCacheManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebCacheManager.swift; sourceTree = ""; }; F1AA54601E48D90700223211 /* NotificationCenter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NotificationCenter.framework; path = System/Library/Frameworks/NotificationCenter.framework; sourceTree = SDKROOT; }; F1AE54E71F0425FC00D9A700 /* AuthenticationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationViewController.swift; sourceTree = ""; }; @@ -2815,6 +2825,7 @@ EE8E568A2A56BCE400F11DCA /* NetworkProtection in Frameworks */, CBC83E3429B631780008E19C /* Configuration in Frameworks */, D61CDA182B7CF78300A0FBB9 /* ZIPFoundation in Frameworks */, + 851F74262B9A1BFD00747C42 /* Suggestions in Frameworks */, 98A16C2D28A11D6200A6C003 /* BrowserServicesKit in Frameworks */, 8599690F29D2F1C100DBF9FA /* DDGSync in Frameworks */, 851481882A600EFC00ABC65F /* RemoteMessaging in Frameworks */, @@ -2852,6 +2863,7 @@ 02025669298818B200E694E7 /* PacketTunnelProvider.entitlements */, EEFC6A5F2AC0F2F80065027D /* UserText.swift */, EEDFE2DC2AC6ED4F00F0E19C /* Localizable.strings */, + 4BCBE45F2BA7E87100FC75A1 /* PrivacyInfo.xcprivacy */, ); path = PacketTunnelProvider; sourceTree = ""; @@ -3514,6 +3526,15 @@ name = Widget; sourceTree = ""; }; + 4B37E04E2B928C91009E81CA /* Resources */ = { + isa = PBXGroup; + children = ( + 4B37E04F2B928CA6009E81CA /* vpn-light-mode.json */, + 4B6ED9442B992FE4007F5CAA /* vpn-dark-mode.json */, + ); + name = Resources; + sourceTree = ""; + }; 4B470ED4299C484B0086EBDC /* AppTrackingProtection */ = { isa = PBXGroup; children = ( @@ -3576,6 +3597,7 @@ 4BCD146A2B05C4B5000B1E4C /* VPNWaitlistTermsAndConditionsViewController.swift */, 4B78074B2B1823C5009DB2CF /* VPNWaitlistActivationDateStore.swift */, 4B78074D2B183A1F009DB2CF /* SurveyURLBuilder.swift */, + BDFF031F2BA3D3AD00F324C9 /* Feature Visibility */, ); name = VPN; sourceTree = ""; @@ -3844,6 +3866,7 @@ isa = PBXGroup; children = ( 8512EA5624ED30D30073EE19 /* Assets.xcassets */, + 4BCBE45D2BA7E81F00FC75A1 /* PrivacyInfo.xcprivacy */, 853273AC24FEF49600E3C778 /* ColorExtension.swift */, 853273B124FF114700E3C778 /* DeepLinks.swift */, 8512EA5824ED30D30073EE19 /* Info.plist */, @@ -4122,14 +4145,6 @@ name = iPad; sourceTree = ""; }; - 85E5602D26541D0900F4DC44 /* AutoComplete */ = { - isa = PBXGroup; - children = ( - 85E5602E26541D1D00F4DC44 /* AutocompleteRequestTests.swift */, - ); - name = AutoComplete; - sourceTree = ""; - }; 85EE7F53224667C3000FE757 /* WebContainer */ = { isa = PBXGroup; children = ( @@ -4425,6 +4440,16 @@ name = Subscription; sourceTree = ""; }; + BDFF031F2BA3D3AD00F324C9 /* Feature Visibility */ = { + isa = PBXGroup; + children = ( + BDFF03192BA39C5A00F324C9 /* NetworkProtectionFeatureVisibility.swift */, + BDFF031C2BA3D2BD00F324C9 /* DefaultNetworkProtectionVisibility.swift */, + BDFF03202BA3D3CF00F324C9 /* NetworkProtectionVisibilityForTunnelProvider.swift */, + ); + name = "Feature Visibility"; + sourceTree = ""; + }; C14882D627F2010700D59F0C /* ImportExport */ = { isa = PBXGroup; children = ( @@ -4813,6 +4838,7 @@ EE41BD182A729E9C00546C57 /* NetworkProtectionInviteViewModelTests.swift */, 4BCD146C2B05DB09000B1E4C /* NetworkProtectionAccessControllerTests.swift */, EEC02C152B065BE00045CE11 /* NetworkProtectionVPNLocationViewModelTests.swift */, + BDFF03242BA3D92E00F324C9 /* NetworkProtectionFeatureVisibilityTests.swift */, ); name = NetworkProtection; sourceTree = ""; @@ -4832,24 +4858,16 @@ children = ( EE9D68D02AE00CF300B55EF4 /* NetworkProtectionVPNSettingsView.swift */, EE01EB3F2AFBD0000096AAC9 /* NetworkProtectionVPNSettingsViewModel.swift */, + 4B0F3F4F2B9BFF2100392892 /* NetworkProtectionFAQView.swift */, ); name = VPNSettings; sourceTree = ""; }; - EE9D68D62AE1527F00B55EF4 /* VPNNotifications */ = { - isa = PBXGroup; - children = ( - EE9D68D42AE1526600B55EF4 /* NetworkProtectionVPNNotificationsView.swift */, - EE9D68D92AE1659F00B55EF4 /* NetworkProtectionVPNNotificationsViewModel.swift */, - ); - name = VPNNotifications; - sourceTree = ""; - }; EECD94B22A28B8580085C66E /* NetworkProtection */ = { isa = PBXGroup; children = ( + 4B37E04E2B928C91009E81CA /* Resources */, EE01EB412AFC1DE10096AAC9 /* PreferredLocation */, - EE9D68D62AE1527F00B55EF4 /* VPNNotifications */, EE9D68CF2AE00CE000B55EF4 /* VPNSettings */, EE458D122ABB651500FC651A /* Debug */, EE0153E22A6FE031002A8B26 /* Root */, @@ -4999,7 +5017,6 @@ F17669A21E411D63003D3222 /* Application */, 026F08B629B7DC130079B9DF /* AppTrackingProtection */, 981FED7222045FFA008488D7 /* AutoClear */, - 85E5602D26541D0900F4DC44 /* AutoComplete */, 1E1D8B5B2994FF7800C96994 /* Autoconsent */, F40F843228C92B1C0081AE75 /* Autofill */, 98559FD0267099F400A83094 /* ContentBlocker */, @@ -5172,7 +5189,6 @@ F143C3251E4A9A0E00CFDE3A /* URLExtension.swift */, 1E4DCF4B27B6A4CB00961E25 /* URLFileExtension.swift */, F1075C911E9EF827006BE8A8 /* UserDefaultsExtension.swift */, - 98982B3322F8D8E400578AC9 /* Debounce.swift */, 1CB7B82023CEA1F800AA24EA /* DateExtension.swift */, 1E8AD1DA27C51AE000ABA377 /* TimeIntervalExtension.swift */, 85449EFA23FDA0BC00512AAF /* UserDefaultsPropertyWrapper.swift */, @@ -5205,8 +5221,6 @@ F15D43211E70849A00BF2CDC /* Autocomplete */ = { isa = PBXGroup; children = ( - F17922D31E7109C4006E3D97 /* API */, - F17922DC1E717C91006E3D97 /* Domain */, F17922D41E7109DB006E3D97 /* UI */, ); name = Autocomplete; @@ -5259,14 +5273,6 @@ name = Mocks; sourceTree = ""; }; - F17922D31E7109C4006E3D97 /* API */ = { - isa = PBXGroup; - children = ( - F1A568391E70F98E0081082E /* AutocompleteRequest.swift */, - ); - name = API; - sourceTree = ""; - }; F17922D41E7109DB006E3D97 /* UI */ = { isa = PBXGroup; children = ( @@ -5275,18 +5281,11 @@ F17922DF1E71BB59006E3D97 /* AutocompleteViewControllerDelegate.swift */, F17922E11E71CD67006E3D97 /* NoSuggestionsTableViewCell.swift */, F17922DD1E7192E6006E3D97 /* SuggestionTableViewCell.swift */, + 8562CE142B9B645C00E1D399 /* CachedBookmarkSuggestions.swift */, ); name = UI; sourceTree = ""; }; - F17922DC1E717C91006E3D97 /* Domain */ = { - isa = PBXGroup; - children = ( - F17922DA1E717C8D006E3D97 /* Suggestion.swift */, - ); - name = Domain; - sourceTree = ""; - }; F17D722C1E8B3563003E8B0E /* Domain */ = { isa = PBXGroup; children = ( @@ -5961,6 +5960,7 @@ D61CDA152B7CF77300A0FBB9 /* Subscription */, D61CDA172B7CF78300A0FBB9 /* ZIPFoundation */, 858D009C2B9799FC004E5B4C /* History */, + 851F74252B9A1BFD00747C42 /* Suggestions */, ); productName = Core; productReference = F143C2E41E4A4CD400CFDE3A /* Core.framework */; @@ -6114,6 +6114,7 @@ files = ( 0262085C2A37915D006CB755 /* ios_blocklist_075.json in Resources */, CB1143DE2AF6D4B600C1CCD3 /* InfoPlist.strings in Resources */, + 4BCBE4602BA7E87100FC75A1 /* PrivacyInfo.xcprivacy in Resources */, EEDFE2DA2AC6ED4F00F0E19C /* Localizable.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -6145,6 +6146,7 @@ AA4D6AA223DE4CC4007E8790 /* AppIconBlue76x76@2x.png in Resources */, AA4D6AB823DE4D15007E8790 /* AppIconYellow29x29@2x.png in Resources */, 984147C024F026A300362052 /* Tab.storyboard in Resources */, + 4B6ED9452B992FE4007F5CAA /* vpn-dark-mode.json in Resources */, 02F880642AB206740020C2DF /* PrivacyInfo.xcprivacy in Resources */, AA4D6AE123DE4D33007E8790 /* AppIconGreen76x76@2x.png in Resources */, AA4D6A9123DE49A5007E8790 /* AppIconBlack60x60@3x.png in Resources */, @@ -6251,6 +6253,7 @@ AA4D6AA523DE4CC4007E8790 /* AppIconBlue29x29@3x.png in Resources */, 1EEF124C2850A93F003DDE57 /* Trackers.xcassets in Resources */, AA4D6ACF23DE4D27007E8790 /* AppIconPurple76x76@2x.png in Resources */, + 4B37E0502B928CA6009E81CA /* vpn-light-mode.json in Resources */, 9830A06325ED0DB900DB64DE /* BrowsingMenu.xcassets in Resources */, 98EF177D21837E35006750C1 /* new_tab_dark.json in Resources */, 85C2970A247EB7AA0063A335 /* Text.xcassets in Resources */, @@ -6275,6 +6278,7 @@ 8512EA9D24EEA6820073EE19 /* Assets.xcassets in Resources */, 98B001AA251EABB40090EC07 /* Localizable.strings in Resources */, 98B001A4251EABB40090EC07 /* InfoPlist.strings in Resources */, + 4BCBE45E2BA7E81F00FC75A1 /* PrivacyInfo.xcprivacy in Resources */, 8512EA5724ED30D30073EE19 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -6459,6 +6463,7 @@ files = ( 02025B0D29884D2C00E694E7 /* AppTrackerData.swift in Sources */, 4BEF656C2989C2FC00B650CB /* TunnelEvent.swift in Sources */, + BDFF03232BA3D8E300F324C9 /* NetworkProtectionFeatureVisibility.swift in Sources */, 02025A9A2988229800E694E7 /* TUNInterface.swift in Sources */, 02025A9B2988229800E694E7 /* IPStackProtocol.swift in Sources */, 02025AA32988229800E694E7 /* PacketProtocolParser.swift in Sources */, @@ -6516,6 +6521,7 @@ 02025AEC2988229800E694E7 /* AppTrackingProtectionPacketTunnelProvider.swift in Sources */, 02025B1029884DC500E694E7 /* AppTrackerDataParser.swift in Sources */, 4BB697A52B1D99C5003699B5 /* VPNWaitlistActivationDateStore.swift in Sources */, + BDFF03212BA3D3CF00F324C9 /* NetworkProtectionVisibilityForTunnelProvider.swift in Sources */, EEFC6A602AC0F2F80065027D /* UserText.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -6579,6 +6585,7 @@ F4E1936625AF722F001D2666 /* HighlightCutOutView.swift in Sources */, 1E162605296840D80004127F /* Triangle.swift in Sources */, B609D5522862EAFF0088CAC2 /* InlineWKDownloadDelegate.swift in Sources */, + BDFF03222BA3D8E200F324C9 /* NetworkProtectionFeatureVisibility.swift in Sources */, B652DEFD287BE67400C12A9C /* UserScripts.swift in Sources */, 31DD208427395A5A008FB313 /* VoiceSearchHelper.swift in Sources */, 9874F9EE2187AFCE00CAF33D /* Themable.swift in Sources */, @@ -6621,6 +6628,7 @@ 853C5F6121C277C7001F7A05 /* global.swift in Sources */, EE9D68D82AE15AD600B55EF4 /* UIApplicationExtension.swift in Sources */, F13B4BD31F1822C700814661 /* Tab.swift in Sources */, + BDFF031D2BA3D2BD00F324C9 /* DefaultNetworkProtectionVisibility.swift in Sources */, F1BE54581E69DE1000FCF649 /* TutorialSettings.swift in Sources */, 1EE52ABB28FB1D6300B750C1 /* UIImageExtension.swift in Sources */, 858650D12469BCDE00C36F8A /* DaxDialogs.swift in Sources */, @@ -6645,7 +6653,6 @@ C1B7B529289420830098FD6A /* RemoteMessaging.xcdatamodeld in Sources */, 986B16C425E92DF0007D23E8 /* BrowsingMenuViewController.swift in Sources */, 988AC355257E47C100793C64 /* RequeryLogic.swift in Sources */, - EE9D68D52AE1526600B55EF4 /* NetworkProtectionVPNNotificationsView.swift in Sources */, 1E4F4A5A297193DE00625985 /* MainViewController+CookiesManaged.swift in Sources */, 8586A10D24CBA7070049720E /* FindInPageActivity.swift in Sources */, 1E1626072968413B0004127F /* ViewExtension.swift in Sources */, @@ -6842,7 +6849,6 @@ 0268FC132A449F04000EE6A2 /* OnboardingContainerView.swift in Sources */, 858650D9246B0D3C00C36F8A /* DaxOnboardingViewController.swift in Sources */, 312E5746283BB04A00C18FA0 /* AutofillEmptySearchView.swift in Sources */, - F1A5683A1E70F98E0081082E /* AutocompleteRequest.swift in Sources */, 8565A34B1FC8D96B00239327 /* LaunchTabNotification.swift in Sources */, 0290472829E861BE0008FE3C /* AppTPTrackerDetailViewModel.swift in Sources */, 311BD1AD2836BB3900AEF6C1 /* AutofillItemsEmptyView.swift in Sources */, @@ -6893,6 +6899,7 @@ 981CA7EA2617797500E119D5 /* MainViewController+AddFavoriteFlow.swift in Sources */, 373608902ABB1E6C00629E7F /* FavoritesDisplayModeStorage.swift in Sources */, 9872D205247DCAC100CEF398 /* TabPreviewsSource.swift in Sources */, + 4B0F3F502B9BFF2100392892 /* NetworkProtectionFAQView.swift in Sources */, F130D73A1E5776C500C45811 /* OmniBarDelegate.swift in Sources */, 85DFEDEF24C7EA3B00973FE7 /* SmallOmniBarState.swift in Sources */, 1E908BF129827C480008C8F3 /* AutoconsentUserScript.swift in Sources */, @@ -6908,8 +6915,8 @@ 851624C72B96389D002D5CD7 /* HistoryDebugViewController.swift in Sources */, 8540BBA22440857A00017FE4 /* PreserveLoginsWorker.swift in Sources */, 85DFEDF924CF3D0E00973FE7 /* TabsBarCell.swift in Sources */, - F17922DB1E717C8D006E3D97 /* Suggestion.swift in Sources */, 020108A729A6ABF600644F9D /* AppTPToggleView.swift in Sources */, + 8562CE152B9B645C00E1D399 /* CachedBookmarkSuggestions.swift in Sources */, 02A54A982A093126000C8FED /* AppTPHomeViewModel.swift in Sources */, C13F3F682B7F88100083BE40 /* AuthConfirmationPromptView.swift in Sources */, F1617C191E573EA800DEDCAF /* TabSwitcherDelegate.swift in Sources */, @@ -6991,7 +6998,6 @@ F1D796F41E7C2A410019D451 /* BookmarksDelegate.swift in Sources */, D664C7B92B289AA200CBFA76 /* WKUserContentController+Handler.swift in Sources */, C1B7B52428941F2A0098FD6A /* RemoteMessageRequest.swift in Sources */, - EE9D68DA2AE1659F00B55EF4 /* NetworkProtectionVPNNotificationsViewModel.swift in Sources */, 1E8AD1D727C2E24E00ABA377 /* DownloadsListRowViewModel.swift in Sources */, 1E865AF0272042DB001C74F3 /* TextSizeSettingsViewController.swift in Sources */, D6E0C1892B7A2E0D00D5E1E9 /* DesktopDownloadViewModel.swift in Sources */, @@ -7051,7 +7057,6 @@ 31C138B227A4097800FFD4B2 /* DownloadTestsHelper.swift in Sources */, 1E1D8B5D2994FFE100C96994 /* AutoconsentMessageProtocolTests.swift in Sources */, 85C11E532090B23A00BFFEB4 /* UserDefaultsHomeRowReminderStorageTests.swift in Sources */, - 85E5603026541D9E00F4DC44 /* AutocompleteRequestTests.swift in Sources */, F1DA2F7D1EBCF23700313F51 /* ExternalUrlSchemeTests.swift in Sources */, F198D78E1E39762C0088DA8A /* StringExtensionTests.swift in Sources */, 31B1FA87286EFC5C00CA3C1C /* XCTestCaseExtension.swift in Sources */, @@ -7070,6 +7075,7 @@ 850559D223CF710C0055C0D5 /* WebCacheManagerTests.swift in Sources */, EEC02C162B065BE00045CE11 /* NetworkProtectionVPNLocationViewModelTests.swift in Sources */, 987130C5294AAB9F00AB05E0 /* BookmarkEditorViewModelTests.swift in Sources */, + BDFF03262BA3DA4900F324C9 /* NetworkProtectionFeatureVisibilityTests.swift in Sources */, 8341D807212D5E8D000514C2 /* HashExtensionTest.swift in Sources */, C1D21E2F293A599C006E5A05 /* AutofillLoginSessionTests.swift in Sources */, 85D2187924BF6B8B004373D2 /* FaviconSourcesProviderTests.swift in Sources */, @@ -7247,7 +7253,6 @@ 37E615752A5F533E00ACD63D /* SyncCredentialsAdapter.swift in Sources */, 02CA904B24F6C11A00D41DDF /* NavigatorSharePatchUserScript.swift in Sources */, 85BDC3192436161C0053DB07 /* LoginFormDetectionUserScript.swift in Sources */, - 98982B3422F8D8E400578AC9 /* Debounce.swift in Sources */, 37DF000A29F9C416002B7D3E /* SyncMetadataDatabase.swift in Sources */, F143C3291E4A9A0E00CFDE3A /* URLExtension.swift in Sources */, F143C3271E4A9A0E00CFDE3A /* Logging.swift in Sources */, @@ -10186,6 +10191,11 @@ package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = RemoteMessaging; }; + 851F74252B9A1BFD00747C42 /* Suggestions */ = { + isa = XCSwiftPackageProductDependency; + package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = Suggestions; + }; 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 39d0949c5f..aab8d9d962 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -140,8 +140,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-argument-parser", "state" : { - "revision" : "c8ed701b513cf5177118a175d85fbbbcd707ab41", - "version" : "1.3.0" + "revision" : "46989693916f56d1186bd59ac15124caef896560", + "version" : "1.3.1" } }, { @@ -183,7 +183,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.xcodeproj/xcshareddata/xcschemes/DuckDuckGo.xcscheme b/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/DuckDuckGo.xcscheme index e564636928..d3926becad 100644 --- a/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/DuckDuckGo.xcscheme +++ b/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/DuckDuckGo.xcscheme @@ -124,7 +124,7 @@ + isEnabled = "YES"> String { - guard !showsFullURL, let shortAddress = shortURLString(url) else { - return url.absoluteString + static func addressForDisplay(url: URL, showsFullURL: Bool) -> NSAttributedString { + + if !showsFullURL, let shortAddress = shortURLString(url) { + return NSAttributedString( + string: shortAddress, + attributes: [.foregroundColor: ThemeManager.shared.currentTheme.searchBarTextColor]) + } else { + return deemphasisePath(forUrl: url) + } + } + + static func deemphasisePath(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 shortAddress + return attributedString } /// Creates a string containing a short version the http(s) URL. diff --git a/DuckDuckGo/AppDelegate+Waitlists.swift b/DuckDuckGo/AppDelegate+Waitlists.swift index 7fae089578..5d58408534 100644 --- a/DuckDuckGo/AppDelegate+Waitlists.swift +++ b/DuckDuckGo/AppDelegate+Waitlists.swift @@ -37,7 +37,9 @@ extension AppDelegate { func checkWaitlists() { #if NETWORK_PROTECTION - checkNetworkProtectionWaitlist() + if vpnFeatureVisibilty.shouldKeepVPNAccessViaWaitlist() { + checkNetworkProtectionWaitlist() + } #endif checkWaitlistBackgroundTasks() @@ -93,6 +95,8 @@ extension AppDelegate { #endif private func checkWaitlistBackgroundTasks() { + guard vpnFeatureVisibilty.shouldKeepVPNAccessViaWaitlist() else { return } + BGTaskScheduler.shared.getPendingTaskRequests { tasks in #if NETWORK_PROTECTION diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index ec4aedf063..7348724343 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -75,6 +75,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { #if NETWORK_PROTECTION private let widgetRefreshModel = NetworkProtectionWidgetRefreshModel() private let tunnelDefaults = UserDefaults.networkProtectionGroupDefaults + lazy var vpnFeatureVisibilty = DefaultNetworkProtectionVisibility() #endif private var autoClear: AutoClear? @@ -303,7 +304,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { AppConfigurationFetch.registerBackgroundRefreshTaskHandler() #if NETWORK_PROTECTION - VPNWaitlist.shared.registerBackgroundRefreshTaskHandler() + if vpnFeatureVisibilty.shouldKeepVPNAccessViaWaitlist() { + VPNWaitlist.shared.registerBackgroundRefreshTaskHandler() + } #endif RemoteMessaging.registerBackgroundRefreshTaskHandler( @@ -332,7 +335,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { setupSubscriptionsEnvironment() #endif - clearDebugWaitlistState() + if vpnFeatureVisibilty.shouldKeepVPNAccessViaWaitlist() { + clearDebugWaitlistState() + } AppDependencyProvider.shared.toggleProtectionsCounter.sendEventsIfNeeded() AppDependencyProvider.shared.userBehaviorMonitor.handleAction(.reopenApp) @@ -385,7 +390,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } } - private func presentExpiredEntitlementNotification() { + private func presentExpiredEntitlementNotificationIfNeeded() { let presenter = NetworkProtectionNotificationsPresenterTogglableDecorator( settings: VPNSettings(defaults: .networkProtectionGroupDefaults), defaults: .networkProtectionGroupDefaults, @@ -393,6 +398,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ) presenter.showEntitlementNotification() } + + private func presentVPNEarlyAccessOverAlert() { + let alertController = CriticalAlerts.makeVPNEarlyAccessOverAlert() + window?.rootViewController?.present(alertController, animated: true) { [weak self] in + self?.tunnelDefaults.vpnEarlyAccessOverAlertAlreadyShown = true + } + } #endif private func cleanUpMacPromoExperiment2() { @@ -417,6 +429,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate { private func setupSubscriptionsEnvironment() { Task { SubscriptionPurchaseEnvironment.currentServiceEnvironment = .staging +#if NETWORK_PROTECTION + if VPNSettings(defaults: .networkProtectionGroupDefaults).selectedEnvironment == .staging { + SubscriptionPurchaseEnvironment.currentServiceEnvironment = .staging + } +#endif SubscriptionPurchaseEnvironment.current = .appStore await AccountManager().checkSubscriptionState() } @@ -474,11 +491,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate { #if NETWORK_PROTECTION widgetRefreshModel.refreshVPNWidget() + if vpnFeatureVisibilty.shouldShowThankYouMessaging() && !tunnelDefaults.vpnEarlyAccessOverAlertAlreadyShown { + presentVPNEarlyAccessOverAlert() + } + if tunnelDefaults.showEntitlementAlert { presentExpiredEntitlementAlert() } - presentExpiredEntitlementNotification() + presentExpiredEntitlementNotificationIfNeeded() #endif updateSubscriptionStatus() @@ -830,7 +851,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func refreshShortcuts() { #if NETWORK_PROTECTION - guard NetworkProtectionKeychainTokenStore().isFeatureActivated else { + guard vpnFeatureVisibilty.shouldShowVPNShortcut() else { + UIApplication.shared.shortcutItems = nil return } @@ -906,10 +928,11 @@ extension AppDelegate: UNUserNotificationCenterDelegate { presentNetworkProtectionStatusSettingsModal() } - if identifier == VPNWaitlist.notificationIdentifier { + if vpnFeatureVisibilty.shouldKeepVPNAccessViaWaitlist(), identifier == VPNWaitlist.notificationIdentifier { presentNetworkProtectionWaitlistModal() DailyPixel.fire(pixel: .networkProtectionWaitlistNotificationLaunched) } + #endif } diff --git a/DuckDuckGo/AppDependencyProvider.swift b/DuckDuckGo/AppDependencyProvider.swift index c928b90ed5..8a1361cfef 100644 --- a/DuckDuckGo/AppDependencyProvider.swift +++ b/DuckDuckGo/AppDependencyProvider.swift @@ -22,6 +22,7 @@ import Core import BrowserServicesKit import DDGSync import Bookmarks +import Subscription protocol DependencyProvider { @@ -39,6 +40,7 @@ protocol DependencyProvider { var configurationManager: ConfigurationManager { get } var toggleProtectionsCounter: ToggleProtectionsCounter { get } var userBehaviorMonitor: UserBehaviorMonitor { get } + var subscriptionFeatureAvailability: SubscriptionFeatureAvailability { get } } @@ -68,5 +70,9 @@ class AppDependencyProvider: DependencyProvider { let toggleProtectionsCounter: ToggleProtectionsCounter = ContentBlocking.shared.privacyConfigurationManager.toggleProtectionsCounter let userBehaviorMonitor = UserBehaviorMonitor() + + let subscriptionFeatureAvailability: SubscriptionFeatureAvailability = DefaultSubscriptionFeatureAvailability( + privacyConfigurationManager: ContentBlocking.shared.privacyConfigurationManager, + purchasePlatform: .appStore) } diff --git a/DuckDuckGo/AppSettings.swift b/DuckDuckGo/AppSettings.swift index b440082ffd..18422b3487 100644 --- a/DuckDuckGo/AppSettings.swift +++ b/DuckDuckGo/AppSettings.swift @@ -56,6 +56,7 @@ protocol AppSettings: AnyObject { var currentFireButtonAnimation: FireButtonAnimationType { get set } var currentAddressBarPosition: AddressBarPosition { get set } + var showFullSiteAddress: Bool { get set } var textSize: Int { get set } diff --git a/DuckDuckGo/AppUserDefaults.swift b/DuckDuckGo/AppUserDefaults.swift index 446bd75fc3..3dbd30d89d 100644 --- a/DuckDuckGo/AppUserDefaults.swift +++ b/DuckDuckGo/AppUserDefaults.swift @@ -36,6 +36,7 @@ public class AppUserDefaults: AppSettings { public static let didVerifyInternalUser = Notification.Name("com.duckduckgo.app.DidVerifyInternalUser") public static let inspectableWebViewsToggled = Notification.Name("com.duckduckgo.app.DidToggleInspectableWebViews") public static let addressBarPositionChanged = Notification.Name("com.duckduckgo.app.AddressBarPositionChanged") + public static let showsFullURLAddressSettingChanged = Notification.Name("com.duckduckgo.app.ShowsFullURLAddressSettingChanged") public static let autofillDebugScriptToggled = Notification.Name("com.duckduckgo.app.DidToggleAutofillDebugScript") } @@ -205,6 +206,13 @@ public class AppUserDefaults: AppSettings { } } + @UserDefaultsWrapper(key: .showFullURLAddress, defaultValue: false) + var showFullSiteAddress: Bool { + didSet { + NotificationCenter.default.post(name: Notifications.showsFullURLAddressSettingChanged, object: showFullSiteAddress) + } + } + @UserDefaultsWrapper(key: .textSize, defaultValue: 100) var textSize: Int diff --git a/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/IP-24.imageset/IP-24.pdf b/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/IP-24.imageset/IP-24.pdf deleted file mode 100644 index 9c1e95fa0d..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/IP-24.imageset/IP-24.pdf and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Server-24.imageset/Server-24.pdf b/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Server-24.imageset/Server-24.pdf deleted file mode 100644 index 78986723df..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Server-24.imageset/Server-24.pdf and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Server-Location-24.imageset/Server-Location-24.pdf b/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Server-Location-24.imageset/Server-Location-24.pdf deleted file mode 100644 index f9c8a03468..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Server-Location-24.imageset/Server-Location-24.pdf and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/VPN.imageset/VPN.pdf b/DuckDuckGo/Assets.xcassets/VPN.imageset/VPN.pdf index 005d8fb066..e4423e08d1 100644 Binary files a/DuckDuckGo/Assets.xcassets/VPN.imageset/VPN.pdf and b/DuckDuckGo/Assets.xcassets/VPN.imageset/VPN.pdf differ diff --git a/DuckDuckGo/Assets.xcassets/VPNDisabled.imageset/VPNDisabled.pdf b/DuckDuckGo/Assets.xcassets/VPNDisabled.imageset/VPNDisabled.pdf index a89d61ec2c..1577b6fc56 100644 Binary files a/DuckDuckGo/Assets.xcassets/VPNDisabled.imageset/VPNDisabled.pdf and b/DuckDuckGo/Assets.xcassets/VPNDisabled.imageset/VPNDisabled.pdf differ diff --git a/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/IP-24.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/VPNDownload.imageset/Contents.json similarity index 71% rename from DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/IP-24.imageset/Contents.json rename to DuckDuckGo/Assets.xcassets/VPNDownload.imageset/Contents.json index 44cb822df9..c9df096cfc 100644 --- a/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/IP-24.imageset/Contents.json +++ b/DuckDuckGo/Assets.xcassets/VPNDownload.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "IP-24.pdf", + "filename" : "vpn-download.pdf", "idiom" : "universal" } ], @@ -10,7 +10,6 @@ "version" : 1 }, "properties" : { - "preserves-vector-representation" : true, "template-rendering-intent" : "template" } } diff --git a/DuckDuckGo/Assets.xcassets/VPNDownload.imageset/vpn-download.pdf b/DuckDuckGo/Assets.xcassets/VPNDownload.imageset/vpn-download.pdf new file mode 100644 index 0000000000..55844a6647 Binary files /dev/null and b/DuckDuckGo/Assets.xcassets/VPNDownload.imageset/vpn-download.pdf differ diff --git a/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Server-24.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/VPNLocation.imageset/Contents.json similarity index 70% rename from DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Server-24.imageset/Contents.json rename to DuckDuckGo/Assets.xcassets/VPNLocation.imageset/Contents.json index 1a54140107..bd59670506 100644 --- a/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Server-24.imageset/Contents.json +++ b/DuckDuckGo/Assets.xcassets/VPNLocation.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "Server-24.pdf", + "filename" : "VPNLocation.pdf", "idiom" : "universal" } ], @@ -10,7 +10,6 @@ "version" : 1 }, "properties" : { - "preserves-vector-representation" : true, "template-rendering-intent" : "template" } } diff --git a/DuckDuckGo/Assets.xcassets/VPNLocation.imageset/VPNLocation.pdf b/DuckDuckGo/Assets.xcassets/VPNLocation.imageset/VPNLocation.pdf new file mode 100644 index 0000000000..2cfa6eb89d Binary files /dev/null and b/DuckDuckGo/Assets.xcassets/VPNLocation.imageset/VPNLocation.pdf differ diff --git a/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Server-Location-24.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/VPNUpload.imageset/Contents.json similarity index 67% rename from DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Server-Location-24.imageset/Contents.json rename to DuckDuckGo/Assets.xcassets/VPNUpload.imageset/Contents.json index 88fc331fe5..4efc61b832 100644 --- a/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Server-Location-24.imageset/Contents.json +++ b/DuckDuckGo/Assets.xcassets/VPNUpload.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "Server-Location-24.pdf", + "filename" : "vpn-upload.pdf", "idiom" : "universal" } ], @@ -10,7 +10,6 @@ "version" : 1 }, "properties" : { - "preserves-vector-representation" : true, "template-rendering-intent" : "template" } } diff --git a/DuckDuckGo/Assets.xcassets/VPNUpload.imageset/vpn-upload.pdf b/DuckDuckGo/Assets.xcassets/VPNUpload.imageset/vpn-upload.pdf new file mode 100644 index 0000000000..97a505ff33 Binary files /dev/null and b/DuckDuckGo/Assets.xcassets/VPNUpload.imageset/vpn-upload.pdf differ diff --git a/DuckDuckGo/AutocompleteRequest.swift b/DuckDuckGo/AutocompleteRequest.swift deleted file mode 100644 index ab17ef62f6..0000000000 --- a/DuckDuckGo/AutocompleteRequest.swift +++ /dev/null @@ -1,95 +0,0 @@ -// -// AutocompleteRequest.swift -// DuckDuckGo -// -// Copyright © 2017 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 -import Networking - -class AutocompleteRequest { - - enum Error: Swift.Error { - - case noData - - } - - private static let session = URLSession(configuration: .ephemeral) - - typealias Completion = ([Suggestion]?, Swift.Error?) -> Void - - private let url: URL - private var task: URLSessionDataTask? - - init(query: String) throws { - self.url = try URL.makeAutocompleteURL(for: query) - } - - func execute(completion: @escaping Completion) { - var request = URLRequest.developerInitiated(url) - request.allHTTPHeaderFields = APIRequest.Headers().httpHeaders - - task = AutocompleteRequest.session.dataTask(with: request) { [weak self] (data, _, error) in - guard let weakSelf = self else { return } - do { - let suggestions = try weakSelf.processResult(data: data, error: error) - weakSelf.complete(completion, withSuccess: suggestions) - } catch { - weakSelf.complete(completion, withError: error) - } - } - task?.resume() - } - - private func processResult(data: Data?, error: Swift.Error?) throws -> [Suggestion] { - if let error = error { throw error } - guard let data = data else { throw Error.noData } - let entries = try JSONDecoder().decode([AutocompleteEntry].self, from: data) - - return entries.compactMap { - guard let phrase = $0.phrase else { return nil } - - if let isNav = $0.isNav { - // We definitely have a nav indication so use it. Phrase should be a fully qualified URL. - // Assume HTTP and that we'll auto-upgrade if needed. - let url = isNav ? URL(string: "http://\(phrase)") : nil - return Suggestion(source: .remote, suggestion: phrase, url: url) - } else { - // We need to infer nav based on the phrase to maintain previous behaviour (ie treat phrase that look like URLs like URLs) - let url = URL.webUrl(from: phrase) - return Suggestion(source: .remote, suggestion: phrase, url: url) - } - } - } - - private func complete(_ completion: @escaping Completion, withSuccess suggestions: [Suggestion]) { - DispatchQueue.main.async { - completion(suggestions, nil) - } - } - - private func complete(_ completion: @escaping Completion, withError error: Swift.Error) { - DispatchQueue.main.async { - completion(nil, error) - } - } - - func cancel() { - task?.cancel() - } -} diff --git a/DuckDuckGo/AutocompleteViewController.swift b/DuckDuckGo/AutocompleteViewController.swift index caa9e5b35a..8f57da2bb2 100644 --- a/DuckDuckGo/AutocompleteViewController.swift +++ b/DuckDuckGo/AutocompleteViewController.swift @@ -21,29 +21,42 @@ import Common import UIKit import Core import DesignResourcesKit +import Suggestions +import Networking +import CoreData +import Persistence +import History +import Combine class AutocompleteViewController: UIViewController { + private static let session = URLSession(configuration: .ephemeral) + struct Constants { - static let debounceDelay: TimeInterval = 0.1 + static let debounceDelay = 100 // millis static let minItems = 1 - static let maxLocalItems = 2 } weak var delegate: AutocompleteViewControllerDelegate? weak var presentationDelegate: AutocompleteViewControllerPresentationDelegate? - private var lastRequest: AutocompleteRequest? + private var task: URLSessionDataTask? + private var loader: SuggestionLoading? private var receivedResponse = false private var pendingRequest = false - fileprivate var query = "" + @Published fileprivate var query = "" + fileprivate var queryDebounceCancellable: AnyCancellable? + fileprivate var suggestions = [Suggestion]() fileprivate var selectedItem = -1 - private var bookmarksSearch: BookmarksStringSearch! - + private var historyCoordinator: HistoryCoordinating! + private var bookmarksDatabase: CoreDataDatabase! private var appSettings: AppSettings! + private lazy var cachedBookmarks: CachedBookmarks = { + CachedBookmarks(bookmarksDatabase) + }() var backgroundColor: UIColor { appSettings.currentAddressBarPosition.isBottom ? @@ -63,20 +76,19 @@ class AutocompleteViewController: UIViewController { } private var hidesBarsOnSwipeDefault = true - - private let debounce = Debounce(queue: .main, seconds: Constants.debounceDelay) @IBOutlet weak var tableView: UITableView! var shouldOffsetY = false - static func loadFromStoryboard(bookmarksSearch: BookmarksStringSearch, + static func loadFromStoryboard(bookmarksDatabase: CoreDataDatabase, + historyCoordinator: HistoryCoordinating, appSettings: AppSettings = AppDependencyProvider.shared.appSettings) -> AutocompleteViewController { let storyboard = UIStoryboard(name: "Autocomplete", bundle: nil) - guard let controller = storyboard.instantiateInitialViewController() as? AutocompleteViewController else { fatalError("Failed to instatiate correct Autocomplete view controller") } - controller.bookmarksSearch = bookmarksSearch + controller.bookmarksDatabase = bookmarksDatabase + controller.historyCoordinator = historyCoordinator controller.appSettings = appSettings return controller } @@ -85,6 +97,12 @@ class AutocompleteViewController: UIViewController { super.viewDidLoad() configureTableView() applyTheme(ThemeManager.shared.currentTheme) + + queryDebounceCancellable = $query + .debounce(for: .milliseconds(Constants.debounceDelay), scheduler: RunLoop.main) + .sink { [weak self] query in + self?.requestSuggestions(query: query) + } } private func configureTableView() { @@ -125,24 +143,29 @@ class AutocompleteViewController: UIViewController { } func updateQuery(query: String) { - self.query = query selectedItem = -1 cancelInFlightRequests() - debounce.schedule { [weak self] in - self?.requestSuggestions(query: query) - } + self.query = query } func willDismiss(with query: String) { - guard selectedItem != -1, selectedItem < suggestions.count else { return } - + guard suggestions.indices.contains(selectedItem) else { return } let suggestion = suggestions[selectedItem] - if let url = suggestion.url { - if query == url.absoluteString { - firePixel(selectedSuggestion: suggestion) - } - } else if query == suggestion.suggestion { - firePixel(selectedSuggestion: suggestion) + firePixelForSelectedSuggestion(suggestion) + } + + private func firePixelForSelectedSuggestion(_ suggestion: Suggestion) { + switch suggestion { + case .phrase: + Pixel.fire(pixel: .autocompleteClickPhrase) + case .website: + Pixel.fire(pixel: .autocompleteClickWebsite) + case .bookmark(_, _, isFavorite: let isFavorite, _): + Pixel.fire(pixel: isFavorite ? .autocompleteClickFavorite : .autocompleteClickBookmark) + case .historyEntry: + Pixel.fire(pixel: .autocompleteClickHistory) + case .unknown(value: let value): + assertionFailure("Unknown suggestion \(value)") } } @@ -152,47 +175,32 @@ class AutocompleteViewController: UIViewController { } private func cancelInFlightRequests() { - if let inFlightRequest = lastRequest { - inFlightRequest.cancel() - lastRequest = nil - } + task?.cancel() + task = nil } private func requestSuggestions(query: String) { selectedItem = -1 tableView.reloadData() - do { - lastRequest = try AutocompleteRequest(query: query) - pendingRequest = true - } catch { - os_log("Couldn‘t form AutocompleteRequest for query “%s”: %s", log: .lifecycleLog, type: .debug, query, error.localizedDescription) - lastRequest = nil - pendingRequest = false - return - } - lastRequest!.execute { [weak self] (suggestions, error) in - guard let strongSelf = self else { return } - - Task { @MainActor in - let matches = strongSelf.bookmarksSearch.search(query: query) - let notQueryMatches = matches.filter { $0.url.absoluteString != query } - let filteredMatches = notQueryMatches.prefix(Constants.maxLocalItems) - let localSuggestions = filteredMatches.map { Suggestion(source: .local, - suggestion: $0.title, - url: $0.url) - } - - guard let suggestions = suggestions, error == nil else { - os_log("%s", log: .generalLog, type: .debug, error?.localizedDescription ?? "Failed to retrieve suggestions") - self?.updateSuggestions(localSuggestions) - return - } - - let combinedSuggestions = localSuggestions + suggestions - strongSelf.updateSuggestions(Array(combinedSuggestions)) - strongSelf.pendingRequest = false + loader = SuggestionLoader(dataSource: self, urlFactory: { phrase in + guard let url = URL(trimmedAddressBarString: phrase), + let scheme = url.scheme, + scheme.description.hasPrefix("http"), + url.isValid else { + return nil + } + + return url + }) + pendingRequest = true + + loader?.getSuggestions(query: query) { [weak self] result, error in + defer { + self?.pendingRequest = false } + guard error == nil else { return } + self?.updateSuggestions(result?.all ?? []) } } @@ -262,37 +270,29 @@ extension AutocompleteViewController: UITableViewDataSource { if appSettings.currentAddressBarPosition.isBottom && suggestions.isEmpty { return view.frame.height } - return 46 + + let defaultHeight: CGFloat = 46 + guard suggestions.indices.contains(indexPath.row) else { return defaultHeight } + + switch suggestions[indexPath.row] { + case .bookmark, .historyEntry: + return 60 + default: + return defaultHeight + } } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return receivedResponse ? max(Constants.minItems, suggestions.count) : 0 } - - private func firePixel(selectedSuggestion: Suggestion) { - let resultsIncludeBookmarks: Bool - if let firstSuggestion = suggestions.first { - resultsIncludeBookmarks = firstSuggestion.source == .local - } else { - resultsIncludeBookmarks = false - } - - let params = [PixelParameters.autocompleteBookmarkCapable: bookmarksSearch.hasData ? "true" : "false", - PixelParameters.autocompleteIncludedLocalResults: resultsIncludeBookmarks ? "true" : "false"] - - if selectedSuggestion.source == .local { - Pixel.fire(pixel: .autocompleteSelectedLocal, withAdditionalParameters: params) - } else { - Pixel.fire(pixel: .autocompleteSelectedRemote, withAdditionalParameters: params) - } - } + } extension AutocompleteViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let suggestion = suggestions[indexPath.row] - firePixel(selectedSuggestion: suggestion) delegate?.autocomplete(selectedSuggestion: suggestion) + firePixelForSelectedSuggestion(suggestion) } } @@ -334,3 +334,37 @@ extension AutocompleteViewController { } } + +extension AutocompleteViewController: SuggestionLoadingDataSource { + + func history(for suggestionLoading: Suggestions.SuggestionLoading) -> [HistorySuggestion] { + return historyCoordinator.history ?? [] + } + + func bookmarks(for suggestionLoading: Suggestions.SuggestionLoading) -> [Suggestions.Bookmark] { + return cachedBookmarks.all + } + + func suggestionLoading(_ suggestionLoading: Suggestions.SuggestionLoading, suggestionDataFromUrl url: URL, withParameters parameters: [String: String], completion: @escaping (Data?, Error?) -> Void) { + var queryURL = url + parameters.forEach { + queryURL = queryURL.appendingParameter(name: $0.key, value: $0.value) + } + + var request = URLRequest.developerInitiated(queryURL) + request.allHTTPHeaderFields = APIRequest.Headers().httpHeaders + task = Self.session.dataTask(with: request) { data, _, error in + completion(data, error) + } + task?.resume() + } + +} + +extension HistoryEntry: HistorySuggestion { + + public var numberOfVisits: Int { + return numberOfTotalVisits + } + +} diff --git a/DuckDuckGo/AutocompleteViewControllerDelegate.swift b/DuckDuckGo/AutocompleteViewControllerDelegate.swift index fbb0b6d9fb..e7f3671328 100644 --- a/DuckDuckGo/AutocompleteViewControllerDelegate.swift +++ b/DuckDuckGo/AutocompleteViewControllerDelegate.swift @@ -18,6 +18,7 @@ // import UIKit +import Suggestions protocol AutocompleteViewControllerDelegate: AnyObject { diff --git a/DuckDuckGo/AutofillLoginPromptViewController.swift b/DuckDuckGo/AutofillLoginPromptViewController.swift index d2c78acb49..701ef51c0e 100644 --- a/DuckDuckGo/AutofillLoginPromptViewController.swift +++ b/DuckDuckGo/AutofillLoginPromptViewController.swift @@ -28,6 +28,7 @@ class AutofillLoginPromptViewController: UIViewController { typealias AutofillLoginPromptViewControllerCompletion = (_ account: SecureVaultModels.WebsiteAccount?, _ showExpanded: Bool) -> Void let completion: AutofillLoginPromptViewControllerCompletion? + let onAccountSelected: (SecureVaultModels.WebsiteAccount?) -> Void private let accounts: AccountMatches private let domain: String @@ -36,10 +37,12 @@ class AutofillLoginPromptViewController: UIViewController { internal init(accounts: AccountMatches, domain: String, trigger: AutofillUserScript.GetTriggerType, + onAccountSelected: @escaping (SecureVaultModels.WebsiteAccount?) -> Void, completion: AutofillLoginPromptViewControllerCompletion? = nil) { self.accounts = accounts self.domain = domain self.trigger = trigger + self.onAccountSelected = onAccountSelected self.completion = completion super.init(nibName: nil, bundle: nil) } @@ -112,6 +115,8 @@ extension AutofillLoginPromptViewController: AutofillLoginPromptViewModelDelegat Pixel.fire(pixel: .autofillLoginsFillLoginInlineManualConfirmed) } + onAccountSelected(account) + if AppDependencyProvider.shared.autofillLoginSession.isSessionValid { dismiss(animated: true, completion: nil) completion?(account, false) diff --git a/DuckDuckGo/Base.lproj/Autocomplete.storyboard b/DuckDuckGo/Base.lproj/Autocomplete.storyboard index 3254452129..2e7d24e4c4 100644 --- a/DuckDuckGo/Base.lproj/Autocomplete.storyboard +++ b/DuckDuckGo/Base.lproj/Autocomplete.storyboard @@ -1,9 +1,10 @@ - + - + + @@ -20,7 +21,7 @@ - + @@ -34,13 +35,28 @@ + + + + - + + + + + + +