Skip to content

Commit

Permalink
Add Sync feature flags (#2279)
Browse files Browse the repository at this point in the history
Task/Issue URL: https://app.asana.com/0/0/1206046777189407/f

Description:
This change add support for Sync feature in Privacy Configuration, together with
4 subfeatures defining availability of various parts of Sync experience.
DDGSyncing gets a read-only feature flag variable as well as a publisher.
PrivacyConfigurationManager is now a Sync dependency, and DDGSync takes
care internally of listening to Privacy Config changes and updating feature flags
as needed.
Feature flag responsible for actual data syncing is handled internally in DDGSync
by cancelling all pending sync operations and disabling adding new operations.
Other feature flags should be handled by client apps.
  • Loading branch information
ayoy authored Dec 20, 2023
1 parent 0d613be commit 4240dd5
Show file tree
Hide file tree
Showing 16 changed files with 217 additions and 80 deletions.
4 changes: 2 additions & 2 deletions Core/AppPrivacyConfigurationDataProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ import BrowserServicesKit
final public class AppPrivacyConfigurationDataProvider: EmbeddedDataProvider {

public struct Constants {
public static let embeddedDataETag = "\"f4e8436ab9977e1a8a9d6ee600fc353e\""
public static let embeddedDataSHA = "86b3a7bece52da74f7d267c2b522ac929d363a384cacc013f2b2d057ee1e386c"
public static let embeddedDataETag = "\"388dd0526e94f80473728c0bfbb48b39\""
public static let embeddedDataSHA = "f7b9ae8860ff84f33e602b40d0938776d2d9327115b4ddfe09fc0fa09b5e1ff1"
}

public var embeddedDataEtag: String {
Expand Down
4 changes: 3 additions & 1 deletion Core/FeatureFlag.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ public enum FeatureFlag: String {
extension FeatureFlag: FeatureFlagSourceProviding {
public var source: FeatureFlagSource {
switch self {
case .debugMenu, .sync, .appTrackingProtection:
case .debugMenu, .appTrackingProtection:
return .internalOnly
case .sync:
return .remoteReleasable(.subfeature(SyncSubfeature.level0ShowSync))
case .networkProtection:
return .remoteReleasable(.feature(.networkProtection))
case .networkProtectionWaitlistAccess:
Expand Down
73 changes: 63 additions & 10 deletions Core/ios-config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"readme": "https://github.com/duckduckgo/privacy-configuration",
"version": 1702917767277,
"version": 1703026028516,
"features": {
"adClickAttribution": {
"readme": "https://help.duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/#3rd-party-tracker-loading-protection",
Expand Down Expand Up @@ -2956,11 +2956,14 @@
]
},
{
"domain": "orange.fr",
"domain": "oceanofcompressed.xyz",
"rules": [
{
"selector": ".tag-rm",
"type": "hide-empty"
"type": "disable-default"
},
{
"selector": "#sticky-ads",
"type": "hide"
}
]
},
Expand All @@ -2977,6 +2980,15 @@
}
]
},
{
"domain": "orange.fr",
"rules": [
{
"selector": ".tag-rm",
"type": "hide-empty"
}
]
},
{
"domain": "ouest-france.fr",
"rules": [
Expand Down Expand Up @@ -3788,7 +3800,7 @@
]
},
"state": "enabled",
"hash": "81d363bc5cd7d76de1aa11d5b0f8e27d"
"hash": "182ef21a9dcfd3a160468f851c4b1789"
},
"exceptionHandler": {
"exceptions": [
Expand Down Expand Up @@ -4502,6 +4514,25 @@
"state": "disabled",
"hash": "5e792dd491428702bc0104240fbce0ce"
},
"sync": {
"exceptions": [],
"state": "internal",
"features": {
"level0ShowSync": {
"state": "enabled"
},
"level1AllowDataSyncing": {
"state": "enabled"
},
"level2AllowSetupFlows": {
"state": "enabled"
},
"level3AllowCreateAccount": {
"state": "enabled"
}
},
"hash": "92673fe625ae2b888a4b0bfa9a974ce4"
},
"trackerAllowlist": {
"state": "enabled",
"settings": {
Expand Down Expand Up @@ -4764,6 +4795,12 @@
"wxii12.com",
"wyff4.com"
]
},
{
"rule": "z-na.amazon-adsystem.com/widgets/onejs",
"domains": [
"oceanofcompressed.xyz"
]
}
]
},
Expand Down Expand Up @@ -5522,8 +5559,7 @@
{
"rule": "app.five9.com",
"domains": [
"gmsdnv.com",
"machiassavings.bank"
"<all>"
]
}
]
Expand Down Expand Up @@ -6058,7 +6094,13 @@
{
"rule": "api.hubspot.com/livechat-public/v1/message/public",
"domains": [
"pippintitle.com"
"<all>"
]
},
{
"rule": "js.hubspot.com/web-interactives-embed.js",
"domains": [
"<all>"
]
},
{
Expand Down Expand Up @@ -6367,6 +6409,16 @@
}
]
},
"media.net": {
"rules": [
{
"rule": "contextual.media.net/dmedianet.js",
"domains": [
"oceanofcompressed.xyz"
]
}
]
},
"mediavine.com": {
"rules": [
{
Expand Down Expand Up @@ -6825,7 +6877,8 @@
{
"rule": "secure.quantserve.com/quant.js",
"domains": [
"aternos.org"
"aternos.org",
"oceanofcompressed.xyz"
]
}
]
Expand Down Expand Up @@ -7510,7 +7563,7 @@
"domain": "sundancecatalog.com"
}
],
"hash": "163c9ec4fc3bdb9dbfc75e70839a31d7"
"hash": "c1968268cb8a82bf532443edd17d9499"
},
"trackingCookies1p": {
"settings": {
Expand Down
2 changes: 1 addition & 1 deletion DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9221,7 +9221,7 @@
repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit";
requirement = {
kind = exactVersion;
version = 96.0.0;
version = 97.0.0;
};
};
C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */ = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/DuckDuckGo/BrowserServicesKit",
"state" : {
"revision" : "e5c9e31b19b3cf78e2b704a60882ba24b9bc680d",
"version" : "96.0.0"
"revision" : "d671accf1bf7097c4e7f5cd55cd1c6dfa005cf92",
"version" : "97.0.0"
}
},
{
Expand Down
8 changes: 7 additions & 1 deletion DuckDuckGo/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
favoritesDisplayModeStorage: FavoritesDisplayModeStorage()
)

