diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 526fdb1c36..bfff45d66d 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -306,6 +306,7 @@ 4BCD14632B05AF2B000B1E4C /* NetworkProtectionAccessController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BCD14622B05AF2B000B1E4C /* NetworkProtectionAccessController.swift */; }; 4BCD14672B05B682000B1E4C /* NetworkProtectionTermsAndConditionsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BCD14662B05B682000B1E4C /* NetworkProtectionTermsAndConditionsStore.swift */; }; 4BCD14692B05BDD5000B1E4C /* AppDelegate+Waitlists.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BCD14682B05BDD5000B1E4C /* AppDelegate+Waitlists.swift */; }; + 4BCD146B2B05C4B5000B1E4C /* VPNWaitlistTermsAndConditionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BCD146A2B05C4B5000B1E4C /* VPNWaitlistTermsAndConditionsViewController.swift */; }; 4BE2756827304F57006B20B0 /* URLRequestExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BE27566272F878F006B20B0 /* URLRequestExtension.swift */; }; 4BEF65692989C2FC00B650CB /* AdapterSocketEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 021D307A2989C0C400918636 /* AdapterSocketEvent.swift */; }; 4BEF656A2989C2FC00B650CB /* ProxyServerEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 021D307C2989C0C600918636 /* ProxyServerEvent.swift */; }; @@ -1331,6 +1332,7 @@ 4BCD14622B05AF2B000B1E4C /* NetworkProtectionAccessController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionAccessController.swift; sourceTree = ""; }; 4BCD14662B05B682000B1E4C /* NetworkProtectionTermsAndConditionsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionTermsAndConditionsStore.swift; sourceTree = ""; }; 4BCD14682B05BDD5000B1E4C /* AppDelegate+Waitlists.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Waitlists.swift"; sourceTree = ""; }; + 4BCD146A2B05C4B5000B1E4C /* VPNWaitlistTermsAndConditionsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNWaitlistTermsAndConditionsViewController.swift; sourceTree = ""; }; 4BE27566272F878F006B20B0 /* URLRequestExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = URLRequestExtension.swift; path = ../DuckDuckGo/URLRequestExtension.swift; sourceTree = ""; }; 4BFB911A29B7D9530014D4B7 /* AppTrackingProtectionStoringModelPerformanceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTrackingProtectionStoringModelPerformanceTests.swift; sourceTree = ""; }; 56244C1C2A137B1900EDF259 /* WaitlistViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitlistViews.swift; sourceTree = ""; }; @@ -3479,6 +3481,7 @@ 4BBBBA892B031B4200D965DA /* VPNWaitlistDebugViewController.swift */, 4BBBBA8B2B031B4200D965DA /* VPNWaitlistView.swift */, 4BBBBA8A2B031B4200D965DA /* VPNWaitlistViewController.swift */, + 4BCD146A2B05C4B5000B1E4C /* VPNWaitlistTermsAndConditionsViewController.swift */, ); name = VPN; sourceTree = ""; @@ -6425,6 +6428,7 @@ 85AE6690209724120014CF04 /* NotificationView.swift in Sources */, 1EA51376286596A000493C6A /* PrivacyIconLogic.swift in Sources */, 980891A92238504B00313A70 /* UILabelExtension.swift in Sources */, + 4BCD146B2B05C4B5000B1E4C /* VPNWaitlistTermsAndConditionsViewController.swift in Sources */, 984D035A24ACCC7D0066CFB8 /* TabViewCell.swift in Sources */, 31951E8E2823003200CAF535 /* AutofillLoginDetailsHeaderView.swift in Sources */, F194FAED1F14E2B3009B4DF8 /* UIFontExtension.swift in Sources */, diff --git a/DuckDuckGo/MacWaitlistViewController.swift b/DuckDuckGo/MacWaitlistViewController.swift index d7d50cfe59..19c3f05a70 100644 --- a/DuckDuckGo/MacWaitlistViewController.swift +++ b/DuckDuckGo/MacWaitlistViewController.swift @@ -100,9 +100,6 @@ extension MacWaitlistViewController: WaitlistViewModelDelegate { } } - func waitlistViewModelShouldRefreshState(_ viewModel: WaitlistViewModel) -> Bool { - return true - } } private final class MacWaitlistLinkMetadata: NSObject, UIActivityItemSource { diff --git a/DuckDuckGo/VPNWaitlist.swift b/DuckDuckGo/VPNWaitlist.swift index 66ebc3e278..e2beeb1660 100644 --- a/DuckDuckGo/VPNWaitlist.swift +++ b/DuckDuckGo/VPNWaitlist.swift @@ -116,10 +116,6 @@ final class VPNWaitlist: Waitlist { } -extension WaitlistViewModel.ViewCustomState { - static var networkProtectionPrivacyPolicyScreen = WaitlistViewModel.ViewCustomState(identifier: "networkProtectionPrivacyPolicyScreen") -} - extension WaitlistViewModel.ViewCustomAction { static var openNetworkProtectionInviteCodeScreen = WaitlistViewModel.ViewCustomAction(identifier: "openNetworkProtectionInviteCodeScreen") static var openNetworkProtectionPrivacyPolicyScreen = WaitlistViewModel.ViewCustomAction(identifier: "openNetworkProtectionPrivacyPolicyScreen") diff --git a/DuckDuckGo/VPNWaitlistTermsAndConditionsViewController.swift b/DuckDuckGo/VPNWaitlistTermsAndConditionsViewController.swift new file mode 100644 index 0000000000..d1f80e13ca --- /dev/null +++ b/DuckDuckGo/VPNWaitlistTermsAndConditionsViewController.swift @@ -0,0 +1,73 @@ +// +// VPNWaitlistTermsAndConditionsViewController.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. +// + +#if NETWORK_PROTECTION + +import UIKit +import SwiftUI +import Core +import Waitlist + +@available(iOS 15.0, *) +final class VPNWaitlistTermsAndConditionsViewController: UIViewController { + + override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + title = UserText.networkProtectionWaitlistJoinTitle + addHostingControllerToViewHierarchy() + } + + private func addHostingControllerToViewHierarchy() { + let waitlistView = VPNWaitlistPrivacyPolicyView { _ in + var termsAndConditionsStore = NetworkProtectionTermsAndConditionsUserDefaultsStore() + termsAndConditionsStore.networkProtectionWaitlistTermsAndConditionsAccepted = true + + self.navigationController?.popToRootViewController(animated: true) + let networkProtectionViewController = NetworkProtectionRootViewController() + self.navigationController?.pushViewController(networkProtectionViewController, animated: true) + } + + let waitlistViewController = UIHostingController(rootView: waitlistView) + waitlistViewController.view.backgroundColor = UIColor(designSystemColor: .background) + + addChild(waitlistViewController) + waitlistViewController.view.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(waitlistViewController.view) + waitlistViewController.didMove(toParent: self) + + NSLayoutConstraint.activate([ + waitlistViewController.view.widthAnchor.constraint(equalTo: view.widthAnchor), + waitlistViewController.view.heightAnchor.constraint(equalTo: view.heightAnchor), + waitlistViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), + waitlistViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor) + ]) + } + +} + +#endif diff --git a/DuckDuckGo/VPNWaitlistView.swift b/DuckDuckGo/VPNWaitlistView.swift index 421aaa3923..c47ef347fe 100644 --- a/DuckDuckGo/VPNWaitlistView.swift +++ b/DuckDuckGo/VPNWaitlistView.swift @@ -49,12 +49,6 @@ struct VPNWaitlistView: View { } case .waitlistRemoved: fatalError("State not supported for VPN waitlists") - case .custom(let customState): - if customState == .networkProtectionPrivacyPolicyScreen { - VPNWaitlistPrivacyPolicyView { action in - Task { await viewModel.perform(action: action) } - } - } } } } @@ -230,7 +224,7 @@ struct VPNWaitlistInvitedView: View { ), ] - let action: (WaitlistViewModel.ViewAction) -> Void + let action: WaitlistViewActionHandler @State private var shareButtonFrame: CGRect = .zero @@ -275,7 +269,7 @@ struct VPNWaitlistInvitedView: View { @available(iOS 15.0, *) struct VPNWaitlistPrivacyPolicyView: View { - let action: (WaitlistViewModel.ViewAction) -> Void + let action: WaitlistViewActionHandler var body: some View { ScrollView { diff --git a/DuckDuckGo/VPNWaitlistViewController.swift b/DuckDuckGo/VPNWaitlistViewController.swift index c0e2113ee1..38990a15be 100644 --- a/DuckDuckGo/VPNWaitlistViewController.swift +++ b/DuckDuckGo/VPNWaitlistViewController.swift @@ -21,7 +21,6 @@ import UIKit import SwiftUI -import LinkPresentation import Core import Waitlist @@ -132,16 +131,8 @@ extension VPNWaitlistViewController: WaitlistViewModelDelegate { } if action == .openNetworkProtectionPrivacyPolicyScreen { - self.viewModel.set(customState: .networkProtectionPrivacyPolicyScreen) - } - - if action == .acceptNetworkProtectionTerms { - var termsAndConditionsStore = NetworkProtectionTermsAndConditionsUserDefaultsStore() - termsAndConditionsStore.networkProtectionWaitlistTermsAndConditionsAccepted = true - - self.navigationController?.popViewController(animated: true) - let networkProtectionViewController = NetworkProtectionRootViewController() - self.navigationController?.pushViewController(networkProtectionViewController, animated: true) + let termsAndConditionsViewController = VPNWaitlistTermsAndConditionsViewController() + self.navigationController?.pushViewController(termsAndConditionsViewController, animated: true) } } @@ -153,10 +144,6 @@ extension VPNWaitlistViewController: WaitlistViewModelDelegate { // The VPN waitlist doesn't support the share sheet } - func waitlistViewModelShouldRefreshState(_ viewModel: WaitlistViewModel) -> Bool { - return viewModel.viewState != .custom(.networkProtectionPrivacyPolicyScreen) - } - } #endif diff --git a/DuckDuckGo/WindowsBrowserWaitlistView.swift b/DuckDuckGo/WindowsBrowserWaitlistView.swift index c2726ea2f8..f6a0796f21 100644 --- a/DuckDuckGo/WindowsBrowserWaitlistView.swift +++ b/DuckDuckGo/WindowsBrowserWaitlistView.swift @@ -48,8 +48,6 @@ struct WindowsBrowserWaitlistView: View { WaitlistDownloadBrowserContentView(platform: .windows) { action in Task { await viewModel.perform(action: action) } } - case .custom: - fatalError("Tried to use custom state without supporting one") } } } diff --git a/DuckDuckGo/WindowsWaitlistViewController.swift b/DuckDuckGo/WindowsWaitlistViewController.swift index 43df2cf85d..c130b99c62 100644 --- a/DuckDuckGo/WindowsWaitlistViewController.swift +++ b/DuckDuckGo/WindowsWaitlistViewController.swift @@ -144,10 +144,6 @@ extension WindowsWaitlistViewController: WaitlistViewModelDelegate { } } - func waitlistViewModelShouldRefreshState(_ viewModel: WaitlistViewModel) -> Bool { - return true - } - } private final class WindowsWaitlistLinkMetadata: NSObject, UIActivityItemSource { diff --git a/LocalPackages/Waitlist/Sources/Waitlist/WaitlistViewModel.swift b/LocalPackages/Waitlist/Sources/Waitlist/WaitlistViewModel.swift index f4c0fe9c23..4802594be3 100644 --- a/LocalPackages/Waitlist/Sources/Waitlist/WaitlistViewModel.swift +++ b/LocalPackages/Waitlist/Sources/Waitlist/WaitlistViewModel.swift @@ -28,7 +28,6 @@ public protocol WaitlistViewModelDelegate: AnyObject { func waitlistViewModelDidOpenInviteCodeShareSheet(_ viewModel: WaitlistViewModel, inviteCode: String, senderFrame: CGRect) func waitlistViewModelDidOpenDownloadURLShareSheet(_ viewModel: WaitlistViewModel, senderFrame: CGRect) func waitlistViewModel(_ viewModel: WaitlistViewModel, didTriggerCustomAction action: WaitlistViewModel.ViewCustomAction) - func waitlistViewModelShouldRefreshState(_ viewModel: WaitlistViewModel) -> Bool } @MainActor @@ -40,15 +39,6 @@ public final class WaitlistViewModel: ObservableObject { case joinedQueue(NotificationPermissionState) case invited(inviteCode: String) case waitlistRemoved - case custom(ViewCustomState) - } - - public struct ViewCustomState: Equatable { - public let identifier: String - - public init(identifier: String) { - self.identifier = identifier - } } public enum ViewAction: Equatable { @@ -104,10 +94,6 @@ public final class WaitlistViewModel: ObservableObject { } public func updateViewState() async { - guard delegate?.waitlistViewModelShouldRefreshState(self) ?? true else { - return - } - guard viewState != .waitlistRemoved else { return } @@ -133,10 +119,6 @@ public final class WaitlistViewModel: ObservableObject { } } - public func set(customState: ViewCustomState) { - self.viewState = .custom(customState) - } - // MARK: - Private private func checkNotificationPermissions() async { diff --git a/LocalPackages/Waitlist/Sources/WaitlistMocks/MockWaitlistViewModelDelegate.swift b/LocalPackages/Waitlist/Sources/WaitlistMocks/MockWaitlistViewModelDelegate.swift index db3f16af5e..f8440ccda3 100644 --- a/LocalPackages/Waitlist/Sources/WaitlistMocks/MockWaitlistViewModelDelegate.swift +++ b/LocalPackages/Waitlist/Sources/WaitlistMocks/MockWaitlistViewModelDelegate.swift @@ -53,8 +53,4 @@ public class MockWaitlistViewModelDelegate: WaitlistViewModelDelegate { didTriggerCustomActionCalled = true } - public func waitlistViewModelShouldRefreshState(_ viewModel: WaitlistViewModel) -> Bool { - return true - } - }