Skip to content

Commit

Permalink
Merge branch 'main' into sam/vpn-ui-improvements
Browse files Browse the repository at this point in the history
# By Anh Do (2) and others
# Via GitHub
* main:
  23. Subscriptions:  - Feature Flags (#2605)
  fix settings maestro text (#2613)
  Web pixels (handlers + pixels) (#2611)
  Handle subscription-related iOS use cases (#2597)
  Remove hardcoded NetP staging endpoint (#2612)
  Add Privacy Manifests to app extensions (#2608)
  Add a VPN metadata section to the debug UI (#2502)
  Use History in Suggestions on iOS (#2552)

# Conflicts:
#	DuckDuckGo.xcodeproj/project.pbxproj
#	DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
  • Loading branch information
samsymons committed Mar 20, 2024
2 parents e6e14f6 + 933d14e commit f04e39e
Show file tree
Hide file tree
Showing 51 changed files with 1,182 additions and 603 deletions.
5 changes: 5 additions & 0 deletions .maestro/data_clearing_tests/02_duckduckgo_settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ disabled_rules:
- todo
- unused_capture_list
- trailing_comma
- cyclomatic_complexity

opt_in_rules:
- closure_end_indentation
Expand Down
52 changes: 37 additions & 15 deletions Core/BookmarksCachingSearch.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<NSFetchRequestResult>(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]]
Expand All @@ -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<NSFetchRequestResult> {
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<NSFetchRequestResult>(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 {
Expand Down Expand Up @@ -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<NSManagedObject>)?.isEmpty != true)
isFavorite: favoritesFolderCount > 0)
}

public func togglingFavorite() -> BookmarksStringSearchResult {
Expand Down
39 changes: 0 additions & 39 deletions Core/Debounce.swift

This file was deleted.

4 changes: 4 additions & 0 deletions Core/DefaultVariantManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
]

Expand Down
4 changes: 2 additions & 2 deletions Core/FeatureFlag.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ public enum FeatureFlag: String {
case networkProtection
case networkProtectionWaitlistAccess
case networkProtectionWaitlistActive
case subscription
case swipeTabs
case autoconsentOnByDefault
case history
Expand All @@ -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))
Expand Down Expand Up @@ -71,6 +70,7 @@ extension FeatureFlag: FeatureFlagSourceProviding {
return .remoteReleasable(.subfeature(AutoconsentSubfeature.onByDefault))
case .history:
return .remoteReleasable(.feature(.history))

}
}
}
Expand Down
9 changes: 4 additions & 5 deletions Core/HistoryCapture.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
34 changes: 27 additions & 7 deletions Core/PixelEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -596,6 +601,11 @@ extension Pixel {
case privacyProSubscriptionManagementEmail
case privacyProSubscriptionManagementPlanBilling
case privacyProSubscriptionManagementRemoval
// Web pixels
case privacyProOfferMonthlyPriceClick
case privacyProOfferYearlyPriceClick
case privacyProAddEmailSuccess
case privacyProWelcomeFAQClick
}

}
Expand Down Expand Up @@ -677,9 +687,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)"

Expand Down Expand Up @@ -1133,7 +1148,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"
Expand Down Expand Up @@ -1164,6 +1179,11 @@ 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"
// 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"
}
}
}
Expand Down
Loading

0 comments on commit f04e39e

Please sign in to comment.