let syncService = DDGSync(dataProvidersSource: syncDataProviders, errorEvents: SyncErrorHandler(), log: .syncLog, environment: environment)
let syncService = DDGSync(
dataProvidersSource: syncDataProviders,
errorEvents: SyncErrorHandler(),
privacyConfigurationManager: ContentBlocking.shared.privacyConfigurationManager,
log: .syncLog,
environment: environment
)
syncService.initializeIfNeeded()
self.syncService = syncService

Expand Down
3 changes: 2 additions & 1 deletion DuckDuckGo/FaviconsFetcherOnboarding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ final class FaviconsFetcherOnboarding {
}

private var shouldPresentOnboarding: Bool {
!didPresentFaviconsFetchingOnboarding
syncService.featureFlags.contains(.userInterface)
&& !didPresentFaviconsFetchingOnboarding
&& !syncBookmarksAdapter.isFaviconsFetchingEnabled
&& syncBookmarksAdapter.isEligibleForFaviconsFetcherOnboarding
}
Expand Down
2 changes: 1 addition & 1 deletion DuckDuckGo/SettingsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ class SettingsViewController: UITableViewController {
}

private var shouldShowSyncCell: Bool {
return featureFlagger.isFeatureOn(.sync)
return syncService.featureFlags.contains(.userInterface) || internalUserDecider.isInternalUser
}

