From 496e489bb4973da86aa99228e31020de50151d93 Mon Sep 17 00:00:00 2001 From: Dominik Kapusta Date: Mon, 18 Dec 2023 21:14:05 +0100 Subject: [PATCH 1/8] Update syncService initializer and hide sync from settings based on feature flag --- Core/FeatureFlag.swift | 4 +- DuckDuckGo.xcodeproj/project.pbxproj | 4 +- .../xcshareddata/swiftpm/Package.resolved | 350 +++++++++--------- DuckDuckGo/AppDelegate.swift | 8 +- DuckDuckGo/FaviconsFetcherOnboarding.swift | 3 +- DuckDuckGo/SettingsViewController.swift | 2 +- 6 files changed, 189 insertions(+), 182 deletions(-) diff --git a/Core/FeatureFlag.swift b/Core/FeatureFlag.swift index dfe79f7d48..06e6589641 100644 --- a/Core/FeatureFlag.swift +++ b/Core/FeatureFlag.swift @@ -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: diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 12c0f57d98..e10e235bfc 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -9220,8 +9220,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { - kind = exactVersion; - version = 94.0.3; + branch = "dominik/sync-feature-flags"; + kind = branch; }; }; C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 914e51ecef..84c0e2460e 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,178 +1,176 @@ { - "object": { - "pins": [ - { - "package": "BloomFilter", - "repositoryURL": "https://github.com/duckduckgo/bloom_cpp.git", - "state": { - "branch": null, - "revision": "8076199456290b61b4544bf2f4caf296759906a0", - "version": "3.0.0" - } - }, - { - "package": "BrowserServicesKit", - "repositoryURL": "https://github.com/DuckDuckGo/BrowserServicesKit", - "state": { - "branch": null, - "revision": "d07e18b3785d31c15364e56bdd2cb4b27edd35d0", - "version": "94.0.3" - } - }, - { - "package": "CocoaAsyncSocket", - "repositoryURL": "https://github.com/robbiehanson/CocoaAsyncSocket", - "state": { - "branch": null, - "revision": "dbdc00669c1ced63b27c3c5f052ee4d28f10150c", - "version": "7.6.5" - } - }, - { - "package": "ContentScopeScripts", - "repositoryURL": "https://github.com/duckduckgo/content-scope-scripts", - "state": { - "branch": null, - "revision": "b7ad9843e70cede0c2ca9c4260d970f62cb28156", - "version": "4.52.0" - } - }, - { - "package": "DesignResourcesKit", - "repositoryURL": "https://github.com/duckduckgo/DesignResourcesKit", - "state": { - "branch": null, - "revision": "d7ea2561ec7624c224f52e1c9b349075ddf1c782", - "version": "2.0.0" - } - }, - { - "package": "Autofill", - "repositoryURL": "https://github.com/duckduckgo/duckduckgo-autofill.git", - "state": { - "branch": null, - "revision": "5597bc17709c8acf454ecaad4f4082007986242a", - "version": "10.0.2" - } - }, - { - "package": "GRDB", - "repositoryURL": "https://github.com/duckduckgo/GRDB.swift.git", - "state": { - "branch": null, - "revision": "77d9a83191a74e319a5cfa27b0e3145d15607573", - "version": "2.2.0" - } - }, - { - "package": "FindInPageIOSJSSupport", - "repositoryURL": "https://github.com/duckduckgo/ios-js-support", - "state": { - "branch": null, - "revision": "6a6789ac8104a587316c58af75539753853b50d9", - "version": "2.0.0" - } - }, - { - "package": "Kingfisher", - "repositoryURL": "https://github.com/onevcat/Kingfisher.git", - "state": { - "branch": null, - "revision": "af4be924ad984cf4d16f4ae4df424e79a443d435", - "version": "7.6.2" - } - }, - { - "package": "Lottie", - "repositoryURL": "https://github.com/duckduckgo/lottie-ios.git", - "state": { - "branch": null, - "revision": "abf5510e261c85ffddd29de0bca9b72592ea2bdd", - "version": "3.3.0" - } - }, - { - "package": "OHHTTPStubs", - "repositoryURL": "https://github.com/AliSoftware/OHHTTPStubs.git", - "state": { - "branch": null, - "revision": "12f19662426d0434d6c330c6974d53e2eb10ecd9", - "version": "9.1.0" - } - }, - { - "package": "PrivacyDashboardResources", - "repositoryURL": "https://github.com/duckduckgo/privacy-dashboard", - "state": { - "branch": null, - "revision": "38336a574e13090764ba09a6b877d15ee514e371", - "version": "3.1.1" - } - }, - { - "package": "Punycode", - "repositoryURL": "https://github.com/gumob/PunycodeSwift.git", - "state": { - "branch": null, - "revision": "4356ec54e073741449640d3d50a1fd24fd1e1b8b", - "version": "2.1.0" - } - }, - { - "package": "swift-argument-parser", - "repositoryURL": "https://github.com/apple/swift-argument-parser", - "state": { - "branch": null, - "revision": "c8ed701b513cf5177118a175d85fbbbcd707ab41", - "version": "1.3.0" - } - }, - { - "package": "Swifter", - "repositoryURL": "https://github.com/httpswift/swifter.git", - "state": { - "branch": null, - "revision": "9483a5d459b45c3ffd059f7b55f9638e268632fd", - "version": "1.5.0" - } - }, - { - "package": "SwiftSoup", - "repositoryURL": "https://github.com/scinfu/SwiftSoup", - "state": { - "branch": null, - "revision": "41e7c263fb8c277e980ebcb9b0b5f6031d3d4886", - "version": "2.4.2" - } - }, - { - "package": "DDGSyncCrypto", - "repositoryURL": "https://github.com/duckduckgo/sync_crypto", - "state": { - "branch": null, - "revision": "2ab6ab6f0f96b259c14c2de3fc948935fc16ac78", - "version": "0.2.0" - } - }, - { - "package": "TrackerRadarKit", - "repositoryURL": "https://github.com/duckduckgo/TrackerRadarKit", - "state": { - "branch": null, - "revision": "a6b7ba151d9dc6684484f3785293875ec01cc1ff", - "version": "1.2.2" - } - }, - { - "package": "WireGuardKit", - "repositoryURL": "https://github.com/duckduckgo/wireguard-apple", - "state": { - "branch": null, - "revision": "2d8172c11478ab11b0f5ad49bdb4f93f4b3d5e0d", - "version": "1.1.1" - } - } - ] - }, - "version": 1 + "pins" : [ + { + "identity" : "bloom_cpp", + "kind" : "remoteSourceControl", + "location" : "https://github.com/duckduckgo/bloom_cpp.git", + "state" : { + "revision" : "8076199456290b61b4544bf2f4caf296759906a0", + "version" : "3.0.0" + } + }, + { + "identity" : "browserserviceskit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", + "state" : { + "branch" : "dominik/sync-feature-flags", + "revision" : "137e833a89cdd853981161d5a3cea0ce70e3c2f0" + } + }, + { + "identity" : "cocoaasyncsocket", + "kind" : "remoteSourceControl", + "location" : "https://github.com/robbiehanson/CocoaAsyncSocket", + "state" : { + "revision" : "dbdc00669c1ced63b27c3c5f052ee4d28f10150c", + "version" : "7.6.5" + } + }, + { + "identity" : "content-scope-scripts", + "kind" : "remoteSourceControl", + "location" : "https://github.com/duckduckgo/content-scope-scripts", + "state" : { + "revision" : "b7ad9843e70cede0c2ca9c4260d970f62cb28156", + "version" : "4.52.0" + } + }, + { + "identity" : "designresourceskit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/duckduckgo/DesignResourcesKit", + "state" : { + "revision" : "d7ea2561ec7624c224f52e1c9b349075ddf1c782", + "version" : "2.0.0" + } + }, + { + "identity" : "duckduckgo-autofill", + "kind" : "remoteSourceControl", + "location" : "https://github.com/duckduckgo/duckduckgo-autofill.git", + "state" : { + "revision" : "5597bc17709c8acf454ecaad4f4082007986242a", + "version" : "10.0.2" + } + }, + { + "identity" : "grdb.swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/duckduckgo/GRDB.swift.git", + "state" : { + "revision" : "77d9a83191a74e319a5cfa27b0e3145d15607573", + "version" : "2.2.0" + } + }, + { + "identity" : "ios-js-support", + "kind" : "remoteSourceControl", + "location" : "https://github.com/duckduckgo/ios-js-support", + "state" : { + "revision" : "6a6789ac8104a587316c58af75539753853b50d9", + "version" : "2.0.0" + } + }, + { + "identity" : "kingfisher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/onevcat/Kingfisher.git", + "state" : { + "revision" : "af4be924ad984cf4d16f4ae4df424e79a443d435", + "version" : "7.6.2" + } + }, + { + "identity" : "lottie-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/duckduckgo/lottie-ios.git", + "state" : { + "revision" : "abf5510e261c85ffddd29de0bca9b72592ea2bdd", + "version" : "3.3.0" + } + }, + { + "identity" : "ohhttpstubs", + "kind" : "remoteSourceControl", + "location" : "https://github.com/AliSoftware/OHHTTPStubs.git", + "state" : { + "revision" : "12f19662426d0434d6c330c6974d53e2eb10ecd9", + "version" : "9.1.0" + } + }, + { + "identity" : "privacy-dashboard", + "kind" : "remoteSourceControl", + "location" : "https://github.com/duckduckgo/privacy-dashboard", + "state" : { + "revision" : "38336a574e13090764ba09a6b877d15ee514e371", + "version" : "3.1.1" + } + }, + { + "identity" : "punycodeswift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/gumob/PunycodeSwift.git", + "state" : { + "revision" : "4356ec54e073741449640d3d50a1fd24fd1e1b8b", + "version" : "2.1.0" + } + }, + { + "identity" : "swift-argument-parser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-argument-parser", + "state" : { + "revision" : "c8ed701b513cf5177118a175d85fbbbcd707ab41", + "version" : "1.3.0" + } + }, + { + "identity" : "swifter", + "kind" : "remoteSourceControl", + "location" : "https://github.com/httpswift/swifter.git", + "state" : { + "revision" : "9483a5d459b45c3ffd059f7b55f9638e268632fd", + "version" : "1.5.0" + } + }, + { + "identity" : "swiftsoup", + "kind" : "remoteSourceControl", + "location" : "https://github.com/scinfu/SwiftSoup", + "state" : { + "revision" : "41e7c263fb8c277e980ebcb9b0b5f6031d3d4886", + "version" : "2.4.2" + } + }, + { + "identity" : "sync_crypto", + "kind" : "remoteSourceControl", + "location" : "https://github.com/duckduckgo/sync_crypto", + "state" : { + "revision" : "2ab6ab6f0f96b259c14c2de3fc948935fc16ac78", + "version" : "0.2.0" + } + }, + { + "identity" : "trackerradarkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/duckduckgo/TrackerRadarKit", + "state" : { + "revision" : "a6b7ba151d9dc6684484f3785293875ec01cc1ff", + "version" : "1.2.2" + } + }, + { + "identity" : "wireguard-apple", + "kind" : "remoteSourceControl", + "location" : "https://github.com/duckduckgo/wireguard-apple", + "state" : { + "revision" : "2d8172c11478ab11b0f5ad49bdb4f93f4b3d5e0d", + "version" : "1.1.1" + } + } + ], + "version" : 2 } diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index a7b87702f1..8057fa74a6 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -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 diff --git a/DuckDuckGo/FaviconsFetcherOnboarding.swift b/DuckDuckGo/FaviconsFetcherOnboarding.swift index ec1418f2a7..857ae52b06 100644 --- a/DuckDuckGo/FaviconsFetcherOnboarding.swift +++ b/DuckDuckGo/FaviconsFetcherOnboarding.swift @@ -60,7 +60,8 @@ final class FaviconsFetcherOnboarding { } private var shouldPresentOnboarding: Bool { - !didPresentFaviconsFetchingOnboarding + syncService.featureFlag.isSyncVisible + && !didPresentFaviconsFetchingOnboarding && !syncBookmarksAdapter.isFaviconsFetchingEnabled && syncBookmarksAdapter.isEligibleForFaviconsFetcherOnboarding } diff --git a/DuckDuckGo/SettingsViewController.swift b/DuckDuckGo/SettingsViewController.swift index 40ddfbdf3e..391c72d015 100644 --- a/DuckDuckGo/SettingsViewController.swift +++ b/DuckDuckGo/SettingsViewController.swift @@ -113,7 +113,7 @@ class SettingsViewController: UITableViewController { } private var shouldShowSyncCell: Bool { - return featureFlagger.isFeatureOn(.sync) + return syncService.featureFlag.isSyncVisible || internalUserDecider.isInternalUser } private var shouldShowTextSizeCell: Bool { From 58f2689a22d71562c2f9b957cc114f6360d033a1 Mon Sep 17 00:00:00 2001 From: Dominik Kapusta Date: Mon, 18 Dec 2023 23:02:06 +0100 Subject: [PATCH 2/8] Implement Sync feature flags --- DuckDuckGo/SyncSettingsViewController.swift | 13 ++ .../ViewModels/SyncSettingsViewModel.swift | 5 + .../SyncUI/Views/Internal/UserText.swift | 6 +- .../SyncUI/Views/SyncSettingsView.swift | 120 +++++++++--------- .../SyncUI/Views/SyncWarningMessageView.swift | 46 +++++++ 5 files changed, 132 insertions(+), 58 deletions(-) create mode 100644 LocalPackages/SyncUI/Sources/SyncUI/Views/SyncWarningMessageView.swift diff --git a/DuckDuckGo/SyncSettingsViewController.swift b/DuckDuckGo/SyncSettingsViewController.swift index 059b28fd26..3c6ed6dfc7 100644 --- a/DuckDuckGo/SyncSettingsViewController.swift +++ b/DuckDuckGo/SyncSettingsViewController.swift @@ -69,6 +69,7 @@ class SyncSettingsViewController: UIHostingController { setUpFaviconsFetcherSwitch(viewModel) setUpFavoritesDisplayModeSwitch(viewModel, appSettings) setUpSyncPaused(viewModel, appSettings) + setUpSyncFeatureFlags(viewModel) refreshForState(syncService.authState) syncService.authStatePublisher @@ -87,6 +88,18 @@ class SyncSettingsViewController: UIHostingController { fatalError("init(coder:) has not been implemented") } + private func setUpSyncFeatureFlags(_ viewModel: SyncSettingsViewModel) { + syncService.featureFlagPublisher.prepend(syncService.featureFlag) + .removeDuplicates() + .sink { featureFlag in + viewModel.isSyncAvailable = featureFlag.isSyncAvailable + viewModel.isConnectingDevicesAvailable = featureFlag.canConnectNewDevice + viewModel.isCreatingAccountAvailable = featureFlag.canCreateAccount + viewModel.isAccountRecoveryAvailable = featureFlag.canRestoreAccount + } + .store(in: &cancellables) + } + private func setUpFaviconsFetcherSwitch(_ viewModel: SyncSettingsViewModel) { viewModel.isFaviconsFetchingEnabled = syncBookmarksAdapter.isFaviconsFetchingEnabled diff --git a/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/SyncSettingsViewModel.swift b/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/SyncSettingsViewModel.swift index c4d11a852f..4d04f56989 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/SyncSettingsViewModel.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/SyncSettingsViewModel.swift @@ -84,6 +84,11 @@ public class SyncSettingsViewModel: ObservableObject { @Published var isBusy = false @Published var recoveryCode = "" + @Published public var isSyncAvailable: Bool = true + @Published public var isConnectingDevicesAvailable: Bool = true + @Published public var isCreatingAccountAvailable: Bool = true + @Published public var isAccountRecoveryAvailable: Bool = true + public weak var delegate: SyncManagementViewModelDelegate? private(set) var isOnDevEnvironment: Bool private(set) var switchToProdEnvironment: () -> Void = {} diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/UserText.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/UserText.swift index f9691a5ff9..591a324fdd 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/UserText.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/UserText.swift @@ -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") @@ -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 } diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsView.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsView.swift index 02e6e171e7..210a128bd9 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsView.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsView.swift @@ -17,8 +17,9 @@ // limitations under the License. // -import SwiftUI import DesignResourcesKit +import DuckUI +import SwiftUI public struct SyncSettingsView: View { @@ -46,6 +47,8 @@ public struct SyncSettingsView: View { if model.isSyncEnabled { + syncUnavailableViewWhileLoggedIn() + turnOffSync() // Sync Paused Errors @@ -66,6 +69,8 @@ public struct SyncSettingsView: View { } else { + syncUnavailableViewWhileLoggedOut() + syncWithAnotherDeviceView() otherOptions() @@ -89,9 +94,21 @@ public struct SyncSettingsView: View { @State var selectedDevice: SyncSettingsViewModel.Device? } -// Sync Set up Views +// MARK: - Sync Set up Views + extension SyncSettingsView { + @ViewBuilder + fileprivate func syncUnavailableViewWhileLoggedOut() -> some View { + if !model.isSyncAvailable || !model.isConnectingDevicesAvailable { + SyncWarningMessageView(title: UserText.serviceUnavailable, message: UserText.warningSyncDisabled) + } else if !model.isCreatingAccountAvailable { + SyncWarningMessageView(title: UserText.serviceUnavailable, message: UserText.warningAccountCreationDisabled) + } else { + EmptyView() + } + } + @ViewBuilder func syncWithAnotherDeviceView() -> some View { Section { @@ -105,20 +122,11 @@ extension SyncSettingsView { .daxBodyRegular() .multilineTextAlignment(.center) .foregroundColor(Color(designSystemColor: .textPrimary)) - Button(action: { - model.scanQRCode() - }, label: { - Text(UserText.syncWithAnotherDeviceButton) - .daxButton() - .foregroundColor(.white) - .frame(maxWidth: 310) - .frame(height: 50) - .background( - RoundedRectangle(cornerRadius: 8) - .fill(Color(designSystemColor: .accent)) - ) - }) - .padding(.vertical, 16) + Button(UserText.syncWithAnotherDeviceButton, action: model.scanQRCode) + .buttonStyle(PrimaryButtonStyle(disabled: !model.isConnectingDevicesAvailable)) + .frame(maxWidth: 310) + .disabled(!model.isConnectingDevicesAvailable) + .padding(.vertical, 16) } Spacer() } @@ -138,37 +146,46 @@ extension SyncSettingsView { @ViewBuilder func otherOptions() -> some View { Section { - Text(UserText.syncAndBackUpThisDeviceLink) - .daxBodyRegular() - .foregroundColor(Color(designSystemColor: .accent)) - .onTapGesture { - isSyncWithSetUpSheetVisible = true - } - .sheet(isPresented: $isSyncWithSetUpSheetVisible, content: { - SyncWithServerView(model: model, onCancel: { - isSyncWithSetUpSheetVisible = false - }) + + Button(UserText.syncAndBackUpThisDeviceLink) { + isSyncWithSetUpSheetVisible = true + } + .sheet(isPresented: $isSyncWithSetUpSheetVisible, content: { + SyncWithServerView(model: model, onCancel: { + isSyncWithSetUpSheetVisible = false }) - Text(UserText.recoverSyncedDataLink) - .daxBodyRegular() - .foregroundColor(Color(designSystemColor: .accent)) - .onTapGesture { - isRecoverSyncedDataSheetVisible = true - } - .sheet(isPresented: $isRecoverSyncedDataSheetVisible, content: { - RecoverSyncedDataView(model: model, onCancel: { - isRecoverSyncedDataSheetVisible = false - }) + }) + .disabled(!model.isCreatingAccountAvailable) + + Button(UserText.recoverSyncedDataLink) { + isRecoverSyncedDataSheetVisible = true + } + .sheet(isPresented: $isRecoverSyncedDataSheetVisible, content: { + RecoverSyncedDataView(model: model, onCancel: { + isRecoverSyncedDataSheetVisible = false }) + }) + .disabled(!model.isCreatingAccountAvailable) + } header: { Text(UserText.otherOptionsSectionHeader) } } } +// MARK: - Sync Enabled Views -// Sync Enabled Views extension SyncSettingsView { + + @ViewBuilder + fileprivate func syncUnavailableViewWhileLoggedIn() -> some View { + if !model.isSyncAvailable { + SyncWarningMessageView(title: UserText.serviceUnavailable, message: UserText.warningSyncDisabled) + } else { + EmptyView() + } + } + @ViewBuilder func deleteAllData() -> some View { Section { @@ -219,12 +236,9 @@ extension SyncSettingsView { .padding() } devicesList() - Button(action: { - model.scanQRCode() - }, label: { - Text(UserText.syncedDevicesSyncWithAnotherDeviceLabel) - .padding(.leading, 32) - }) + Button(UserText.syncedDevicesSyncWithAnotherDeviceLabel, action: model.scanQRCode) + .padding(.leading, 32) + .disabled(!model.isConnectingDevicesAvailable) } header: { Text(UserText.syncedDevicesSectionHeader) } @@ -330,20 +344,12 @@ extension SyncSettingsView { } } - Section { - VStack(alignment: .leading, spacing: 4) { - Text(UserText.syncLimitExceededTitle) - .daxBodyBold() - Text(explanation) - .daxBodyRegular() - } - Button(buttonTitle) { - switch itemType { - case .bookmarks: - model.manageBookmarks() - case .credentials: - model.manageLogins() - } + SyncWarningMessageView(title: UserText.syncLimitExceededTitle, message: explanation, buttonTitle: buttonTitle) { + switch itemType { + case .bookmarks: + model.manageBookmarks() + case .credentials: + model.manageLogins() } } } diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncWarningMessageView.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncWarningMessageView.swift new file mode 100644 index 0000000000..3d0ecd4d02 --- /dev/null +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncWarningMessageView.swift @@ -0,0 +1,46 @@ +// +// SyncWarningMessageView.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI + +struct SyncWarningMessageView: View { + let title: String + let message: String + let buttonTitle: String? + let buttonAction: (() -> Void)? + + init(title: String, message: String, buttonTitle: String? = nil, buttonAction: (() -> Void)? = nil) { + self.title = title + self.message = message + self.buttonTitle = buttonTitle + self.buttonAction = buttonAction + } + + var body: some View { + Section { + VStack(alignment: .leading, spacing: 4) { + Text("⚠️ " + title).daxBodyBold() + Text(message).daxBodyRegular() + } + if let buttonTitle, let buttonAction { + Button(buttonTitle, action: buttonAction) + } + } + } +} From b1d73defe9630b9995568ea4adaae5e6f6f8b0f1 Mon Sep 17 00:00:00 2001 From: Dominik Kapusta Date: Tue, 19 Dec 2023 17:24:06 +0100 Subject: [PATCH 3/8] Fix sync account recovery feature flag --- .../xcshareddata/swiftpm/Package.resolved | 2 +- .../SyncUI/Sources/SyncUI/Views/SyncSettingsView.swift | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 84c0e2460e..6b28ef753a 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -15,7 +15,7 @@ "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { "branch" : "dominik/sync-feature-flags", - "revision" : "137e833a89cdd853981161d5a3cea0ce70e3c2f0" + "revision" : "b781c119800c6e75514cf2131b2e11ab1beb8181" } }, { diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsView.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsView.swift index 210a128bd9..4ded1cc4ad 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsView.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsView.swift @@ -123,9 +123,9 @@ extension SyncSettingsView { .multilineTextAlignment(.center) .foregroundColor(Color(designSystemColor: .textPrimary)) Button(UserText.syncWithAnotherDeviceButton, action: model.scanQRCode) - .buttonStyle(PrimaryButtonStyle(disabled: !model.isConnectingDevicesAvailable)) + .buttonStyle(PrimaryButtonStyle(disabled: !model.isCreatingAccountAvailable)) .frame(maxWidth: 310) - .disabled(!model.isConnectingDevicesAvailable) + .disabled(!model.isCreatingAccountAvailable) .padding(.vertical, 16) } Spacer() @@ -165,7 +165,7 @@ extension SyncSettingsView { isRecoverSyncedDataSheetVisible = false }) }) - .disabled(!model.isCreatingAccountAvailable) + .disabled(!model.isAccountRecoveryAvailable) } header: { Text(UserText.otherOptionsSectionHeader) From 3dcc18666378f9b144789818529a3ed9ca9b5a26 Mon Sep 17 00:00:00 2001 From: Dominik Kapusta Date: Tue, 19 Dec 2023 22:00:41 +0100 Subject: [PATCH 4/8] Refactor SyncFeatureFlags into an OptionSet --- .../xcshareddata/swiftpm/Package.resolved | 2 +- DuckDuckGo/FaviconsFetcherOnboarding.swift | 2 +- DuckDuckGo/SettingsViewController.swift | 2 +- DuckDuckGo/SyncSettingsViewController.swift | 12 ++++++------ .../ViewModels/SyncSettingsViewModel.swift | 4 ++-- .../Sources/SyncUI/Views/SyncSettingsView.swift | 16 ++++++++-------- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 6b28ef753a..a7db8adc52 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -15,7 +15,7 @@ "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { "branch" : "dominik/sync-feature-flags", - "revision" : "b781c119800c6e75514cf2131b2e11ab1beb8181" + "revision" : "92d815d9a21f36c5fecf8df827a42e8b19ff314c" } }, { diff --git a/DuckDuckGo/FaviconsFetcherOnboarding.swift b/DuckDuckGo/FaviconsFetcherOnboarding.swift index 857ae52b06..1f87dd7c82 100644 --- a/DuckDuckGo/FaviconsFetcherOnboarding.swift +++ b/DuckDuckGo/FaviconsFetcherOnboarding.swift @@ -60,7 +60,7 @@ final class FaviconsFetcherOnboarding { } private var shouldPresentOnboarding: Bool { - syncService.featureFlag.isSyncVisible + syncService.featureFlags.contains(.userInterface) && !didPresentFaviconsFetchingOnboarding && !syncBookmarksAdapter.isFaviconsFetchingEnabled && syncBookmarksAdapter.isEligibleForFaviconsFetcherOnboarding diff --git a/DuckDuckGo/SettingsViewController.swift b/DuckDuckGo/SettingsViewController.swift index 391c72d015..81b82048a0 100644 --- a/DuckDuckGo/SettingsViewController.swift +++ b/DuckDuckGo/SettingsViewController.swift @@ -113,7 +113,7 @@ class SettingsViewController: UITableViewController { } private var shouldShowSyncCell: Bool { - return syncService.featureFlag.isSyncVisible || internalUserDecider.isInternalUser + return syncService.featureFlags.contains(.userInterface) || internalUserDecider.isInternalUser } private var shouldShowTextSizeCell: Bool { diff --git a/DuckDuckGo/SyncSettingsViewController.swift b/DuckDuckGo/SyncSettingsViewController.swift index 3c6ed6dfc7..6b57e836cd 100644 --- a/DuckDuckGo/SyncSettingsViewController.swift +++ b/DuckDuckGo/SyncSettingsViewController.swift @@ -89,13 +89,13 @@ class SyncSettingsViewController: UIHostingController { } private func setUpSyncFeatureFlags(_ viewModel: SyncSettingsViewModel) { - syncService.featureFlagPublisher.prepend(syncService.featureFlag) + syncService.featureFlagsPublisher.prepend(syncService.featureFlags) .removeDuplicates() - .sink { featureFlag in - viewModel.isSyncAvailable = featureFlag.isSyncAvailable - viewModel.isConnectingDevicesAvailable = featureFlag.canConnectNewDevice - viewModel.isCreatingAccountAvailable = featureFlag.canCreateAccount - viewModel.isAccountRecoveryAvailable = featureFlag.canRestoreAccount + .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) } diff --git a/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/SyncSettingsViewModel.swift b/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/SyncSettingsViewModel.swift index 4d04f56989..6934aaae01 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/SyncSettingsViewModel.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/SyncSettingsViewModel.swift @@ -84,9 +84,9 @@ public class SyncSettingsViewModel: ObservableObject { @Published var isBusy = false @Published var recoveryCode = "" - @Published public var isSyncAvailable: Bool = true + @Published public var isDataSyncingAvailable: Bool = true @Published public var isConnectingDevicesAvailable: Bool = true - @Published public var isCreatingAccountAvailable: Bool = true + @Published public var isAccountCreationAvailable: Bool = true @Published public var isAccountRecoveryAvailable: Bool = true public weak var delegate: SyncManagementViewModelDelegate? diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsView.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsView.swift index 4ded1cc4ad..d796e39d20 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsView.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsView.swift @@ -100,9 +100,9 @@ extension SyncSettingsView { @ViewBuilder fileprivate func syncUnavailableViewWhileLoggedOut() -> some View { - if !model.isSyncAvailable || !model.isConnectingDevicesAvailable { + if !model.isDataSyncingAvailable || !model.isConnectingDevicesAvailable { SyncWarningMessageView(title: UserText.serviceUnavailable, message: UserText.warningSyncDisabled) - } else if !model.isCreatingAccountAvailable { + } else if !model.isAccountCreationAvailable { SyncWarningMessageView(title: UserText.serviceUnavailable, message: UserText.warningAccountCreationDisabled) } else { EmptyView() @@ -123,9 +123,9 @@ extension SyncSettingsView { .multilineTextAlignment(.center) .foregroundColor(Color(designSystemColor: .textPrimary)) Button(UserText.syncWithAnotherDeviceButton, action: model.scanQRCode) - .buttonStyle(PrimaryButtonStyle(disabled: !model.isCreatingAccountAvailable)) + .buttonStyle(PrimaryButtonStyle(disabled: !model.isAccountCreationAvailable)) .frame(maxWidth: 310) - .disabled(!model.isCreatingAccountAvailable) + .disabled(!model.isAccountCreationAvailable) .padding(.vertical, 16) } Spacer() @@ -155,7 +155,7 @@ extension SyncSettingsView { isSyncWithSetUpSheetVisible = false }) }) - .disabled(!model.isCreatingAccountAvailable) + .disabled(!model.isAccountCreationAvailable) Button(UserText.recoverSyncedDataLink) { isRecoverSyncedDataSheetVisible = true @@ -179,10 +179,10 @@ extension SyncSettingsView { @ViewBuilder fileprivate func syncUnavailableViewWhileLoggedIn() -> some View { - if !model.isSyncAvailable { - SyncWarningMessageView(title: UserText.serviceUnavailable, message: UserText.warningSyncDisabled) - } else { + if model.isDataSyncingAvailable { EmptyView() + } else { + SyncWarningMessageView(title: UserText.serviceUnavailable, message: UserText.warningSyncDisabled) } } From 323be03ab9a38d69025b5aba3990cfe980c708d2 Mon Sep 17 00:00:00 2001 From: Dominik Kapusta Date: Tue, 19 Dec 2023 23:57:57 +0100 Subject: [PATCH 5/8] Update BSK ref --- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index a7db8adc52..9e5d38f5a6 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -15,7 +15,7 @@ "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { "branch" : "dominik/sync-feature-flags", - "revision" : "92d815d9a21f36c5fecf8df827a42e8b19ff314c" + "revision" : "fb5df7cb678c6817caa1cae5ab63bf725410b31b" } }, { From 9264f8275e96ae0b0750eed4575e4552efdc257e Mon Sep 17 00:00:00 2001 From: Dominik Kapusta Date: Wed, 20 Dec 2023 00:09:06 +0100 Subject: [PATCH 6/8] Update embedded Privacy Config --- .../AppPrivacyConfigurationDataProvider.swift | 4 +- Core/ios-config.json | 73 ++++++++++++++++--- 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/Core/AppPrivacyConfigurationDataProvider.swift b/Core/AppPrivacyConfigurationDataProvider.swift index ff311a2fa2..88beeca5eb 100644 --- a/Core/AppPrivacyConfigurationDataProvider.swift +++ b/Core/AppPrivacyConfigurationDataProvider.swift @@ -23,8 +23,8 @@ import BrowserServicesKit final public class AppPrivacyConfigurationDataProvider: EmbeddedDataProvider { public struct Constants { - public static let embeddedDataETag = "\"f4e8436ab9977e1a8a9d6ee600fc353e\"" - public static let embeddedDataSHA = "86b3a7bece52da74f7d267c2b522ac929d363a384cacc013f2b2d057ee1e386c" + public static let embeddedDataETag = "\"388dd0526e94f80473728c0bfbb48b39\"" + public static let embeddedDataSHA = "f7b9ae8860ff84f33e602b40d0938776d2d9327115b4ddfe09fc0fa09b5e1ff1" } public var embeddedDataEtag: String { diff --git a/Core/ios-config.json b/Core/ios-config.json index 7f2a82608c..2afc6d62c5 100644 --- a/Core/ios-config.json +++ b/Core/ios-config.json @@ -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", @@ -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" } ] }, @@ -2977,6 +2980,15 @@ } ] }, + { + "domain": "orange.fr", + "rules": [ + { + "selector": ".tag-rm", + "type": "hide-empty" + } + ] + }, { "domain": "ouest-france.fr", "rules": [ @@ -3788,7 +3800,7 @@ ] }, "state": "enabled", - "hash": "81d363bc5cd7d76de1aa11d5b0f8e27d" + "hash": "182ef21a9dcfd3a160468f851c4b1789" }, "exceptionHandler": { "exceptions": [ @@ -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": { @@ -4764,6 +4795,12 @@ "wxii12.com", "wyff4.com" ] + }, + { + "rule": "z-na.amazon-adsystem.com/widgets/onejs", + "domains": [ + "oceanofcompressed.xyz" + ] } ] }, @@ -5522,8 +5559,7 @@ { "rule": "app.five9.com", "domains": [ - "gmsdnv.com", - "machiassavings.bank" + "" ] } ] @@ -6058,7 +6094,13 @@ { "rule": "api.hubspot.com/livechat-public/v1/message/public", "domains": [ - "pippintitle.com" + "" + ] + }, + { + "rule": "js.hubspot.com/web-interactives-embed.js", + "domains": [ + "" ] }, { @@ -6367,6 +6409,16 @@ } ] }, + "media.net": { + "rules": [ + { + "rule": "contextual.media.net/dmedianet.js", + "domains": [ + "oceanofcompressed.xyz" + ] + } + ] + }, "mediavine.com": { "rules": [ { @@ -6825,7 +6877,8 @@ { "rule": "secure.quantserve.com/quant.js", "domains": [ - "aternos.org" + "aternos.org", + "oceanofcompressed.xyz" ] } ] @@ -7510,7 +7563,7 @@ "domain": "sundancecatalog.com" } ], - "hash": "163c9ec4fc3bdb9dbfc75e70839a31d7" + "hash": "c1968268cb8a82bf532443edd17d9499" }, "trackingCookies1p": { "settings": { From a075d95de37d886c4583d64a04e451f924174387 Mon Sep 17 00:00:00 2001 From: Dominik Kapusta Date: Wed, 20 Dec 2023 09:28:21 +0100 Subject: [PATCH 7/8] Receive feature flags on main queue --- DuckDuckGo/SyncSettingsViewController.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/DuckDuckGo/SyncSettingsViewController.swift b/DuckDuckGo/SyncSettingsViewController.swift index 6b57e836cd..d5ecc2569f 100644 --- a/DuckDuckGo/SyncSettingsViewController.swift +++ b/DuckDuckGo/SyncSettingsViewController.swift @@ -91,6 +91,7 @@ class SyncSettingsViewController: UIHostingController { 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) From f959dbbee25981d4e46798988531b73b312ed0fb Mon Sep 17 00:00:00 2001 From: Dominik Kapusta Date: Wed, 20 Dec 2023 09:45:56 +0100 Subject: [PATCH 8/8] Update BSK ref --- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index b7efe3a643..eb2b21b0e6 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -15,7 +15,7 @@ "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { "branch" : "dominik/sync-feature-flags", - "revision" : "155125853e643abd80676907b410828280e9b6df" + "revision" : "1c8fa640dd55a9a4d4d4acc433c96cb226448eea" } }, {