From 83fa6ee3f3b7be6dd400875aad728fcb2667e16b Mon Sep 17 00:00:00 2001 From: Anh Do Date: Mon, 6 Jan 2025 15:07:48 +0700 Subject: [PATCH 1/4] Remove support for usesUnifiedFeedback subfeature flag --- Core/ios-config.json | 4 ---- DuckDuckGo/NetworkProtectionRootView.swift | 3 +-- DuckDuckGo/NetworkProtectionStatusView.swift | 2 +- DuckDuckGo/NetworkProtectionStatusViewModel.swift | 6 +++--- DuckDuckGo/SettingsOthersView.swift | 2 +- DuckDuckGo/SettingsViewModel.swift | 4 ++-- .../ViewModel/SubscriptionSettingsViewModel.swift | 4 ++-- .../Subscription/Views/SubscriptionSettingsView.swift | 2 +- DuckDuckGoTests/NetworkProtectionStatusViewModelTests.swift | 2 +- .../Subscription/SubscriptionFeatureAvailabilityMock.swift | 6 ++---- .../SubscriptionPagesUseSubscriptionFeatureTests.swift | 3 +-- 11 files changed, 15 insertions(+), 23 deletions(-) diff --git a/Core/ios-config.json b/Core/ios-config.json index 370e342505..02136fb376 100644 --- a/Core/ios-config.json +++ b/Core/ios-config.json @@ -6161,10 +6161,6 @@ "allowPurchase": { "state": "enabled" }, - "useUnifiedFeedback": { - "state": "enabled", - "minSupportedVersion": "7.136.0" - }, "setAccessTokenCookieForSubscriptionDomains": { "state": "disabled", "rollout": { diff --git a/DuckDuckGo/NetworkProtectionRootView.swift b/DuckDuckGo/NetworkProtectionRootView.swift index 357e302c3d..9538ec954c 100644 --- a/DuckDuckGo/NetworkProtectionRootView.swift +++ b/DuckDuckGo/NetworkProtectionRootView.swift @@ -30,13 +30,12 @@ struct NetworkProtectionRootView: View { let subscriptionManager = AppDependencyProvider.shared.subscriptionManager let accountManager = AppDependencyProvider.shared.subscriptionManager.accountManager let locationListRepository = NetworkProtectionLocationListCompositeRepository(accountManager: accountManager) - let usesUnifiedFeedbackForm = accountManager.isUserAuthenticated statusViewModel = NetworkProtectionStatusViewModel(tunnelController: AppDependencyProvider.shared.networkProtectionTunnelController, settings: AppDependencyProvider.shared.vpnSettings, statusObserver: AppDependencyProvider.shared.connectionObserver, serverInfoObserver: AppDependencyProvider.shared.serverInfoObserver, locationListRepository: locationListRepository, - usesUnifiedFeedbackForm: usesUnifiedFeedbackForm, + enablesUnifiedFeedbackForm: accountManager.isUserAuthenticated, subscriptionManager: subscriptionManager) } diff --git a/DuckDuckGo/NetworkProtectionStatusView.swift b/DuckDuckGo/NetworkProtectionStatusView.swift index 24dd47e31c..af376f6611 100644 --- a/DuckDuckGo/NetworkProtectionStatusView.swift +++ b/DuckDuckGo/NetworkProtectionStatusView.swift @@ -293,7 +293,7 @@ struct NetworkProtectionStatusView: View { .daxBodyRegular() .foregroundColor(.init(designSystemColor: .textPrimary)) - if statusModel.usesUnifiedFeedbackForm { + if statusModel.enablesUnifiedFeedbackForm { NavigationLink(UserText.subscriptionFeedback, destination: UnifiedFeedbackRootView(viewModel: viewModel)) .daxBodyRegular() .foregroundColor(.init(designSystemColor: .textPrimary)) diff --git a/DuckDuckGo/NetworkProtectionStatusViewModel.swift b/DuckDuckGo/NetworkProtectionStatusViewModel.swift index 3d6ef4f9fa..909c553105 100644 --- a/DuckDuckGo/NetworkProtectionStatusViewModel.swift +++ b/DuckDuckGo/NetworkProtectionStatusViewModel.swift @@ -177,7 +177,7 @@ final class NetworkProtectionStatusViewModel: ObservableObject { @Published public var animationsOn: Bool = false - public let usesUnifiedFeedbackForm: Bool + public let enablesUnifiedFeedbackForm: Bool public let subscriptionManager: SubscriptionManager public init(tunnelController: (TunnelController & TunnelSessionProvider), @@ -186,14 +186,14 @@ final class NetworkProtectionStatusViewModel: ObservableObject { serverInfoObserver: ConnectionServerInfoObserver, errorObserver: ConnectionErrorObserver = ConnectionErrorObserverThroughSession(), locationListRepository: NetworkProtectionLocationListRepository, - usesUnifiedFeedbackForm: Bool, + enablesUnifiedFeedbackForm: Bool, subscriptionManager: SubscriptionManager) { self.tunnelController = tunnelController self.settings = settings self.statusObserver = statusObserver self.serverInfoObserver = serverInfoObserver self.errorObserver = errorObserver - self.usesUnifiedFeedbackForm = usesUnifiedFeedbackForm + self.enablesUnifiedFeedbackForm = enablesUnifiedFeedbackForm self.subscriptionManager = subscriptionManager statusMessage = Self.message(for: statusObserver.recentValue) diff --git a/DuckDuckGo/SettingsOthersView.swift b/DuckDuckGo/SettingsOthersView.swift index b3818cf4b3..42768bb6d4 100644 --- a/DuckDuckGo/SettingsOthersView.swift +++ b/DuckDuckGo/SettingsOthersView.swift @@ -34,7 +34,7 @@ struct SettingsOthersView: View { } // Share Feedback - if viewModel.usesUnifiedFeedbackForm { + if viewModel.enablesUnifiedFeedbackForm { let formViewModel = UnifiedFeedbackFormViewModel(subscriptionManager: viewModel.subscriptionManager, apiService: DefaultAPIService(), vpnMetadataCollector: DefaultVPNMetadataCollector(), diff --git a/DuckDuckGo/SettingsViewModel.swift b/DuckDuckGo/SettingsViewModel.swift index 224f880c2c..691a161896 100644 --- a/DuckDuckGo/SettingsViewModel.swift +++ b/DuckDuckGo/SettingsViewModel.swift @@ -400,8 +400,8 @@ final class SettingsViewModel: ObservableObject { legacyViewProvider.syncService.authState != .inactive ? .on : .off } - var usesUnifiedFeedbackForm: Bool { - subscriptionManager.accountManager.isUserAuthenticated && subscriptionFeatureAvailability.usesUnifiedFeedbackForm + var enablesUnifiedFeedbackForm: Bool { + subscriptionManager.accountManager.isUserAuthenticated } // MARK: Default Init diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift index a8a3c3a875..a316744edc 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift @@ -67,14 +67,14 @@ final class SubscriptionSettingsViewModel: ObservableObject { // Read only View State - Should only be modified from the VM @Published private(set) var state: State - public let usesUnifiedFeedbackForm: Bool + public let enablesUnifiedFeedbackForm: Bool init(subscriptionManager: SubscriptionManager = AppDependencyProvider.shared.subscriptionManager) { self.subscriptionManager = subscriptionManager let subscriptionFAQURL = subscriptionManager.url(for: .faq) let learnMoreURL = subscriptionFAQURL.appendingPathComponent("adding-email") self.state = State(faqURL: subscriptionFAQURL, learnMoreURL: learnMoreURL) - self.usesUnifiedFeedbackForm = subscriptionManager.accountManager.isUserAuthenticated + self.enablesUnifiedFeedbackForm = subscriptionManager.accountManager.isUserAuthenticated setupNotificationObservers() } diff --git a/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift b/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift index 70d0639948..60f93f5cf2 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift @@ -201,7 +201,7 @@ struct SubscriptionSettingsView: View { } @ViewBuilder var helpSection: some View { - if viewModel.usesUnifiedFeedbackForm { + if viewModel.enablesUnifiedFeedbackForm { Section { faqButton supportButton diff --git a/DuckDuckGoTests/NetworkProtectionStatusViewModelTests.swift b/DuckDuckGoTests/NetworkProtectionStatusViewModelTests.swift index 231ad293f4..952f4f9dd1 100644 --- a/DuckDuckGoTests/NetworkProtectionStatusViewModelTests.swift +++ b/DuckDuckGoTests/NetworkProtectionStatusViewModelTests.swift @@ -54,7 +54,7 @@ final class NetworkProtectionStatusViewModelTests: XCTestCase { statusObserver: statusObserver, serverInfoObserver: serverInfoObserver, locationListRepository: MockNetworkProtectionLocationListRepository(), - usesUnifiedFeedbackForm: false, + enablesUnifiedFeedbackForm: false, subscriptionManager: subscriptionManager) } diff --git a/DuckDuckGoTests/Subscription/SubscriptionFeatureAvailabilityMock.swift b/DuckDuckGoTests/Subscription/SubscriptionFeatureAvailabilityMock.swift index 055df663ca..05ea302a97 100644 --- a/DuckDuckGoTests/Subscription/SubscriptionFeatureAvailabilityMock.swift +++ b/DuckDuckGoTests/Subscription/SubscriptionFeatureAvailabilityMock.swift @@ -22,17 +22,15 @@ import Foundation public final class SubscriptionFeatureAvailabilityMock: SubscriptionFeatureAvailability { static var enabled: SubscriptionFeatureAvailabilityMock { - return SubscriptionFeatureAvailabilityMock(isFeatureAvailable: true, isSubscriptionPurchaseAllowed: true, usesUnifiedFeedbackForm: true) + return SubscriptionFeatureAvailabilityMock(isFeatureAvailable: true, isSubscriptionPurchaseAllowed: true) } public var isFeatureAvailable: Bool public var isSubscriptionPurchaseAllowed: Bool - public var usesUnifiedFeedbackForm: Bool - public init(isFeatureAvailable: Bool, isSubscriptionPurchaseAllowed: Bool, usesUnifiedFeedbackForm: Bool) { + public init(isFeatureAvailable: Bool, isSubscriptionPurchaseAllowed: Bool) { self.isFeatureAvailable = isFeatureAvailable self.isSubscriptionPurchaseAllowed = isSubscriptionPurchaseAllowed - self.usesUnifiedFeedbackForm = usesUnifiedFeedbackForm } } diff --git a/DuckDuckGoTests/Subscription/SubscriptionPagesUseSubscriptionFeatureTests.swift b/DuckDuckGoTests/Subscription/SubscriptionPagesUseSubscriptionFeatureTests.swift index b350fbd2c6..e67112c147 100644 --- a/DuckDuckGoTests/Subscription/SubscriptionPagesUseSubscriptionFeatureTests.swift +++ b/DuckDuckGoTests/Subscription/SubscriptionPagesUseSubscriptionFeatureTests.swift @@ -319,8 +319,7 @@ final class SubscriptionPagesUseSubscriptionFeatureTests: XCTestCase { // Given let subscriptionFeatureAvailabilityWithoutPurchaseAllowed = SubscriptionFeatureAvailabilityMock( isFeatureAvailable: true, - isSubscriptionPurchaseAllowed: false, - usesUnifiedFeedbackForm: true + isSubscriptionPurchaseAllowed: false ) feature = SubscriptionPagesUseSubscriptionFeature(subscriptionManager: subscriptionManager, From bc729d6366f4a8e4accffb9ba6a363e61ef506a4 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Mon, 6 Jan 2025 15:13:17 +0700 Subject: [PATCH 2/4] Simplify logic --- DuckDuckGo/NetworkProtectionRootView.swift | 1 - DuckDuckGo/NetworkProtectionStatusViewModel.swift | 7 ++++--- .../NetworkProtectionStatusViewModelTests.swift | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/DuckDuckGo/NetworkProtectionRootView.swift b/DuckDuckGo/NetworkProtectionRootView.swift index 9538ec954c..f69d30d6a2 100644 --- a/DuckDuckGo/NetworkProtectionRootView.swift +++ b/DuckDuckGo/NetworkProtectionRootView.swift @@ -35,7 +35,6 @@ struct NetworkProtectionRootView: View { statusObserver: AppDependencyProvider.shared.connectionObserver, serverInfoObserver: AppDependencyProvider.shared.serverInfoObserver, locationListRepository: locationListRepository, - enablesUnifiedFeedbackForm: accountManager.isUserAuthenticated, subscriptionManager: subscriptionManager) } diff --git a/DuckDuckGo/NetworkProtectionStatusViewModel.swift b/DuckDuckGo/NetworkProtectionStatusViewModel.swift index 909c553105..49854d654a 100644 --- a/DuckDuckGo/NetworkProtectionStatusViewModel.swift +++ b/DuckDuckGo/NetworkProtectionStatusViewModel.swift @@ -177,7 +177,10 @@ final class NetworkProtectionStatusViewModel: ObservableObject { @Published public var animationsOn: Bool = false - public let enablesUnifiedFeedbackForm: Bool + public var enablesUnifiedFeedbackForm: Bool { + subscriptionManager.accountManager.isUserAuthenticated + } + public let subscriptionManager: SubscriptionManager public init(tunnelController: (TunnelController & TunnelSessionProvider), @@ -186,14 +189,12 @@ final class NetworkProtectionStatusViewModel: ObservableObject { serverInfoObserver: ConnectionServerInfoObserver, errorObserver: ConnectionErrorObserver = ConnectionErrorObserverThroughSession(), locationListRepository: NetworkProtectionLocationListRepository, - enablesUnifiedFeedbackForm: Bool, subscriptionManager: SubscriptionManager) { self.tunnelController = tunnelController self.settings = settings self.statusObserver = statusObserver self.serverInfoObserver = serverInfoObserver self.errorObserver = errorObserver - self.enablesUnifiedFeedbackForm = enablesUnifiedFeedbackForm self.subscriptionManager = subscriptionManager statusMessage = Self.message(for: statusObserver.recentValue) diff --git a/DuckDuckGoTests/NetworkProtectionStatusViewModelTests.swift b/DuckDuckGoTests/NetworkProtectionStatusViewModelTests.swift index 952f4f9dd1..906b2f4539 100644 --- a/DuckDuckGoTests/NetworkProtectionStatusViewModelTests.swift +++ b/DuckDuckGoTests/NetworkProtectionStatusViewModelTests.swift @@ -54,7 +54,6 @@ final class NetworkProtectionStatusViewModelTests: XCTestCase { statusObserver: statusObserver, serverInfoObserver: serverInfoObserver, locationListRepository: MockNetworkProtectionLocationListRepository(), - enablesUnifiedFeedbackForm: false, subscriptionManager: subscriptionManager) } From 9b88ef33255ab94b6bf42fa8a01b88093790b953 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Mon, 6 Jan 2025 15:36:52 +0700 Subject: [PATCH 3/4] Remove VPNFeedbackFormView --- DuckDuckGo.xcodeproj/project.pbxproj | 26 +- DuckDuckGo/Feedback/VPNFeedbackCategory.swift | 42 ---- DuckDuckGo/Feedback/VPNFeedbackFormView.swift | 231 ------------------ .../Feedback/VPNFeedbackFormViewModel.swift | 90 ------- DuckDuckGo/Feedback/VPNFeedbackSender.swift | 47 ---- DuckDuckGo/NetworkProtectionStatusView.swift | 4 - .../Feedback/VPNMetadataCollector.swift | 0 7 files changed, 1 insertion(+), 439 deletions(-) delete mode 100644 DuckDuckGo/Feedback/VPNFeedbackCategory.swift delete mode 100644 DuckDuckGo/Feedback/VPNFeedbackFormView.swift delete mode 100644 DuckDuckGo/Feedback/VPNFeedbackFormViewModel.swift delete mode 100644 DuckDuckGo/Feedback/VPNFeedbackSender.swift rename DuckDuckGo/{ => Subscription}/Feedback/VPNMetadataCollector.swift (100%) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index fc091546fe..652b7c3496 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -868,11 +868,7 @@ BD10B8AA2C7629740033115D /* Logger+Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD10B8A92C7629740033115D /* Logger+Subscription.swift */; }; BD15DB852B959CFD00821457 /* BundleExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD15DB842B959CFD00821457 /* BundleExtension.swift */; }; BD2F39EB2C19F955005B19E7 /* NetworkProtectionDNSSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD2F39EA2C19F955005B19E7 /* NetworkProtectionDNSSettingsView.swift */; }; - BD862E032B30DA170073E2EE /* VPNFeedbackFormViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD862E022B30DA170073E2EE /* VPNFeedbackFormViewModel.swift */; }; - BD862E052B30DB250073E2EE /* VPNFeedbackCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD862E042B30DB250073E2EE /* VPNFeedbackCategory.swift */; }; - BD862E072B30F5E30073E2EE /* VPNFeedbackSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD862E062B30F5E30073E2EE /* VPNFeedbackSender.swift */; }; BD862E092B30F63E0073E2EE /* VPNMetadataCollector.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD862E082B30F63E0073E2EE /* VPNMetadataCollector.swift */; }; - BD862E0B2B30F9300073E2EE /* VPNFeedbackFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD862E0A2B30F9300073E2EE /* VPNFeedbackFormView.swift */; }; BDC234F72B27F51100D3C798 /* UniquePixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDC234F62B27F51100D3C798 /* UniquePixel.swift */; }; BDD3B3552B8EF8DB005857A8 /* NetworkProtectionUNNotificationPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE3766DD2AC5945500AAB575 /* NetworkProtectionUNNotificationPresenter.swift */; }; BDE219E62C406D19005D5884 /* PrivacyProDataReporting.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE219E52C406D19005D5884 /* PrivacyProDataReporting.swift */; }; @@ -2732,11 +2728,7 @@ BD10B8A92C7629740033115D /* Logger+Subscription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Logger+Subscription.swift"; sourceTree = ""; }; BD15DB842B959CFD00821457 /* BundleExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundleExtension.swift; sourceTree = ""; }; BD2F39EA2C19F955005B19E7 /* NetworkProtectionDNSSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionDNSSettingsView.swift; sourceTree = ""; }; - BD862E022B30DA170073E2EE /* VPNFeedbackFormViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNFeedbackFormViewModel.swift; sourceTree = ""; }; - BD862E042B30DB250073E2EE /* VPNFeedbackCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNFeedbackCategory.swift; sourceTree = ""; }; - BD862E062B30F5E30073E2EE /* VPNFeedbackSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNFeedbackSender.swift; sourceTree = ""; }; BD862E082B30F63E0073E2EE /* VPNMetadataCollector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNMetadataCollector.swift; sourceTree = ""; }; - BD862E0A2B30F9300073E2EE /* VPNFeedbackFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNFeedbackFormView.swift; sourceTree = ""; }; BDC234F62B27F51100D3C798 /* UniquePixel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UniquePixel.swift; sourceTree = ""; }; BDE219E52C406D19005D5884 /* PrivacyProDataReporting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyProDataReporting.swift; sourceTree = ""; }; BDE219E92C457B46005D5884 /* PrivacyProDataReporterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrivacyProDataReporterTests.swift; sourceTree = ""; }; @@ -5294,18 +5286,6 @@ name = ContentBlocking; sourceTree = ""; }; - BD862E012B30D9FB0073E2EE /* Feedback */ = { - isa = PBXGroup; - children = ( - BD862E022B30DA170073E2EE /* VPNFeedbackFormViewModel.swift */, - BD862E042B30DB250073E2EE /* VPNFeedbackCategory.swift */, - BD862E062B30F5E30073E2EE /* VPNFeedbackSender.swift */, - BD862E082B30F63E0073E2EE /* VPNMetadataCollector.swift */, - BD862E0A2B30F9300073E2EE /* VPNFeedbackFormView.swift */, - ); - path = Feedback; - sourceTree = ""; - }; BDE91CD42C6292BF0005CB74 /* Feedback */ = { isa = PBXGroup; children = ( @@ -5313,6 +5293,7 @@ BDE91CD72C629A910005CB74 /* UnifiedFeedbackSender.swift */, BDE91CD92C62A70B0005CB74 /* UnifiedMetadataCollector.swift */, BDE91CDB2C62AA3A0005CB74 /* DefaultMetadataCollector.swift */, + BD862E082B30F63E0073E2EE /* VPNMetadataCollector.swift */, BDE91CDD2C62B90F0005CB74 /* UnifiedFeedbackRootView.swift */, BDE91CDF2C6515410005CB74 /* UnifiedFeedbackFormViewModel.swift */, ); @@ -5967,7 +5948,6 @@ EE458D122ABB651500FC651A /* Debug */, EE0153E22A6FE031002A8B26 /* Root */, EE0153DF2A6EABAF002A8B26 /* Helpers */, - BD862E012B30D9FB0073E2EE /* Feedback */, EECD94B32A28B96C0085C66E /* Status */, 4B5C46282AF2A6DB002A4432 /* Intents */, 4B274F5E2AFEAEB3003F0745 /* Widget */, @@ -7940,7 +7920,6 @@ 859DB8152CE6263C001F7210 /* TextZoomEditorModel.swift in Sources */, D668D9292B69681C008E2FF2 /* IdentityTheftRestorationPagesUserScript.swift in Sources */, D64648AD2B59936B0033090B /* SubscriptionEmailView.swift in Sources */, - BD862E032B30DA170073E2EE /* VPNFeedbackFormViewModel.swift in Sources */, F4147354283BF834004AA7A5 /* AutofillContentScopeFeatureToggles.swift in Sources */, 6FE1273A2C204BD000EB5724 /* NewTabPageView.swift in Sources */, 986DA94A24884B18004A7E39 /* WebViewTransition.swift in Sources */, @@ -8086,7 +8065,6 @@ 9F7CFF762C86BB8F0012833E /* OnboardingView+AppIconPickerContent.swift in Sources */, D68A21442B7EC08500BB372E /* SubscriptionExternalLinkView.swift in Sources */, BDD3B3552B8EF8DB005857A8 /* NetworkProtectionUNNotificationPresenter.swift in Sources */, - BD862E0B2B30F9300073E2EE /* VPNFeedbackFormView.swift in Sources */, 850365F323DE087800D0F787 /* UIImageViewExtension.swift in Sources */, 56D060262C359D2E003BAEB5 /* ContextualOnboardingDialogs.swift in Sources */, 9F8E0F382CCFAA8A001EA7C5 /* AddToDockPromoView.swift in Sources */, @@ -8128,7 +8106,6 @@ 8598D2E22CEB98B500C45685 /* FaviconRequestModifier.swift in Sources */, 8598D2E32CEB98B500C45685 /* FaviconUserScript.swift in Sources */, 8598D2E42CEB98B500C45685 /* FaviconSourcesProvider.swift in Sources */, - BD862E052B30DB250073E2EE /* VPNFeedbackCategory.swift in Sources */, 317DF60B2D01E7D600DE0145 /* RoundedPageSheetPresentationAnimator.swift in Sources */, 85AE6690209724120014CF04 /* NotificationView.swift in Sources */, BDE91CE02C6515420005CB74 /* UnifiedFeedbackFormViewModel.swift in Sources */, @@ -8214,7 +8191,6 @@ 6F655BE22BAB289E00AC3597 /* DefaultTheme.swift in Sources */, 6FE1274B2C20943500EB5724 /* ShortcutItemView.swift in Sources */, 7B4F87EA2D0738F90010B18F /* SiriEducationView.swift in Sources */, - BD862E072B30F5E30073E2EE /* VPNFeedbackSender.swift in Sources */, AA4D6A6A23DB87B1007E8790 /* AppIconManager.swift in Sources */, 8563A03C1F9288D600F04442 /* BrowserChromeManager.swift in Sources */, 317DF6082D01E7B900DE0145 /* RoundedPageSheetContainerViewController.swift in Sources */, diff --git a/DuckDuckGo/Feedback/VPNFeedbackCategory.swift b/DuckDuckGo/Feedback/VPNFeedbackCategory.swift deleted file mode 100644 index 4c478c662d..0000000000 --- a/DuckDuckGo/Feedback/VPNFeedbackCategory.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// VPNFeedbackCategory.swift -// DuckDuckGo -// -// Copyright © 2023 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -enum VPNFeedbackCategory: String, CaseIterable { - case unableToInstall - case failsToConnect - case tooSlow - case issueWithAppOrWebsite - case cantConnectToLocalDevice - case appCrashesOrFreezes - case featureRequest - case somethingElse - - var displayName: String { - switch self { - case .unableToInstall: return UserText.vpnFeedbackFormCategoryUnableToInstall - case .failsToConnect: return UserText.vpnFeedbackFormCategoryFailsToConnect - case .tooSlow: return UserText.vpnFeedbackFormCategoryTooSlow - case .issueWithAppOrWebsite: return UserText.vpnFeedbackFormCategoryIssuesWithApps - case .cantConnectToLocalDevice: return UserText.vpnFeedbackFormCategoryLocalDeviceConnectivity - case .appCrashesOrFreezes: return UserText.vpnFeedbackFormCategoryBrowserCrashOrFreeze - case .featureRequest: return UserText.vpnFeedbackFormCategoryFeatureRequest - case .somethingElse: return UserText.vpnFeedbackFormCategoryOther - } - } -} diff --git a/DuckDuckGo/Feedback/VPNFeedbackFormView.swift b/DuckDuckGo/Feedback/VPNFeedbackFormView.swift deleted file mode 100644 index 2a4469cdc6..0000000000 --- a/DuckDuckGo/Feedback/VPNFeedbackFormView.swift +++ /dev/null @@ -1,231 +0,0 @@ -// -// VPNFeedbackFormView.swift -// DuckDuckGo -// -// Copyright © 2023 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import SwiftUI -import NetworkProtection - -struct VPNFeedbackFormCategoryView: View { - @Environment(\.dismiss) private var dismiss - let collector = DefaultVPNMetadataCollector( - statusObserver: AppDependencyProvider.shared.connectionObserver, - serverInfoObserver: AppDependencyProvider.shared.serverInfoObserver - ) - - var body: some View { - VStack { - List { - Section { - ForEach(VPNFeedbackCategory.allCases, id: \.self) { category in - NavigationLink { - VPNFeedbackFormView(viewModel: VPNFeedbackFormViewModel(metadataCollector: collector, category: category)) { - dismiss() - DispatchQueue.main.async { - ActionMessageView.present(message: UserText.vpnFeedbackFormSubmittedMessage, - presentationLocation: .withoutBottomBar) - } - } - } label: { - Text(category.displayName) - .daxBodyRegular() - .foregroundColor(.init(designSystemColor: .textPrimary)) - } - } - } header: { - header() - } - .increaseHeaderProminence() - } - .listRowBackground(Color(designSystemColor: .surface)) - } - .applyInsetGroupedListStyle() - .navigationTitle(UserText.netPStatusViewShareFeedback) - } - - @ViewBuilder - private func header() -> some View { - HStack { - Spacer(minLength: 0) - VStack(alignment: .center, spacing: 8) { - Text(UserText.vpnFeedbackFormTitle) - .daxHeadline() - .multilineTextAlignment(.center) - .foregroundColor(.init(designSystemColor: .textPrimary)) - Text(UserText.vpnFeedbackFormCategorySelect) - .daxFootnoteRegular() - .multilineTextAlignment(.center) - .foregroundColor(.init(designSystemColor: .textSecondary)) - } - .padding(.vertical, 16) - .background(Color(designSystemColor: .background)) - Spacer(minLength: 0) - } - } -} - -struct VPNFeedbackFormView: View { - @StateObject var viewModel: VPNFeedbackFormViewModel - @Environment(\.dismiss) private var dismiss - @FocusState private var isTextEditorFocused: Bool - - var onDismiss: () -> Void - - var body: some View { - configuredForm() - .applyBackground() - .navigationTitle(UserText.netPStatusViewShareFeedback) - } - - @ViewBuilder - private func form() -> some View { - ScrollView { - ScrollViewReader { scrollView in - VStack { - header() - textEditor() - .focused($isTextEditorFocused) - .onChange(of: isTextEditorFocused) { isFocused in - guard isFocused else { return } - DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { - withAnimation { - scrollView.scrollTo(1, anchor: .top) - } - } - } - submitButton() - .disabled(!viewModel.submitButtonEnabled) - } - } - } - } - - @ViewBuilder - private func configuredForm() -> some View { - if #available(iOS 16, *) { - form().scrollDismissesKeyboard(.interactively) - } else { - form() - } - } - - @ViewBuilder - private func header() -> some View { - HStack { - Spacer(minLength: 0) - VStack(alignment: .center, spacing: 8) { - Text(UserText.vpnFeedbackFormTitle) - .daxHeadline() - .multilineTextAlignment(.center) - .foregroundColor(.init(designSystemColor: .textPrimary)) - Text(viewModel.categoryName) - .daxFootnoteRegular() - .multilineTextAlignment(.center) - .foregroundColor(.init(designSystemColor: .textSecondary)) - } - .padding(.vertical, 16) - .background(Color(designSystemColor: .background)) - Spacer(minLength: 0) - } - } - - @ViewBuilder - private func textEditor() -> some View { - VStack(alignment: .leading, spacing: 10) { - Text(UserText.vpnFeedbackFormText1) - .multilineTextAlignment(.leading) - .lineLimit(nil) - .fixedSize(horizontal: false, vertical: true) - - Spacer() - .frame(height: 1) - .id(1) - - TextEditor(text: $viewModel.feedbackFormText) - .font(.body) - .foregroundColor(.primary) - .frame(height: 100) - .fixedSize(horizontal: false, vertical: true) - .onChange(of: viewModel.feedbackFormText) { - viewModel.feedbackFormText = String($0.prefix(1000)) - } - .padding(EdgeInsets(top: 3.0, leading: 6.0, bottom: 5.0, trailing: 0.0)) - .clipShape(RoundedRectangle(cornerRadius: 8.0, style: .continuous)) - .background( - ZStack { - RoundedRectangle(cornerRadius: 8.0) - .stroke(Color(designSystemColor: .textPrimary), lineWidth: 0.4) - RoundedRectangle(cornerRadius: 8.0) - .fill(Color(designSystemColor: .panel)) - } - ) - - Text(UserText.vpnFeedbackFormText2) - .multilineTextAlignment(.leading) - .lineLimit(nil) - .fixedSize(horizontal: false, vertical: true) - - VStack(alignment: .leading) { - Text(UserText.vpnFeedbackFormText3) - Text(UserText.vpnFeedbackFormText4) - } - - Text(UserText.vpnFeedbackFormText5) - .multilineTextAlignment(.leading) - .lineLimit(nil) - .fixedSize(horizontal: false, vertical: true) - } - .foregroundColor(.secondary) - .background(Color(designSystemColor: .background)) - .padding(16) - .daxFootnoteRegular() - } - - @ViewBuilder - private func submitButton() -> some View { - Button { - Task { - _ = await viewModel.sendFeedback() - } - dismiss() - onDismiss() - } label: { - Text(UserText.vpnFeedbackFormButtonSubmit) - } - .buttonStyle(VPNFeedbackFormButtonStyle()) - .padding(16) - } -} - -private struct VPNFeedbackFormButtonStyle: ButtonStyle { - - @Environment(\.isEnabled) private var isEnabled: Bool - - func makeBody(configuration: Configuration) -> some View { - configuration.label - .foregroundColor(Color.white) - .frame(maxWidth: .infinity) - .padding(.horizontal) - .frame(height: 50) - .background(Color(designSystemColor: .accent)) - .cornerRadius(8) - .daxButton() - .opacity(isEnabled ? 1.0 : 0.4) - - } - -} diff --git a/DuckDuckGo/Feedback/VPNFeedbackFormViewModel.swift b/DuckDuckGo/Feedback/VPNFeedbackFormViewModel.swift deleted file mode 100644 index 5efcd86a22..0000000000 --- a/DuckDuckGo/Feedback/VPNFeedbackFormViewModel.swift +++ /dev/null @@ -1,90 +0,0 @@ -// -// VPNFeedbackFormViewModel.swift -// DuckDuckGo -// -// Copyright © 2023 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation - -final class VPNFeedbackFormViewModel: ObservableObject { - - enum ViewState { - case feedbackPending - case feedbackSending - case feedbackSendingFailed - case feedbackSent - - var canSubmit: Bool { - switch self { - case .feedbackPending: return true - case .feedbackSending: return false - case .feedbackSendingFailed: return true - case .feedbackSent: return false - } - } - } - - @Published var viewState: ViewState = .feedbackPending { - didSet { - updateSubmitButtonStatus() - } - } - - @Published var feedbackFormText: String = "" { - didSet { - updateSubmitButtonStatus() - } - } - - @Published private(set) var submitButtonEnabled: Bool = false - - var categoryName: String { - category.displayName - } - - private let metadataCollector: VPNMetadataCollector - private let feedbackSender: VPNFeedbackSender - private let category: VPNFeedbackCategory - - init(metadataCollector: VPNMetadataCollector, - feedbackSender: VPNFeedbackSender = DefaultVPNFeedbackSender(), - category: VPNFeedbackCategory) { - self.metadataCollector = metadataCollector - self.feedbackSender = feedbackSender - self.category = category - } - - @MainActor - func sendFeedback() async -> Bool { - viewState = .feedbackSending - - do { - let metadata = await metadataCollector.collectVPNMetadata() - try await feedbackSender.send(metadata: metadata, category: category, userText: feedbackFormText) - viewState = .feedbackSent - return true - } catch { - viewState = .feedbackSendingFailed - } - - return false - } - - private func updateSubmitButtonStatus() { - self.submitButtonEnabled = viewState.canSubmit && !feedbackFormText.isEmpty - } - -} diff --git a/DuckDuckGo/Feedback/VPNFeedbackSender.swift b/DuckDuckGo/Feedback/VPNFeedbackSender.swift deleted file mode 100644 index 869c99791f..0000000000 --- a/DuckDuckGo/Feedback/VPNFeedbackSender.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// VPNFeedbackSender.swift -// DuckDuckGo -// -// Copyright © 2023 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import Core - -protocol VPNFeedbackSender { - func send(metadata: VPNMetadata, category: VPNFeedbackCategory, userText: String) async throws -} - -struct DefaultVPNFeedbackSender: VPNFeedbackSender { - - func send(metadata: VPNMetadata, category: VPNFeedbackCategory, userText: String) async throws { - let encodedUserText = userText.addingPercentEncoding(withAllowedCharacters: .alphanumerics.union(.init(charactersIn: "-._~"))) ?? userText - - try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in - Pixel.fire(pixel: .networkProtectionBreakageReport, withAdditionalParameters: [ - "breakageCategory": category.rawValue, - "breakageDescription": encodedUserText, - "breakageMetadata": metadata.toBase64(), - ], includedParameters: [.appVersion, .atb]) { error in - if let error { - continuation.resume(throwing: error) - } else { - continuation.resume() - } - } - } - } - -} diff --git a/DuckDuckGo/NetworkProtectionStatusView.swift b/DuckDuckGo/NetworkProtectionStatusView.swift index af376f6611..edff67074b 100644 --- a/DuckDuckGo/NetworkProtectionStatusView.swift +++ b/DuckDuckGo/NetworkProtectionStatusView.swift @@ -297,10 +297,6 @@ struct NetworkProtectionStatusView: View { NavigationLink(UserText.subscriptionFeedback, destination: UnifiedFeedbackRootView(viewModel: viewModel)) .daxBodyRegular() .foregroundColor(.init(designSystemColor: .textPrimary)) - } else { - NavigationLink(UserText.netPVPNSettingsShareFeedback, destination: VPNFeedbackFormCategoryView()) - .daxBodyRegular() - .foregroundColor(.init(designSystemColor: .textPrimary)) } } header: { Text(UserText.vpnAbout).foregroundColor(.init(designSystemColor: .textSecondary)) diff --git a/DuckDuckGo/Feedback/VPNMetadataCollector.swift b/DuckDuckGo/Subscription/Feedback/VPNMetadataCollector.swift similarity index 100% rename from DuckDuckGo/Feedback/VPNMetadataCollector.swift rename to DuckDuckGo/Subscription/Feedback/VPNMetadataCollector.swift From 7f129be6a5e0f47175a2f87cdeac41370d59a1d0 Mon Sep 17 00:00:00 2001 From: Anh Do Date: Mon, 6 Jan 2025 15:41:14 +0700 Subject: [PATCH 4/4] Update BSK --- DuckDuckGo.xcodeproj/project.pbxproj | 4 ++-- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 652b7c3496..45f919b89f 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -11796,8 +11796,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { - kind = exactVersion; - version = 223.0.0; + kind = revision; + revision = 6ed78b04baed8a02248b23c6b796573ac020ee70; }; }; 9F8FE9472BAE50E50071E372 /* XCRemoteSwiftPackageReference "lottie-spm" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index a91a1f376a..f846cfc638 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,8 +32,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "e8f94cf597f4a447f86f39f461b736ac9ea280ce", - "version" : "223.0.0" + "revision" : "6ed78b04baed8a02248b23c6b796573ac020ee70" } }, {