private var shouldShowTextSizeCell: Bool {
Expand Down
14 changes: 14 additions & 0 deletions DuckDuckGo/SyncSettingsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class SyncSettingsViewController: UIHostingController<SyncSettingsView> {
setUpFaviconsFetcherSwitch(viewModel)
setUpFavoritesDisplayModeSwitch(viewModel, appSettings)
setUpSyncPaused(viewModel, appSettings)
setUpSyncFeatureFlags(viewModel)
refreshForState(syncService.authState)

syncService.authStatePublisher
Expand All @@ -87,6 +88,19 @@ class SyncSettingsViewController: UIHostingController<SyncSettingsView> {
fatalError("init(coder:) has not been implemented")
}

private func setUpSyncFeatureFlags(_ viewModel: SyncSettingsViewModel) {
syncService.featureFlagsPublisher.prepend(syncService.featureFlags)
.removeDuplicates()
.receive(on: DispatchQueue.main)
.sink { featureFlags in
viewModel.isDataSyncingAvailable = featureFlags.contains(.dataSyncing)
viewModel.isConnectingDevicesAvailable = featureFlags.contains(.connectFlows)
viewModel.isAccountCreationAvailable = featureFlags.contains(.accountCreation)
viewModel.isAccountRecoveryAvailable = featureFlags.contains(.accountRecovery)
}
.store(in: &cancellables)
}

private func setUpFaviconsFetcherSwitch(_ viewModel: SyncSettingsViewModel) {
viewModel.isFaviconsFetchingEnabled = syncBookmarksAdapter.isFaviconsFetchingEnabled

Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/DuckUI/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ let package = Package(
targets: ["DuckUI"])
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "96.0.0"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "97.0.0"),
],
targets: [
.target(
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/SyncUI/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ let package = Package(
],
dependencies: [
.package(path: "../DuckUI"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "96.0.0"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "97.0.0"),
.package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0")
],
targets: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ public class SyncSettingsViewModel: ObservableObject {
@Published var isBusy = false
@Published var recoveryCode = ""

@Published public var isDataSyncingAvailable: Bool = true
@Published public var isConnectingDevicesAvailable: Bool = true
@Published public var isAccountCreationAvailable: Bool = true
@Published public var isAccountRecoveryAvailable: Bool = true

public weak var delegate: SyncManagementViewModelDelegate?
private(set) var isOnDevEnvironment: Bool
private(set) var switchToProdEnvironment: () -> Void = {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ struct UserText {
static let turnSyncOffSectionHeader = NSLocalizedString("turn.sync.off.section.header", value: "Sync Enabled", comment: "Turn Sync Off - Section Header")
static let turnSyncOffSectionFooter = NSLocalizedString("turn.sync.off.section.footer", value: "Bookmarks and passwords are currently synced across your devices.", comment: "Turn Sync Off - Section Footer")
// Sync Paused Errors
static let syncLimitExceededTitle = NSLocalizedString("sync.limit.exceeded.title", value: "⚠️ Sync Paused", comment: "Sync Paused Errors - Title")
static let syncLimitExceededTitle = NSLocalizedString("sync.limit.exceeded.title", value: "Sync Paused", comment: "Sync Paused Errors - Title")
static let bookmarksLimitExceededDescription = NSLocalizedString("bookmarks.limit.exceeded.description", value: "Bookmark limit exceeded. Delete some to resume syncing.", comment: "Sync Paused Errors - Bookmarks Limit Exceeded Description")
static let credentialsLimitExceededDescription = NSLocalizedString("credentials.limit.exceeded.description", value: "Logins limit exceeded. Delete some to resume syncing.", comment: "Sync Paused Errors - Credentials Limit Exceeded Description")
static let bookmarksLimitExceededAction = NSLocalizedString("bookmarks.limit.exceeded.action", value: "Manage Bookmarks", comment: "Sync Paused Errors - Bookmarks Limit Exceeded Action")
Expand Down Expand Up @@ -159,6 +159,10 @@ struct UserText {
static let fetchFaviconsOnboardingMessage = NSLocalizedString("fetch.favicons.onboarding.message", value: "Do you want this device to automatically download icons for any new bookmarks synced from your other devices? This will expose the download to your network any time a bookmark is synced.", comment: "Fetch Favicons Onboarding - Message")
static let fetchFaviconsOnboardingButtonTitle = NSLocalizedString("fetch.favicons.onboarding.button.title", value: "Keep Bookmarks Icons Updated", comment: "Fetch Favicons Onboarding - Button Title")

// Sync Feature Flags
static let serviceUnavailable = NSLocalizedString("sync.warning.service.unavailable", value: "Service Unavailable", comment: "Title of the warning message")
static let warningSyncDisabled = NSLocalizedString("sync.warning.sync.disabled", value: "We apologize, but the service is currently unavailable. Please try again later.", comment: "Sync unavailable warning message")
static let warningAccountCreationDisabled = NSLocalizedString("sync.warning.account.creation.disabled", value: "We apologize, but new account creation is currently unavailable for this service. Please try again later.", comment: "Sync unavailable warning message")

// swiftlint:enable line_length
}
Loading

0 comments on commit 4240dd5

Please sign in to comment.