From cec1679c05bc713ac1ef3a37f123a12c44a0bd4c Mon Sep 17 00:00:00 2001 From: alpavanoglu Date: Wed, 5 Jul 2023 14:17:06 +0200 Subject: [PATCH 01/14] Add `CompliancePopoverCoordinator` `CompliancePopoverViewModel` & `CompliancePopoverViewController` --- Podfile | 4 +- Podfile.lock | 8 +- .../BuildInformation/FeatureFlag.swift | 5 + .../Blog/Blog + Me/GravatarButtonView.swift | 1 - .../BlogDashboardViewController.swift | 5 + .../ComplianceLocationService.swift | 14 +++ .../CompliancePopover.swift | 48 ++++--- .../CompliancePopoverCoordinator.swift | 89 +++++++++++++ .../CompliancePopoverViewController.swift | 117 ++++++++++++++++++ .../CompliancePopoverViewModel.swift | 19 +++ WordPress/WordPress.xcodeproj/project.pbxproj | 34 ++++- 11 files changed, 321 insertions(+), 23 deletions(-) create mode 100644 WordPress/Classes/ViewRelated/EEUUSCompliance/ComplianceLocationService.swift rename WordPress/Classes/ViewRelated/{Reusable SwiftUI Views => EEUUSCompliance}/CompliancePopover.swift (74%) create mode 100644 WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift create mode 100644 WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewController.swift create mode 100644 WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift diff --git a/Podfile b/Podfile index a3b0285d7bae..aeddcaa929a6 100644 --- a/Podfile +++ b/Podfile @@ -50,9 +50,9 @@ def wordpress_ui end def wordpress_kit - pod 'WordPressKit', '~> 8.4-beta' - # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', branch: '' + pod 'WordPressKit', '~> 8.4-beta.3' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', tag: '' + # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', branch: '' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', commit: '' # pod 'WordPressKit', path: '../WordPressKit-iOS' end diff --git a/Podfile.lock b/Podfile.lock index 76d58befec46..4aaf93215397 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -612,7 +612,7 @@ DEPENDENCIES: - SwiftLint (~> 0.50) - WordPress-Editor-iOS (~> 1.19.8) - WordPressAuthenticator (~> 6.1-beta) - - WordPressKit (~> 8.4-beta) + - WordPressKit (~> 8.4-beta.3) - WordPressShared (~> 2.2-beta) - WordPressUI (~> 1.12.5) - WPMediaPicker (~> 1.8-beta) @@ -623,7 +623,6 @@ DEPENDENCIES: SPEC REPOS: https://github.com/wordpress-mobile/cocoapods-specs.git: - WordPressAuthenticator - - WordPressKit trunk: - Alamofire - AlamofireImage @@ -662,6 +661,7 @@ SPEC REPOS: - UIDeviceIdentifier - WordPress-Aztec-iOS - WordPress-Editor-iOS + - WordPressKit - WordPressShared - WordPressUI - WPMediaPicker @@ -880,7 +880,7 @@ SPEC CHECKSUMS: WordPress-Aztec-iOS: 7d11d598f14c82c727c08b56bd35fbeb7dafb504 WordPress-Editor-iOS: 9eb9f12f21a5209cb837908d81ffe1e31cb27345 WordPressAuthenticator: b0b900696de5129a215adcd1e9ae6eb89da36ac8 - WordPressKit: f445e6fc3c63ddf611513a435408f86fdf3808b3 + WordPressKit: 66e1411f48503b3ff3ab2c7db82590e436097882 WordPressShared: 87f3ee89b0a3e83106106f13a8b71605fb8eb6d2 WordPressUI: c5be816f6c7b3392224ac21de9e521e89fa108ac WPMediaPicker: 0d40b8d66b6dfdaa2d6a41e3be51249ff5898775 @@ -895,6 +895,6 @@ SPEC CHECKSUMS: ZendeskSupportSDK: 3a8e508ab1d9dd22dc038df6c694466414e037ba ZIPFoundation: ae5b4b813d216d3bf0a148773267fff14bd51d37 -PODFILE CHECKSUM: 623e3fe64f67d2c0352e26d1ac2bcd58c06b3494 +PODFILE CHECKSUM: df7dcee5d41f8627da2633111de057f07ee5bf55 COCOAPODS: 1.12.1 diff --git a/WordPress/Classes/Utility/BuildInformation/FeatureFlag.swift b/WordPress/Classes/Utility/BuildInformation/FeatureFlag.swift index 258bafae49d6..84db479208c4 100644 --- a/WordPress/Classes/Utility/BuildInformation/FeatureFlag.swift +++ b/WordPress/Classes/Utility/BuildInformation/FeatureFlag.swift @@ -41,6 +41,7 @@ enum FeatureFlag: Int, CaseIterable { case personalizeHomeTab case commentModerationUpdate case jetpackSocial + case compliancePopover /// Returns a boolean indicating if the feature is enabled var enabled: Bool { @@ -131,6 +132,8 @@ enum FeatureFlag: Int, CaseIterable { return false case .jetpackSocial: return AppConfiguration.isJetpack && BuildConfiguration.current == .localDeveloper + case .compliancePopover: + return true } } @@ -231,6 +234,8 @@ extension FeatureFlag { return "Comments Moderation Update" case .jetpackSocial: return "Jetpack Social" + case .compliancePopover: + return "Compliance Popover" } } } diff --git a/WordPress/Classes/ViewRelated/Blog/Blog + Me/GravatarButtonView.swift b/WordPress/Classes/ViewRelated/Blog/Blog + Me/GravatarButtonView.swift index d5e31c5dea59..de7b0c2047f6 100644 --- a/WordPress/Classes/ViewRelated/Blog/Blog + Me/GravatarButtonView.swift +++ b/WordPress/Classes/ViewRelated/Blog/Blog + Me/GravatarButtonView.swift @@ -28,7 +28,6 @@ class GravatarButtonView: CircularImageView { } } - /// Touch animation extension GravatarButtonView { diff --git a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/BlogDashboardViewController.swift b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/BlogDashboardViewController.swift index 5ae6a2a89ca3..54037f32961e 100644 --- a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/BlogDashboardViewController.swift +++ b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/BlogDashboardViewController.swift @@ -14,6 +14,8 @@ final class BlogDashboardViewController: UIViewController { BlogDashboardViewModel(viewController: self, blog: blog) }() + private var complianceCoordinator: CompliancePopoverCoordinator? + lazy var collectionView: DynamicHeightCollectionView = { let collectionView = DynamicHeightCollectionView(frame: .zero, collectionViewLayout: createLayout()) collectionView.translatesAutoresizingMaskIntoConstraints = false @@ -79,6 +81,9 @@ final class BlogDashboardViewController: UIViewController { startAlertTimer() WPAnalytics.track(.mySiteDashboardShown) + + complianceCoordinator = CompliancePopoverCoordinator(viewController: self) + complianceCoordinator?.presentIfNeeded() } override func viewWillDisappear(_ animated: Bool) { diff --git a/WordPress/Classes/ViewRelated/EEUUSCompliance/ComplianceLocationService.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/ComplianceLocationService.swift new file mode 100644 index 000000000000..a5f2cd4350a9 --- /dev/null +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/ComplianceLocationService.swift @@ -0,0 +1,14 @@ +import WordPressKit + +final class ComplianceLocationService { + func getIPCountryCode(completion: @escaping (Result) -> Void) { + IPLocationRemote().fetchIPCountryCode { result in + switch result { + case .success(let countryCode): + completion(.success(countryCode)) + case .failure(let error): + completion(.failure(error)) + } + } + } +} diff --git a/WordPress/Classes/ViewRelated/Reusable SwiftUI Views/CompliancePopover.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopover.swift similarity index 74% rename from WordPress/Classes/ViewRelated/Reusable SwiftUI Views/CompliancePopover.swift rename to WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopover.swift index 85dfc63383e8..e44967563d83 100644 --- a/WordPress/Classes/ViewRelated/Reusable SwiftUI Views/CompliancePopover.swift +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopover.swift @@ -1,10 +1,24 @@ import SwiftUI struct CompliancePopover: View { + var goToSettingsAction: (() -> ())? + var saveAction: (() -> ())? + var shouldScroll: Bool = false + @State private var isAnalyticsOn = true var body: some View { + if shouldScroll { + ScrollView(showsIndicators: false) { + contentVStack + } + } else { + contentVStack + } + } + + private var contentVStack: some View { VStack(alignment: .leading, spacing: Length.Padding.double) { titleText subtitleText @@ -13,6 +27,7 @@ struct CompliancePopover: View { buttonsHStack } .padding(Length.Padding.small) + .fixedSize(horizontal: false, vertical: true) } private var titleText: some View { @@ -34,7 +49,8 @@ struct CompliancePopover: View { } private var footnote: some View { - Text("") + Text(Strings.footnote) + .font(.body) .foregroundColor(.secondary) } @@ -46,30 +62,32 @@ struct CompliancePopover: View { } private var settingsButton: some View { - ZStack { - RoundedRectangle(cornerRadius: Length.Padding.single) - .stroke(Color.DS.Border.divider, lineWidth: Length.Border.thin) - Button(action: { - print("Settings tapped") - }) { + Button(action: { + goToSettingsAction?() + }) { + ZStack { + RoundedRectangle(cornerRadius: Length.Padding.single) + .stroke(Color.DS.Border.divider, lineWidth: Length.Border.thin) Text(Strings.settingsButtonTitle) + .font(.body) } - .foregroundColor(Color.DS.Background.brand) } + .foregroundColor(Color.DS.Background.brand) .frame(height: Length.Hitbox.minTapDimension) } private var saveButton: some View { - ZStack { - RoundedRectangle(cornerRadius: Length.Radius.minHeightButton) - .fill(Color.DS.Background.brand) - Button(action: { - print("Save tapped") - }) { + Button(action: { + saveAction?() + }) { + ZStack { + RoundedRectangle(cornerRadius: Length.Radius.minHeightButton) + .fill(Color.DS.Background.brand) Text(Strings.saveButtonTitle) + .font(.body) } - .foregroundColor(.white) } + .foregroundColor(.white) .frame(height: Length.Hitbox.minTapDimension) } } diff --git a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift new file mode 100644 index 000000000000..af1271dd3f09 --- /dev/null +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift @@ -0,0 +1,89 @@ +import UIKit + +final class CompliancePopoverCoordinator { + fileprivate enum Constants { + static let hasSavedPrivacyBannerSettingsKey = "hasSavedPrivacyBannerSettings" + } + + private let viewController: UIViewController + private let complianceService = ComplianceLocationService() + private let defaults: UserDefaults + + init(viewController: UIViewController, defaults: UserDefaults = UserDefaults.standard) { + self.viewController = viewController + self.defaults = defaults + } + + func presentIfNeeded() { + guard FeatureFlag.compliancePopover.enabled else { + return + } + complianceService.getIPCountryCode { [weak self] result in + switch result { + case .success(let countryCode): + DispatchQueue.main.async { + guard let self, self.shouldShowPrivacyBanner(countryCode: countryCode) else { + return + } + let complianceViewModel = CompliancePopoverViewModel() + let complianceViewController = CompliancePopoverViewController(viewModel: complianceViewModel) + let bottomSheetViewController = BottomSheetViewController(childViewController: complianceViewController, customHeaderSpacing: 0) + bottomSheetViewController.show(from: self.viewController) + } + case .failure(let error): + () + } + } + } + + func shouldShowPrivacyBanner(countryCode: String) -> Bool { + let isCountryInEU = Self.gdprCountryCodes.contains(countryCode) + let hasSavedPrivacySettings = defaults.hasSavedPrivacyBannerSettings + return isCountryInEU && !hasSavedPrivacySettings + } +} + +private extension CompliancePopoverCoordinator { + static let gdprCountryCodes: Set = [ + "AT", "AUT", // Austria + "BE", "BEL", // Belgium + "BG", "BGR", // Bulgaria + "HR", "HRV", // Croatia + "CY", "CYP", // Cyprus + "CZ", "CZE", // Czech Republic + "DK", "DNK", // Denmark + "EE", "EST", // Estonia + "FI", "FIN", // Finland + "FR", "FRA", // France + "DE", "DEU", // Germany + "GR", "GRC", // Greece + "HU", "HUN", // Hungary + "IE", "IRL", // Ireland + "IT", "ITA", // Italy + "LV", "LVA", // Latvia + "LT", "LTU", // Lithuania + "LU", "LUX", // Luxembourg + "MT", "MLT", // Malta + "NL", "NLD", // Netherlands + "NO", "NOR", // Norway + "PL", "POL", // Poland + "PT", "PRT", // Portugal + "RO", "ROU", // Romania + "SK", "SVK", // Slovakia + "SI", "SVN", // Slovenia + "ES", "ESP", // Spain + "SE", "SWE", // Sweden + "CH", "CHE", // Switzerland + "IS", + "LI", + "GB", + // *Although the UK has departed from the EU as of January 2021, + // the GDPR was enacted before its withdrawal and is therefore considered a valid UK law.* + ] +} + +private extension UserDefaults { + @objc dynamic var hasSavedPrivacyBannerSettings: Bool { + bool(forKey: CompliancePopoverCoordinator.Constants.hasSavedPrivacyBannerSettingsKey) + } +} diff --git a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewController.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewController.swift new file mode 100644 index 000000000000..7c142a7b7d28 --- /dev/null +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewController.swift @@ -0,0 +1,117 @@ +import UIKit +import SwiftUI +import WordPressUI + +//final class CompliancePopoverViewController: UIHostingController { +// +// private let viewModel: CompliancePopoverViewModelProtocol +// +// /// Tracks the banner view intrinsic height. +// /// Needed to enable it's scrolling when it grows bigger than the screen. +// /// +// var bannerIntrinsicHeight: CGFloat = 0 +// +// init(viewModel: CompliancePopoverViewModelProtocol) { +// self.viewModel = viewModel +// super.init(rootView: CompliancePopover()) +// } +// +// override func viewDidLoad() { +// super.viewDidLoad() +// rootView.goToSettingsAction = { [weak self] in +// self?.viewModel.didTapSettings() +// } +// +// rootView.saveAction = { [weak self] in +// self?.viewModel.didTapSave() +// } +// } +// +// /// Needed for protocol conformance. +// /// +// required dynamic init?(coder aDecoder: NSCoder) { +// fatalError("init(coder:) has not been implemented") +// } +// +//// override func viewDidLayoutSubviews() { +//// super.viewDidLayoutSubviews() +//// +//// print("Kill me right now") +//// // Make the banner scrollable when the banner height is bigger than the screen height. +//// // Send it in the next run loop to avoid a recursive `viewDidLayoutSubviews`. +//// bannerIntrinsicHeight = view.intrinsicContentSize.height +//// DispatchQueue.main.async { +//// self.rootView.shouldScroll = self.bannerIntrinsicHeight > self.view.frame.height +//// } +//// } +//} + +final class CompliancePopoverViewController: UIViewController { + + // MARK: - Views + private let hostingController: UIViewController = { + let controller = UIHostingController(rootView: CompliancePopover()) + controller.view.translatesAutoresizingMaskIntoConstraints = true + return controller + }() + + private var contentView: UIView { + return hostingController.view + } + + private let viewModel: CompliancePopoverViewModelProtocol + + init(viewModel: CompliancePopoverViewModelProtocol) { + self.viewModel = viewModel + super.init(nibName: nil, bundle: nil) + } + + required dynamic init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - View Lifecycle + override func viewDidLoad() { + super.viewDidLoad() + self.addContentView() + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + let targetSize = CGSize(width: view.bounds.width, height: 0) + let contentViewSize = contentView.systemLayoutSizeFitting(targetSize) + self.contentView.frame = .init(origin: .zero, size: contentViewSize) + self.preferredContentSize = contentView.bounds.size + } + + private func addContentView() { + self.hostingController.willMove(toParent: self) + self.addChild(hostingController) + self.view.addSubview(contentView) + self.hostingController.didMove(toParent: self) + } +} + +// MARK: - DrawerPresentable +extension CompliancePopoverViewController: DrawerPresentable { + var collapsedHeight: DrawerHeight { + if traitCollection.verticalSizeClass == .compact { + return .maxHeight + } + return .intrinsicHeight + } + + var allowsUserTransition: Bool { + print("89fas9(*") + return false + } + + var allowsDragToDismiss: Bool { + false + } + + var allowsTapToDismiss: Bool { + print("AD*SJ*(") + return false + } +} diff --git a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift new file mode 100644 index 000000000000..772f9faac9ca --- /dev/null +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift @@ -0,0 +1,19 @@ +import Foundation +import UIKit +import WordPressUI + +protocol CompliancePopoverViewModelProtocol { + func didTapSettings() + func didTapSave() +} + +final class CompliancePopoverViewModel: CompliancePopoverViewModelProtocol { + func didTapSettings() { + print("** TAPPED SETTINGS **") + RootViewCoordinator.sharedPresenter.navigateToPrivacySettings() + } + + func didTapSave() { + print("** TAPPED SAVE **") + } +} diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 7b4227479154..3cb96d66e4e2 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -233,6 +233,10 @@ 0839F88B2993C0C000415038 /* JetpackDefaultOverlayCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0857BB3F299275760011CBD1 /* JetpackDefaultOverlayCoordinator.swift */; }; 0839F88C2993C1B500415038 /* JetpackPluginOverlayCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 084FC3BA29914C7F00A17BCF /* JetpackPluginOverlayCoordinator.swift */; }; 0839F88D2993C1B600415038 /* JetpackPluginOverlayCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 084FC3BA29914C7F00A17BCF /* JetpackPluginOverlayCoordinator.swift */; }; + 083ED8CC2A4322CB007F89B3 /* ComplianceLocationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083ED8CB2A4322CB007F89B3 /* ComplianceLocationService.swift */; }; + 083ED8CD2A4322CB007F89B3 /* ComplianceLocationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083ED8CB2A4322CB007F89B3 /* ComplianceLocationService.swift */; }; + 0840513E2A4DDE3400A596E6 /* CompliancePopoverCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0840513D2A4DDE3400A596E6 /* CompliancePopoverCoordinator.swift */; }; + 0840513F2A4DDE3400A596E6 /* CompliancePopoverCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0840513D2A4DDE3400A596E6 /* CompliancePopoverCoordinator.swift */; }; 0845B8C61E833C56001BA771 /* URL+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0845B8C51E833C56001BA771 /* URL+Helpers.swift */; }; 08472A201C727E020040769D /* PostServiceOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 08472A1F1C727E020040769D /* PostServiceOptions.m */; }; 084A07062848E1820054508A /* FeatureHighlightStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 084A07052848E1820054508A /* FeatureHighlightStore.swift */; }; @@ -314,6 +318,10 @@ 08DF9C441E8475530058678C /* test-image-portrait.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 08DF9C431E8475530058678C /* test-image-portrait.jpg */; }; 08E39B4528A3DEB200874CB8 /* UserPersistentStoreFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E39B4428A3DEB200874CB8 /* UserPersistentStoreFactory.swift */; }; 08E39B4628A3DEB200874CB8 /* UserPersistentStoreFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E39B4428A3DEB200874CB8 /* UserPersistentStoreFactory.swift */; }; + 08E6E07B2A4C3E3A00B807B0 /* CompliancePopoverViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E6E07A2A4C3E3A00B807B0 /* CompliancePopoverViewController.swift */; }; + 08E6E07C2A4C3E3A00B807B0 /* CompliancePopoverViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E6E07A2A4C3E3A00B807B0 /* CompliancePopoverViewController.swift */; }; + 08E6E07E2A4C405500B807B0 /* CompliancePopoverViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E6E07D2A4C405500B807B0 /* CompliancePopoverViewModel.swift */; }; + 08E6E07F2A4C405500B807B0 /* CompliancePopoverViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E6E07D2A4C405500B807B0 /* CompliancePopoverViewModel.swift */; }; 08E77F451EE87FCF006F9515 /* MediaThumbnailExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E77F441EE87FCF006F9515 /* MediaThumbnailExporter.swift */; }; 08E77F471EE9D72F006F9515 /* MediaThumbnailExporterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E77F461EE9D72F006F9515 /* MediaThumbnailExporterTests.swift */; }; 08EA036729C9B51200B72A87 /* Color+DesignSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08EA036629C9B51200B72A87 /* Color+DesignSystem.swift */; }; @@ -5911,6 +5919,8 @@ 082AB9DB1C4F035E000CA523 /* PostTag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PostTag.h; sourceTree = ""; }; 082AB9DC1C4F035E000CA523 /* PostTag.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PostTag.m; sourceTree = ""; }; 082D50AE1EF46DB300788719 /* WordPress 61.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "WordPress 61.xcdatamodel"; sourceTree = ""; }; + 083ED8CB2A4322CB007F89B3 /* ComplianceLocationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComplianceLocationService.swift; sourceTree = ""; }; + 0840513D2A4DDE3400A596E6 /* CompliancePopoverCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompliancePopoverCoordinator.swift; sourceTree = ""; }; 0845B8C51E833C56001BA771 /* URL+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URL+Helpers.swift"; sourceTree = ""; }; 08472A1E1C7273FA0040769D /* PostServiceOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PostServiceOptions.h; sourceTree = ""; }; 08472A1F1C727E020040769D /* PostServiceOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PostServiceOptions.m; sourceTree = ""; }; @@ -6000,6 +6010,8 @@ 08DF9C431E8475530058678C /* test-image-portrait.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "test-image-portrait.jpg"; sourceTree = ""; }; 08E39B4428A3DEB200874CB8 /* UserPersistentStoreFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPersistentStoreFactory.swift; sourceTree = ""; }; 08E5CAD31E7B3A4500FAC71B /* WordPress 57.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "WordPress 57.xcdatamodel"; sourceTree = ""; }; + 08E6E07A2A4C3E3A00B807B0 /* CompliancePopoverViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompliancePopoverViewController.swift; sourceTree = ""; }; + 08E6E07D2A4C405500B807B0 /* CompliancePopoverViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompliancePopoverViewModel.swift; sourceTree = ""; }; 08E77F441EE87FCF006F9515 /* MediaThumbnailExporter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaThumbnailExporter.swift; sourceTree = ""; }; 08E77F461EE9D72F006F9515 /* MediaThumbnailExporterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaThumbnailExporterTests.swift; sourceTree = ""; }; 08EA036629C9B51200B72A87 /* Color+DesignSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+DesignSystem.swift"; sourceTree = ""; }; @@ -9841,6 +9853,18 @@ path = "Feature Highlight"; sourceTree = ""; }; + 083ED8CA2A4322A7007F89B3 /* EEUUSCompliance */ = { + isa = PBXGroup; + children = ( + 086C117B2A2F6451004A3821 /* CompliancePopover.swift */, + 083ED8CB2A4322CB007F89B3 /* ComplianceLocationService.swift */, + 08E6E07A2A4C3E3A00B807B0 /* CompliancePopoverViewController.swift */, + 08E6E07D2A4C405500B807B0 /* CompliancePopoverViewModel.swift */, + 0840513D2A4DDE3400A596E6 /* CompliancePopoverCoordinator.swift */, + ); + path = EEUUSCompliance; + sourceTree = ""; + }; 084FC3B529913B0A00A17BCF /* JetpackOverlay */ = { isa = PBXGroup; children = ( @@ -10079,7 +10103,6 @@ isa = PBXGroup; children = ( 1717139E265FE59700F3A022 /* ButtonStyles.swift */, - 086C117B2A2F6451004A3821 /* CompliancePopover.swift */, ); path = "Reusable SwiftUI Views"; sourceTree = ""; @@ -13464,6 +13487,7 @@ C533CF320E6D3AB3000C3DE8 /* Comments */, F1C740BD26B18DEA005D0809 /* Developer */, 173BCE711CEB365400AE8817 /* Domains */, + 083ED8CA2A4322A7007F89B3 /* EEUUSCompliance */, 081E4B4A281BFB520085E89C /* Feature Highlight */, 98AA9F1F27EA888C00B3A98C /* Feature Introduction */, 7E3E9B6E2177C9C300FD5797 /* Gutenberg */, @@ -21041,6 +21065,7 @@ 93C486511810445D00A24725 /* ActivityLogViewController.m in Sources */, 9A162F2521C26F5F00FDC035 /* UIViewController+ChildViewController.swift in Sources */, 086C4D101E81F9240011D960 /* Media+Blog.swift in Sources */, + 08E6E07E2A4C405500B807B0 /* CompliancePopoverViewModel.swift in Sources */, 088D58A529E724F300E6C0F4 /* ColorGallery.swift in Sources */, 08216FCB1CDBF96000304BA7 /* MenuItemEditingHeaderView.m in Sources */, 17BD4A0820F76A4700975AC3 /* Routes+Banners.swift in Sources */, @@ -21828,6 +21853,7 @@ 986C90882231AD6200FC31E1 /* PostStatsViewModel.swift in Sources */, FAFC064E27D2360B002F0483 /* QuickStartCell.swift in Sources */, 9A4A8F4B235758EF00088CE4 /* StatsStore+Cache.swift in Sources */, + 083ED8CC2A4322CB007F89B3 /* ComplianceLocationService.swift in Sources */, BE87E1A21BD405790075D45B /* WP3DTouchShortcutHandler.swift in Sources */, 7E3E7A5D20E44DB00075D159 /* BadgeContentStyles.swift in Sources */, 5D4E30D11AA4B41A000D9904 /* WPStyleGuide+Pages.m in Sources */, @@ -22133,6 +22159,7 @@ 9F74696B209EFD0C0074D52B /* CheckmarkTableViewCell.swift in Sources */, 03216ECC27995F3500D444CA /* SchedulingViewControllerPresenter.swift in Sources */, F52CACCA244FA7AA00661380 /* ReaderManageScenePresenter.swift in Sources */, + 0840513E2A4DDE3400A596E6 /* CompliancePopoverCoordinator.swift in Sources */, FF5371631FDFF64F00619A3F /* MediaService.swift in Sources */, 40C403F52215D66A00E8C894 /* TopViewedPostStatsRecordValue+CoreDataClass.swift in Sources */, 40ADB15520686870009A9161 /* PluginStore+Persistence.swift in Sources */, @@ -22293,6 +22320,7 @@ 403F57BC20E5CA6A004E889A /* RewindStatusRow.swift in Sources */, 4A9B81E32921AE03007A05D1 /* ContextManager.swift in Sources */, F5D0A64923C8FA1500B20D27 /* LinkBehavior.swift in Sources */, + 08E6E07B2A4C3E3A00B807B0 /* CompliancePopoverViewController.swift in Sources */, 80C523A429959DE000B1C14B /* BlazeWebViewController.swift in Sources */, E6DE44671B90D251000FA7EF /* ReaderHelpers.swift in Sources */, C7BB601C2863B3D600748FD9 /* QRLoginCameraPermissionsHandler.swift in Sources */, @@ -24277,6 +24305,7 @@ FABB22382602FC2C00C8785C /* EventLoggingDelegate.swift in Sources */, FABB22392602FC2C00C8785C /* TodayStatsRecordValue+CoreDataClass.swift in Sources */, FABB223B2602FC2C00C8785C /* AbstractPost+Searchable.swift in Sources */, + 08E6E07F2A4C405500B807B0 /* CompliancePopoverViewModel.swift in Sources */, FAA9084D27BD60710093FFA8 /* MySiteViewController+QuickStart.swift in Sources */, FABB223C2602FC2C00C8785C /* EditCommentViewController.m in Sources */, FABB223D2602FC2C00C8785C /* ThisWeekWidgetStats.swift in Sources */, @@ -24653,6 +24682,7 @@ FABB23462602FC2C00C8785C /* LoadingStatusView.swift in Sources */, FABB23472602FC2C00C8785C /* MenuDetailsViewController.m in Sources */, FABB23482602FC2C00C8785C /* InlineErrorRetryTableViewCell.swift in Sources */, + 08E6E07C2A4C3E3A00B807B0 /* CompliancePopoverViewController.swift in Sources */, FABB23492602FC2C00C8785C /* MenuItemSourceCell.m in Sources */, FABB234A2602FC2C00C8785C /* BlogDetailsViewController.m in Sources */, FABB234B2602FC2C00C8785C /* UIBarButtonItem+MeBarButton.swift in Sources */, @@ -24952,6 +24982,7 @@ FABB24332602FC2C00C8785C /* WindowManager.swift in Sources */, 08799C262A334645005317F7 /* Spacing.swift in Sources */, FABB24342602FC2C00C8785C /* NSFileManager+FolderSize.swift in Sources */, + 0840513F2A4DDE3400A596E6 /* CompliancePopoverCoordinator.swift in Sources */, FABB24352602FC2C00C8785C /* ErrorStateViewController.swift in Sources */, FABB24362602FC2C00C8785C /* WP3DTouchShortcutCreator.swift in Sources */, FABB24372602FC2C00C8785C /* GIFPlaybackStrategy.swift in Sources */, @@ -25232,6 +25263,7 @@ FABB24FB2602FC2C00C8785C /* ReaderListStreamHeader.swift in Sources */, FABB24FC2602FC2C00C8785C /* NoResultsViewController+MediaLibrary.swift in Sources */, FA4B203929A8C48F0089FE68 /* AbstractPost+Blaze.swift in Sources */, + 083ED8CD2A4322CB007F89B3 /* ComplianceLocationService.swift in Sources */, FABB24FD2602FC2C00C8785C /* StockPhotosDataLoader.swift in Sources */, 010459E729153FFF000C7778 /* JetpackNotificationMigrationService.swift in Sources */, FABB24FE2602FC2C00C8785C /* ReaderTopicToReaderTagTopic37to38.swift in Sources */, From a3e461454691e6c4bfb98720a6528cb068c7c91e Mon Sep 17 00:00:00 2001 From: alpavanoglu Date: Fri, 30 Jun 2023 17:48:49 +0200 Subject: [PATCH 02/14] Update CompliancePopoverViewModel --- .../EEUUSCompliance/CompliancePopover.swift | 6 ++-- .../CompliancePopoverViewModel.swift | 31 ++++++++++++++----- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopover.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopover.swift index e44967563d83..273ce4a28cf7 100644 --- a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopover.swift +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopover.swift @@ -5,8 +5,8 @@ struct CompliancePopover: View { var saveAction: (() -> ())? var shouldScroll: Bool = false - @State - private var isAnalyticsOn = true + @StateObject + var viewModel: CompliancePopoverViewModel var body: some View { if shouldScroll { @@ -42,7 +42,7 @@ struct CompliancePopover: View { } private var analyticsToggle: some View { - Toggle(Strings.toggleTitle, isOn: $isAnalyticsOn) + Toggle(Strings.toggleTitle, isOn: $viewModel.isAnalyticsEnabled) .foregroundColor(Color.DS.Foreground.primary) .toggleStyle(SwitchToggleStyle(tint: Color.DS.Background.brand)) .padding(.vertical, Length.Padding.single) diff --git a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift index 772f9faac9ca..6a669672599d 100644 --- a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift @@ -2,18 +2,33 @@ import Foundation import UIKit import WordPressUI -protocol CompliancePopoverViewModelProtocol { - func didTapSettings() - func didTapSave() -} +final class CompliancePopoverViewModel: ObservableObject { + @Published + var isAnalyticsEnabled: Bool = true + var coordinator: CompliancePopoverCoordinator? + + private let defaults: UserDefaults + + init(defaults: UserDefaults) { + self.defaults = defaults + } -final class CompliancePopoverViewModel: CompliancePopoverViewModelProtocol { func didTapSettings() { - print("** TAPPED SETTINGS **") - RootViewCoordinator.sharedPresenter.navigateToPrivacySettings() + coordinator?.navigateToSettings() + defaults.shouldShowCompliancePopup = true } func didTapSave() { - print("** TAPPED SAVE **") + let appAnalytics = WordPressAppDelegate.shared?.analytics + appAnalytics?.setUserHasOptedOut(!isAnalyticsEnabled) + + let context = ContextManager.shared.mainContext + guard let account = try? WPAccount.lookupDefaultWordPressComAccount(in: context) else { + return } + + let change = AccountSettingsChange.tracksOptOut(!isAnalyticsEnabled) + AccountSettingsService(userID: account.userID.intValue, api: account.wordPressComRestApi).saveChange(change) + coordinator?.dismiss() + defaults.shouldShowCompliancePopup = true } } From 73f247a972b986e69cc2706840719efc5e4ad3fa Mon Sep 17 00:00:00 2001 From: alpavanoglu Date: Fri, 30 Jun 2023 17:49:07 +0200 Subject: [PATCH 03/14] Move feature check logic to Coordinator --- .../CompliancePopoverCoordinator.swift | 35 +++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift index af1271dd3f09..56c178128ce3 100644 --- a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift @@ -2,7 +2,7 @@ import UIKit final class CompliancePopoverCoordinator { fileprivate enum Constants { - static let hasSavedPrivacyBannerSettingsKey = "hasSavedPrivacyBannerSettings" + static let shouldShowCompliancePopup = "shouldShowCompliancePopup" } private let viewController: UIViewController @@ -21,13 +21,15 @@ final class CompliancePopoverCoordinator { complianceService.getIPCountryCode { [weak self] result in switch result { case .success(let countryCode): + guard let self, self.shouldShowPrivacyBanner(countryCode: countryCode) else { + return + } DispatchQueue.main.async { - guard let self, self.shouldShowPrivacyBanner(countryCode: countryCode) else { - return - } - let complianceViewModel = CompliancePopoverViewModel() + let complianceViewModel = CompliancePopoverViewModel(defaults: self.defaults) + complianceViewModel.coordinator = self let complianceViewController = CompliancePopoverViewController(viewModel: complianceViewModel) let bottomSheetViewController = BottomSheetViewController(childViewController: complianceViewController, customHeaderSpacing: 0) + bottomSheetViewController.show(from: self.viewController) } case .failure(let error): @@ -38,8 +40,17 @@ final class CompliancePopoverCoordinator { func shouldShowPrivacyBanner(countryCode: String) -> Bool { let isCountryInEU = Self.gdprCountryCodes.contains(countryCode) - let hasSavedPrivacySettings = defaults.hasSavedPrivacyBannerSettings - return isCountryInEU && !hasSavedPrivacySettings + return isCountryInEU && !defaults.shouldShowCompliancePopup + } + + func navigateToSettings() { + viewController.dismiss(animated: true) { + RootViewCoordinator.sharedPresenter.navigateToPrivacySettings() + } + } + + func dismiss() { + viewController.dismiss(animated: true) } } @@ -82,8 +93,12 @@ private extension CompliancePopoverCoordinator { ] } -private extension UserDefaults { - @objc dynamic var hasSavedPrivacyBannerSettings: Bool { - bool(forKey: CompliancePopoverCoordinator.Constants.hasSavedPrivacyBannerSettingsKey) +extension UserDefaults { + @objc dynamic var shouldShowCompliancePopup: Bool { + get { + bool(forKey: CompliancePopoverCoordinator.Constants.shouldShowCompliancePopup) + } set { + set(newValue, forKey: CompliancePopoverCoordinator.Constants.shouldShowCompliancePopup) + } } } From b7ca691005c8fa7ebfc8e6759ff7f1ba5944926d Mon Sep 17 00:00:00 2001 From: alpavanoglu Date: Fri, 30 Jun 2023 17:49:27 +0200 Subject: [PATCH 04/14] Add popover closure assignments to VC --- .../CompliancePopoverViewController.swift | 60 ++++--------------- 1 file changed, 10 insertions(+), 50 deletions(-) diff --git a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewController.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewController.swift index 7c142a7b7d28..7f420667ca80 100644 --- a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewController.swift +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewController.swift @@ -2,55 +2,11 @@ import UIKit import SwiftUI import WordPressUI -//final class CompliancePopoverViewController: UIHostingController { -// -// private let viewModel: CompliancePopoverViewModelProtocol -// -// /// Tracks the banner view intrinsic height. -// /// Needed to enable it's scrolling when it grows bigger than the screen. -// /// -// var bannerIntrinsicHeight: CGFloat = 0 -// -// init(viewModel: CompliancePopoverViewModelProtocol) { -// self.viewModel = viewModel -// super.init(rootView: CompliancePopover()) -// } -// -// override func viewDidLoad() { -// super.viewDidLoad() -// rootView.goToSettingsAction = { [weak self] in -// self?.viewModel.didTapSettings() -// } -// -// rootView.saveAction = { [weak self] in -// self?.viewModel.didTapSave() -// } -// } -// -// /// Needed for protocol conformance. -// /// -// required dynamic init?(coder aDecoder: NSCoder) { -// fatalError("init(coder:) has not been implemented") -// } -// -//// override func viewDidLayoutSubviews() { -//// super.viewDidLayoutSubviews() -//// -//// print("Kill me right now") -//// // Make the banner scrollable when the banner height is bigger than the screen height. -//// // Send it in the next run loop to avoid a recursive `viewDidLayoutSubviews`. -//// bannerIntrinsicHeight = view.intrinsicContentSize.height -//// DispatchQueue.main.async { -//// self.rootView.shouldScroll = self.bannerIntrinsicHeight > self.view.frame.height -//// } -//// } -//} - final class CompliancePopoverViewController: UIViewController { // MARK: - Views - private let hostingController: UIViewController = { - let controller = UIHostingController(rootView: CompliancePopover()) + private lazy var hostingController: UIHostingController = { + let controller = UIHostingController(rootView: CompliancePopover(viewModel: self.viewModel)) controller.view.translatesAutoresizingMaskIntoConstraints = true return controller }() @@ -59,9 +15,9 @@ final class CompliancePopoverViewController: UIViewController { return hostingController.view } - private let viewModel: CompliancePopoverViewModelProtocol + private let viewModel: CompliancePopoverViewModel - init(viewModel: CompliancePopoverViewModelProtocol) { + init(viewModel: CompliancePopoverViewModel) { self.viewModel = viewModel super.init(nibName: nil, bundle: nil) } @@ -74,6 +30,12 @@ final class CompliancePopoverViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() self.addContentView() + hostingController.rootView.goToSettingsAction = { + self.viewModel.didTapSettings() + } + hostingController.rootView.saveAction = { + self.viewModel.didTapSave() + } } override func viewDidLayoutSubviews() { @@ -102,7 +64,6 @@ extension CompliancePopoverViewController: DrawerPresentable { } var allowsUserTransition: Bool { - print("89fas9(*") return false } @@ -111,7 +72,6 @@ extension CompliancePopoverViewController: DrawerPresentable { } var allowsTapToDismiss: Bool { - print("AD*SJ*(") return false } } From cc3f84ad13baf611ab869b94e2c3b49bf5002019 Mon Sep 17 00:00:00 2001 From: alpavanoglu Date: Wed, 5 Jul 2023 11:42:55 +0200 Subject: [PATCH 05/14] Add scroll functionality for dynamic fonts --- .../EEUUSCompliance/CompliancePopover.swift | 14 ++++++++++++-- .../CompliancePopoverCoordinator.swift | 2 +- .../CompliancePopoverViewController.swift | 6 ++++++ .../CompliancePopoverViewModel.swift | 15 ++++++++++----- 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopover.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopover.swift index 273ce4a28cf7..bf6bae402498 100644 --- a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopover.swift +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopover.swift @@ -1,17 +1,27 @@ import SwiftUI struct CompliancePopover: View { + private enum Constants { + static let verticalScrollBuffer = Length.Padding.large + } + var goToSettingsAction: (() -> ())? var saveAction: (() -> ())? var shouldScroll: Bool = false + var screenHeight: CGFloat = 0 @StateObject var viewModel: CompliancePopoverViewModel var body: some View { if shouldScroll { - ScrollView(showsIndicators: false) { - contentVStack + GeometryReader { reader in + ScrollView(showsIndicators: false) { + contentVStack + // Fixes the issue of scroll view content size not sizing properly. + // Without this, on large dynamic fonts, the view is not properly scrollable. + Spacer().frame(height: reader.size.height - screenHeight + Constants.verticalScrollBuffer) + } } } else { contentVStack diff --git a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift index 56c178128ce3..35aa430957bc 100644 --- a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift @@ -40,7 +40,7 @@ final class CompliancePopoverCoordinator { func shouldShowPrivacyBanner(countryCode: String) -> Bool { let isCountryInEU = Self.gdprCountryCodes.contains(countryCode) - return isCountryInEU && !defaults.shouldShowCompliancePopup + return isCountryInEU && defaults.shouldShowCompliancePopup } func navigateToSettings() { diff --git a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewController.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewController.swift index 7f420667ca80..005b0a9f229a 100644 --- a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewController.swift +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewController.swift @@ -16,6 +16,7 @@ final class CompliancePopoverViewController: UIViewController { } private let viewModel: CompliancePopoverViewModel + private var bannerIntrinsicHeight: CGFloat = 0 init(viewModel: CompliancePopoverViewModel) { self.viewModel = viewModel @@ -44,6 +45,11 @@ final class CompliancePopoverViewController: UIViewController { let contentViewSize = contentView.systemLayoutSizeFitting(targetSize) self.contentView.frame = .init(origin: .zero, size: contentViewSize) self.preferredContentSize = contentView.bounds.size + + DispatchQueue.main.async { + self.hostingController.rootView.screenHeight = self.view.frame.height + self.hostingController.rootView.shouldScroll = (contentViewSize.height + 100) > self.view.frame.height + } } private func addContentView() { diff --git a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift index 6a669672599d..71b25514e677 100644 --- a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift @@ -15,20 +15,25 @@ final class CompliancePopoverViewModel: ObservableObject { func didTapSettings() { coordinator?.navigateToSettings() - defaults.shouldShowCompliancePopup = true + defaults.shouldShowCompliancePopup = false } func didTapSave() { let appAnalytics = WordPressAppDelegate.shared?.analytics appAnalytics?.setUserHasOptedOut(!isAnalyticsEnabled) - let context = ContextManager.shared.mainContext - guard let account = try? WPAccount.lookupDefaultWordPressComAccount(in: context) else { - return } + let account = ContextManager.shared.performQuery { context -> WPAccount? in + let account = try? WPAccount.lookupDefaultWordPressComAccount(in: context) + return account + } + + guard let account else { + return + } let change = AccountSettingsChange.tracksOptOut(!isAnalyticsEnabled) AccountSettingsService(userID: account.userID.intValue, api: account.wordPressComRestApi).saveChange(change) coordinator?.dismiss() - defaults.shouldShowCompliancePopup = true + defaults.shouldShowCompliancePopup = false } } From 93cd7a19f51f658780124c5ebcad5726bde7a18c Mon Sep 17 00:00:00 2001 From: alpavanoglu Date: Wed, 5 Jul 2023 12:08:01 +0200 Subject: [PATCH 06/14] Update default value for the toggle to get value from WPAppAnalytics --- .../EEUUSCompliance/CompliancePopoverViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift index 71b25514e677..3f7839a7d573 100644 --- a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift @@ -4,7 +4,7 @@ import WordPressUI final class CompliancePopoverViewModel: ObservableObject { @Published - var isAnalyticsEnabled: Bool = true + var isAnalyticsEnabled: Bool = !WPAppAnalytics.userHasOptedOut() var coordinator: CompliancePopoverCoordinator? private let defaults: UserDefaults From 28d66b047db3ed83364251840dac0082e12df4c2 Mon Sep 17 00:00:00 2001 From: alpavanoglu Date: Thu, 6 Jul 2023 00:09:53 +0200 Subject: [PATCH 07/14] Update podfile to target WordpressKit 8.5.0-beta.3 --- Podfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Podfile b/Podfile index aeddcaa929a6..c6d5efa87fcd 100644 --- a/Podfile +++ b/Podfile @@ -50,7 +50,7 @@ def wordpress_ui end def wordpress_kit - pod 'WordPressKit', '~> 8.4-beta.3' + pod 'WordPressKit', '~> 8.5.0-beta.3' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', tag: '' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', branch: '' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', commit: '' From a98a0faea8134a3eb98370b42e31dad1fc06371f Mon Sep 17 00:00:00 2001 From: Hassaan El-Garem Date: Thu, 6 Jul 2023 02:58:35 +0300 Subject: [PATCH 08/14] Update: run `rake dependencies` to properly update `Podfile.lock` --- Podfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Podfile.lock b/Podfile.lock index 4aaf93215397..e7387b5bcede 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -512,7 +512,7 @@ PODS: - WordPressKit (~> 8.0-beta) - WordPressShared (~> 2.1-beta) - WordPressUI (~> 1.7-beta) - - WordPressKit (8.4.0): + - WordPressKit (8.5.0-beta.3): - Alamofire (~> 4.8.0) - NSObject-SafeExpectations (~> 0.0.4) - UIDeviceIdentifier (~> 2.0) @@ -612,7 +612,7 @@ DEPENDENCIES: - SwiftLint (~> 0.50) - WordPress-Editor-iOS (~> 1.19.8) - WordPressAuthenticator (~> 6.1-beta) - - WordPressKit (~> 8.4-beta.3) + - WordPressKit (~> 8.5.0-beta.3) - WordPressShared (~> 2.2-beta) - WordPressUI (~> 1.12.5) - WPMediaPicker (~> 1.8-beta) @@ -880,7 +880,7 @@ SPEC CHECKSUMS: WordPress-Aztec-iOS: 7d11d598f14c82c727c08b56bd35fbeb7dafb504 WordPress-Editor-iOS: 9eb9f12f21a5209cb837908d81ffe1e31cb27345 WordPressAuthenticator: b0b900696de5129a215adcd1e9ae6eb89da36ac8 - WordPressKit: 66e1411f48503b3ff3ab2c7db82590e436097882 + WordPressKit: 29a0cd0cfbddc8b4bfbe076e0d29b258ba8daa6a WordPressShared: 87f3ee89b0a3e83106106f13a8b71605fb8eb6d2 WordPressUI: c5be816f6c7b3392224ac21de9e521e89fa108ac WPMediaPicker: 0d40b8d66b6dfdaa2d6a41e3be51249ff5898775 @@ -895,6 +895,6 @@ SPEC CHECKSUMS: ZendeskSupportSDK: 3a8e508ab1d9dd22dc038df6c694466414e037ba ZIPFoundation: ae5b4b813d216d3bf0a148773267fff14bd51d37 -PODFILE CHECKSUM: df7dcee5d41f8627da2633111de057f07ee5bf55 +PODFILE CHECKSUM: 7be6a8ed524025ed4acf2454718cab08a0b9e310 COCOAPODS: 1.12.1 From ed3dd44edeb076e5b5b79f6402343f5dbe215e7e Mon Sep 17 00:00:00 2001 From: Hassaan El-Garem Date: Thu, 6 Jul 2023 03:00:02 +0300 Subject: [PATCH 09/14] Tests: Fix failing blaze test --- .../WordPressTest/Dashboard/BlazeCampaignViewModelTests.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/WordPress/WordPressTest/Dashboard/BlazeCampaignViewModelTests.swift b/WordPress/WordPressTest/Dashboard/BlazeCampaignViewModelTests.swift index ce1fd73c4b62..6c9651a91d22 100644 --- a/WordPress/WordPressTest/Dashboard/BlazeCampaignViewModelTests.swift +++ b/WordPress/WordPressTest/Dashboard/BlazeCampaignViewModelTests.swift @@ -29,6 +29,7 @@ private let campaign: BlazeCampaign = { "start_date": "2023-06-13T00:00:00Z", "end_date": "2023-06-01T19:15:45Z", "status": "finished", + "ui_status": "finished", "avatar_url": "https://0.gravatar.com/avatar/614d27bcc21db12e7c49b516b4750387?s=96&d=identicon&r=G", "budget_cents": 500, "target_url": "https://alextest9123.wordpress.com/2023/06/01/test-post/", From 7c86df4eefc044e2ce3932f2c2155c7bd6c5c4bb Mon Sep 17 00:00:00 2001 From: alpavanoglu Date: Thu, 6 Jul 2023 11:39:01 +0200 Subject: [PATCH 10/14] Address change requests --- Podfile | 2 +- Podfile.lock | 4 +-- .../ComplianceLocationService.swift | 9 +---- .../EEUUSCompliance/CompliancePopover.swift | 10 ++---- .../CompliancePopoverCoordinator.swift | 34 +++++++++---------- .../CompliancePopoverViewController.swift | 14 +++----- .../CompliancePopoverViewModel.swift | 12 +++---- 7 files changed, 34 insertions(+), 51 deletions(-) diff --git a/Podfile b/Podfile index c6d5efa87fcd..c3047244de94 100644 --- a/Podfile +++ b/Podfile @@ -50,7 +50,7 @@ def wordpress_ui end def wordpress_kit - pod 'WordPressKit', '~> 8.5.0-beta.3' + pod 'WordPressKit', '~> 8.5-beta.3' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', tag: '' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', branch: '' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', commit: '' diff --git a/Podfile.lock b/Podfile.lock index e7387b5bcede..2e989e6cd297 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -612,7 +612,7 @@ DEPENDENCIES: - SwiftLint (~> 0.50) - WordPress-Editor-iOS (~> 1.19.8) - WordPressAuthenticator (~> 6.1-beta) - - WordPressKit (~> 8.5.0-beta.3) + - WordPressKit (~> 8.5-beta.3) - WordPressShared (~> 2.2-beta) - WordPressUI (~> 1.12.5) - WPMediaPicker (~> 1.8-beta) @@ -895,6 +895,6 @@ SPEC CHECKSUMS: ZendeskSupportSDK: 3a8e508ab1d9dd22dc038df6c694466414e037ba ZIPFoundation: ae5b4b813d216d3bf0a148773267fff14bd51d37 -PODFILE CHECKSUM: 7be6a8ed524025ed4acf2454718cab08a0b9e310 +PODFILE CHECKSUM: 66f009c76957bfeaeeb56fd48cb877edb372951c COCOAPODS: 1.12.1 diff --git a/WordPress/Classes/ViewRelated/EEUUSCompliance/ComplianceLocationService.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/ComplianceLocationService.swift index a5f2cd4350a9..78abee732725 100644 --- a/WordPress/Classes/ViewRelated/EEUUSCompliance/ComplianceLocationService.swift +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/ComplianceLocationService.swift @@ -2,13 +2,6 @@ import WordPressKit final class ComplianceLocationService { func getIPCountryCode(completion: @escaping (Result) -> Void) { - IPLocationRemote().fetchIPCountryCode { result in - switch result { - case .success(let countryCode): - completion(.success(countryCode)) - case .failure(let error): - completion(.failure(error)) - } - } + IPLocationRemote().fetchIPCountryCode(completion: completion) } } diff --git a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopover.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopover.swift index bf6bae402498..6c1a2380e457 100644 --- a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopover.swift +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopover.swift @@ -111,10 +111,7 @@ private enum Strings { static let subtitle = NSLocalizedString( "compliance.analytics.popover.subtitle", - value: """ - We process your personal data to optimize our website and - marketing activities based on your consent and our legitimate interest. - """, + value: "We process your personal data to optimize our website and marketing activities based on your consent and our legitimate interest.", comment: "Subtitle for the privacy compliance popover." ) @@ -126,10 +123,7 @@ private enum Strings { static let footnote = NSLocalizedString( "compliance.analytics.popover.footnote", - value: """ - These cookies allow us to optimize performance by collecting - information on how users interact with our websites. - """, + value: "These cookies allow us to optimize performance by collecting information on how users interact with our websites.", comment: "Footnote for the privacy compliance popover." ) diff --git a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift index 35aa430957bc..c452386ddf31 100644 --- a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift @@ -1,9 +1,6 @@ import UIKit final class CompliancePopoverCoordinator { - fileprivate enum Constants { - static let shouldShowCompliancePopup = "shouldShowCompliancePopup" - } private let viewController: UIViewController private let complianceService = ComplianceLocationService() @@ -19,28 +16,20 @@ final class CompliancePopoverCoordinator { return } complianceService.getIPCountryCode { [weak self] result in - switch result { - case .success(let countryCode): + if case .success(let countryCode) = result { guard let self, self.shouldShowPrivacyBanner(countryCode: countryCode) else { return } DispatchQueue.main.async { - let complianceViewModel = CompliancePopoverViewModel(defaults: self.defaults) - complianceViewModel.coordinator = self - let complianceViewController = CompliancePopoverViewController(viewModel: complianceViewModel) - let bottomSheetViewController = BottomSheetViewController(childViewController: complianceViewController, customHeaderSpacing: 0) - - bottomSheetViewController.show(from: self.viewController) + self.presentPopover() } - case .failure(let error): - () } } } func shouldShowPrivacyBanner(countryCode: String) -> Bool { let isCountryInEU = Self.gdprCountryCodes.contains(countryCode) - return isCountryInEU && defaults.shouldShowCompliancePopup + return isCountryInEU && !defaults.didShowCompliancePopup } func navigateToSettings() { @@ -52,6 +41,15 @@ final class CompliancePopoverCoordinator { func dismiss() { viewController.dismiss(animated: true) } + + private func presentPopover() { + let complianceViewModel = CompliancePopoverViewModel(defaults: self.defaults) + complianceViewModel.coordinator = self + let complianceViewController = CompliancePopoverViewController(viewModel: complianceViewModel) + let bottomSheetViewController = BottomSheetViewController(childViewController: complianceViewController, customHeaderSpacing: 0) + + bottomSheetViewController.show(from: self.viewController) + } } private extension CompliancePopoverCoordinator { @@ -94,11 +92,13 @@ private extension CompliancePopoverCoordinator { } extension UserDefaults { - @objc dynamic var shouldShowCompliancePopup: Bool { + static let didShowCompliancePopupKey = "didShowCompliancePopup" + + @objc var didShowCompliancePopup: Bool { get { - bool(forKey: CompliancePopoverCoordinator.Constants.shouldShowCompliancePopup) + bool(forKey: Self.didShowCompliancePopupKey) } set { - set(newValue, forKey: CompliancePopoverCoordinator.Constants.shouldShowCompliancePopup) + set(newValue, forKey: Self.didShowCompliancePopupKey) } } } diff --git a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewController.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewController.swift index 005b0a9f229a..9a43c70eacd6 100644 --- a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewController.swift +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewController.swift @@ -5,11 +5,7 @@ import WordPressUI final class CompliancePopoverViewController: UIViewController { // MARK: - Views - private lazy var hostingController: UIHostingController = { - let controller = UIHostingController(rootView: CompliancePopover(viewModel: self.viewModel)) - controller.view.translatesAutoresizingMaskIntoConstraints = true - return controller - }() + private let hostingController: UIHostingController private var contentView: UIView { return hostingController.view @@ -20,6 +16,7 @@ final class CompliancePopoverViewController: UIViewController { init(viewModel: CompliancePopoverViewModel) { self.viewModel = viewModel + hostingController = UIHostingController(rootView: CompliancePopover(viewModel: self.viewModel)) super.init(nibName: nil, bundle: nil) } @@ -31,6 +28,7 @@ final class CompliancePopoverViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() self.addContentView() + hostingController.view.translatesAutoresizingMaskIntoConstraints = true hostingController.rootView.goToSettingsAction = { self.viewModel.didTapSettings() } @@ -46,10 +44,8 @@ final class CompliancePopoverViewController: UIViewController { self.contentView.frame = .init(origin: .zero, size: contentViewSize) self.preferredContentSize = contentView.bounds.size - DispatchQueue.main.async { - self.hostingController.rootView.screenHeight = self.view.frame.height - self.hostingController.rootView.shouldScroll = (contentViewSize.height + 100) > self.view.frame.height - } + self.hostingController.rootView.screenHeight = self.view.frame.height + self.hostingController.rootView.shouldScroll = (contentViewSize.height + 100) > self.view.frame.height } private func addContentView() { diff --git a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift index 3f7839a7d573..e87a688e3120 100644 --- a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift @@ -15,25 +15,25 @@ final class CompliancePopoverViewModel: ObservableObject { func didTapSettings() { coordinator?.navigateToSettings() - defaults.shouldShowCompliancePopup = false + defaults.didShowCompliancePopup = true } func didTapSave() { let appAnalytics = WordPressAppDelegate.shared?.analytics appAnalytics?.setUserHasOptedOut(!isAnalyticsEnabled) - let account = ContextManager.shared.performQuery { context -> WPAccount? in + let (accountID, restAPI) = ContextManager.shared.performQuery { context -> (NSNumber?, WordPressComRestApi?) in let account = try? WPAccount.lookupDefaultWordPressComAccount(in: context) - return account + return (account?.userID, account?.wordPressComRestApi) } - guard let account else { + guard let accountID, let restAPI else { return } let change = AccountSettingsChange.tracksOptOut(!isAnalyticsEnabled) - AccountSettingsService(userID: account.userID.intValue, api: account.wordPressComRestApi).saveChange(change) + AccountSettingsService(userID: accountID.intValue, api: restAPI).saveChange(change) coordinator?.dismiss() - defaults.shouldShowCompliancePopup = false + defaults.didShowCompliancePopup = true } } From 4dec92ef829e11ba53b34a6833270e2f0438d035 Mon Sep 17 00:00:00 2001 From: alpavanoglu Date: Thu, 6 Jul 2023 13:35:14 +0200 Subject: [PATCH 11/14] Add `CompliancePopoverViewModelTests` --- .../CompliancePopoverCoordinator.swift | 22 +++-- .../CompliancePopoverViewModel.swift | 8 +- WordPress/WordPress.xcodeproj/project.pbxproj | 12 +++ WordPress/WordPressTest/AccountBuilder.swift | 6 ++ .../CompliancePopoverViewModelTests.swift | 87 +++++++++++++++++++ 5 files changed, 125 insertions(+), 10 deletions(-) create mode 100644 WordPress/WordPressTest/EUUSCompliance/CompliancePopoverViewModelTests.swift diff --git a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift index c452386ddf31..b15871acd7a3 100644 --- a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift @@ -1,7 +1,12 @@ import UIKit -final class CompliancePopoverCoordinator { +protocol CompliancePopoverCoordinatorProtocol { + func presentIfNeeded() + func navigateToSettings() + func dismiss() +} +final class CompliancePopoverCoordinator: CompliancePopoverCoordinatorProtocol { private let viewController: UIViewController private let complianceService = ComplianceLocationService() private let defaults: UserDefaults @@ -27,11 +32,6 @@ final class CompliancePopoverCoordinator { } } - func shouldShowPrivacyBanner(countryCode: String) -> Bool { - let isCountryInEU = Self.gdprCountryCodes.contains(countryCode) - return isCountryInEU && !defaults.didShowCompliancePopup - } - func navigateToSettings() { viewController.dismiss(animated: true) { RootViewCoordinator.sharedPresenter.navigateToPrivacySettings() @@ -42,8 +42,16 @@ final class CompliancePopoverCoordinator { viewController.dismiss(animated: true) } + private func shouldShowPrivacyBanner(countryCode: String) -> Bool { + let isCountryInEU = Self.gdprCountryCodes.contains(countryCode) + return isCountryInEU && !defaults.didShowCompliancePopup + } + private func presentPopover() { - let complianceViewModel = CompliancePopoverViewModel(defaults: self.defaults) + let complianceViewModel = CompliancePopoverViewModel( + defaults: defaults, + contextManager: ContextManager.shared + ) complianceViewModel.coordinator = self let complianceViewController = CompliancePopoverViewController(viewModel: complianceViewModel) let bottomSheetViewController = BottomSheetViewController(childViewController: complianceViewController, customHeaderSpacing: 0) diff --git a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift index e87a688e3120..08b024e16435 100644 --- a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift @@ -5,12 +5,14 @@ import WordPressUI final class CompliancePopoverViewModel: ObservableObject { @Published var isAnalyticsEnabled: Bool = !WPAppAnalytics.userHasOptedOut() - var coordinator: CompliancePopoverCoordinator? + var coordinator: CompliancePopoverCoordinatorProtocol? private let defaults: UserDefaults + private let contextManager: ContextManager - init(defaults: UserDefaults) { + init(defaults: UserDefaults, contextManager: ContextManager) { self.defaults = defaults + self.contextManager = contextManager } func didTapSettings() { @@ -22,7 +24,7 @@ final class CompliancePopoverViewModel: ObservableObject { let appAnalytics = WordPressAppDelegate.shared?.analytics appAnalytics?.setUserHasOptedOut(!isAnalyticsEnabled) - let (accountID, restAPI) = ContextManager.shared.performQuery { context -> (NSNumber?, WordPressComRestApi?) in + let (accountID, restAPI) = contextManager.performQuery { context -> (NSNumber?, WordPressComRestApi?) in let account = try? WPAccount.lookupDefaultWordPressComAccount(in: context) return (account?.userID, account?.wordPressComRestApi) } diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 3cb96d66e4e2..afa2b53ffc11 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -266,6 +266,7 @@ 087EBFA81F02313E001F7ACE /* MediaThumbnailService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 087EBFA71F02313E001F7ACE /* MediaThumbnailService.swift */; }; 0880BADC29ED6FF3002D3AB0 /* UIColor+DesignSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0880BADB29ED6FF3002D3AB0 /* UIColor+DesignSystem.swift */; }; 0880BADD29ED6FF3002D3AB0 /* UIColor+DesignSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0880BADB29ED6FF3002D3AB0 /* UIColor+DesignSystem.swift */; }; + 088134FF2A56C5240027C086 /* CompliancePopoverViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 088134FE2A56C5240027C086 /* CompliancePopoverViewModelTests.swift */; }; 0885A3671E837AFE00619B4D /* URLIncrementalFilenameTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0885A3661E837AFE00619B4D /* URLIncrementalFilenameTests.swift */; }; 088B89891DA6F93B000E8DEF /* ReaderPostCardContentLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 088B89881DA6F93B000E8DEF /* ReaderPostCardContentLabel.swift */; }; 088CC594282BEC41007B9421 /* TooltipPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 088CC593282BEC41007B9421 /* TooltipPresenter.swift */; }; @@ -5949,6 +5950,7 @@ 0879FC151E9301DD00E1EFC8 /* MediaTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaTests.swift; sourceTree = ""; }; 087EBFA71F02313E001F7ACE /* MediaThumbnailService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaThumbnailService.swift; sourceTree = ""; }; 0880BADB29ED6FF3002D3AB0 /* UIColor+DesignSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+DesignSystem.swift"; sourceTree = ""; }; + 088134FE2A56C5240027C086 /* CompliancePopoverViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompliancePopoverViewModelTests.swift; sourceTree = ""; }; 0885A3661E837AFE00619B4D /* URLIncrementalFilenameTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLIncrementalFilenameTests.swift; sourceTree = ""; }; 088B89881DA6F93B000E8DEF /* ReaderPostCardContentLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReaderPostCardContentLabel.swift; sourceTree = ""; }; 088CC593282BEC41007B9421 /* TooltipPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TooltipPresenter.swift; sourceTree = ""; }; @@ -9887,6 +9889,14 @@ name = Media; sourceTree = ""; }; + 088134FD2A56C5020027C086 /* EUUSCompliance */ = { + isa = PBXGroup; + children = ( + 088134FE2A56C5240027C086 /* CompliancePopoverViewModelTests.swift */, + ); + path = EUUSCompliance; + sourceTree = ""; + }; 089F087F1CE25D30009909F2 /* Controllers */ = { isa = PBXGroup; children = ( @@ -15589,6 +15599,7 @@ BE20F5E11B2F738E0020694C /* ViewRelated */ = { isa = PBXGroup; children = ( + 088134FD2A56C5020027C086 /* EUUSCompliance */, 3F28CEAD2A4ACEA400B79686 /* Me */, 0141929E2983F5D900CAEDB0 /* Support */, F41E4E8F28F1949D001880C6 /* App Icons */, @@ -23400,6 +23411,7 @@ 08F8CD3B1EBD2D020049D0C0 /* MediaURLExporterTests.swift in Sources */, D81C2F6220F89632002AE1F1 /* EditCommentActionTests.swift in Sources */, AEE0828A2681C23C00DCF54B /* GutenbergRefactoredGalleryUploadProcessorTests.swift in Sources */, + 088134FF2A56C5240027C086 /* CompliancePopoverViewModelTests.swift in Sources */, F4426FD9287F02FD00218003 /* SiteSuggestionsServiceMock.swift in Sources */, E66969CD1B9E2EBF00EC9C00 /* SafeReaderTopicToReaderTopic.m in Sources */, DC3B9B2F27739887003F7249 /* TimeZoneSelectorViewModelTests.swift in Sources */, diff --git a/WordPress/WordPressTest/AccountBuilder.swift b/WordPress/WordPressTest/AccountBuilder.swift index d54f14dc6d16..3f9262916b65 100644 --- a/WordPress/WordPressTest/AccountBuilder.swift +++ b/WordPress/WordPressTest/AccountBuilder.swift @@ -49,6 +49,12 @@ class AccountBuilder: NSObject { return self } + @objc + func with(authToken: String) -> AccountBuilder { + account.authToken = authToken + return self + } + @objc @discardableResult func build() -> WPAccount { diff --git a/WordPress/WordPressTest/EUUSCompliance/CompliancePopoverViewModelTests.swift b/WordPress/WordPressTest/EUUSCompliance/CompliancePopoverViewModelTests.swift new file mode 100644 index 000000000000..b22ba697d3a0 --- /dev/null +++ b/WordPress/WordPressTest/EUUSCompliance/CompliancePopoverViewModelTests.swift @@ -0,0 +1,87 @@ +import XCTest +@testable import WordPress + +final class CompliancePopoverViewModelTests: CoreDataTestCase { + let testDefaults = UserDefaults(suiteName: "compliance-popover-view-model-tests") + + override func setUp() { + super.setUp() + testDefaults?.removeObject(forKey: UserDefaults.didShowCompliancePopupKey) + + let windowManager = WindowManager(window: UIWindow()) + WordPressAuthenticationManager( + windowManager: windowManager, + remoteFeaturesStore: RemoteFeatureFlagStore() + ).initializeWordPressAuthenticator() + } + + override func tearDown() { + super.tearDown() + testDefaults?.removeObject(forKey: UserDefaults.didShowCompliancePopupKey) + } + + func testDidTapSettingsUpdatesDefaults() { + guard let defaults = try? XCTUnwrap(testDefaults) else { + return + } + let sut = CompliancePopoverViewModel(defaults: defaults, contextManager: contextManager) + sut.didTapSettings() + XCTAssert(defaults.didShowCompliancePopup) + } + + func testDidTapSettingsInvokesCoordinatorNavigation() { + guard let defaults = try? XCTUnwrap(testDefaults) else { + return + } + let mockCoordinator = MockCompliancePopoverCoordinator() + let sut = CompliancePopoverViewModel(defaults: defaults, contextManager: contextManager) + sut.coordinator = mockCoordinator + sut.didTapSettings() + XCTAssertEqual(mockCoordinator.navigateToSettingsCallCount, 1) + } + + func testDidTapSaveInvokesDismissWhenAccountIDExists() { + guard let defaults = try? XCTUnwrap(testDefaults) else { + return + } + let mockCoordinator = MockCompliancePopoverCoordinator() + let sut = CompliancePopoverViewModel( + defaults: defaults, + contextManager: makeCoreDataStack() + ) + sut.coordinator = mockCoordinator + sut.didTapSave() + XCTAssertEqual(mockCoordinator.dismissCallCount, 1) + XCTAssert(defaults.didShowCompliancePopup) + } + + private func makeCoreDataStack() -> ContextManager { + let contextManager = ContextManager.forTesting() + let account = AccountBuilder(contextManager) + .with(id: 1229) + .with(username: "foobar") + .with(email: "foo@automattic.com") + .with(authToken: "9384rj398t34j98") + .build() + UserSettings.defaultDotComUUID = account.uuid + return contextManager + } +} + +private class MockCompliancePopoverCoordinator: CompliancePopoverCoordinatorProtocol { + var navigateToSettingsCallCount = 0 + var presentIfNeededCallCount = 0 + var dismissCallCount = 0 + + func presentIfNeeded() { + presentIfNeededCallCount += 1 + } + + func navigateToSettings() { + navigateToSettingsCallCount += 1 + } + + func dismiss() { + dismissCallCount += 1 + } +} From 7570461594a74be471e732f4e8ecaddfdc0cf251 Mon Sep 17 00:00:00 2001 From: alpavanoglu Date: Fri, 7 Jul 2023 18:51:50 +0200 Subject: [PATCH 12/14] Apply change requests --- .../CompliancePopoverCoordinator.swift | 2 +- .../CompliancePopoverViewModel.swift | 2 +- .../CompliancePopoverViewModelTests.swift | 34 +++++++------------ 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift index b15871acd7a3..f679555b3335 100644 --- a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverCoordinator.swift @@ -102,7 +102,7 @@ private extension CompliancePopoverCoordinator { extension UserDefaults { static let didShowCompliancePopupKey = "didShowCompliancePopup" - @objc var didShowCompliancePopup: Bool { + var didShowCompliancePopup: Bool { get { bool(forKey: Self.didShowCompliancePopupKey) } set { diff --git a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift index 08b024e16435..af31b107e8de 100644 --- a/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift +++ b/WordPress/Classes/ViewRelated/EEUUSCompliance/CompliancePopoverViewModel.swift @@ -25,7 +25,7 @@ final class CompliancePopoverViewModel: ObservableObject { appAnalytics?.setUserHasOptedOut(!isAnalyticsEnabled) let (accountID, restAPI) = contextManager.performQuery { context -> (NSNumber?, WordPressComRestApi?) in - let account = try? WPAccount.lookupDefaultWordPressComAccount(in: context) + let account = try? WPAccount.lookupDefaultWordPressComAccount(in: context) return (account?.userID, account?.wordPressComRestApi) } diff --git a/WordPress/WordPressTest/EUUSCompliance/CompliancePopoverViewModelTests.swift b/WordPress/WordPressTest/EUUSCompliance/CompliancePopoverViewModelTests.swift index b22ba697d3a0..c3cd2bff3ce2 100644 --- a/WordPress/WordPressTest/EUUSCompliance/CompliancePopoverViewModelTests.swift +++ b/WordPress/WordPressTest/EUUSCompliance/CompliancePopoverViewModelTests.swift @@ -20,19 +20,15 @@ final class CompliancePopoverViewModelTests: CoreDataTestCase { testDefaults?.removeObject(forKey: UserDefaults.didShowCompliancePopupKey) } - func testDidTapSettingsUpdatesDefaults() { - guard let defaults = try? XCTUnwrap(testDefaults) else { - return - } + func testDidTapSettingsUpdatesDefaults() throws { + let defaults = try XCTUnwrap(testDefaults) let sut = CompliancePopoverViewModel(defaults: defaults, contextManager: contextManager) sut.didTapSettings() XCTAssert(defaults.didShowCompliancePopup) } - func testDidTapSettingsInvokesCoordinatorNavigation() { - guard let defaults = try? XCTUnwrap(testDefaults) else { - return - } + func testDidTapSettingsInvokesCoordinatorNavigation() throws { + let defaults = try XCTUnwrap(testDefaults) let mockCoordinator = MockCompliancePopoverCoordinator() let sut = CompliancePopoverViewModel(defaults: defaults, contextManager: contextManager) sut.coordinator = mockCoordinator @@ -40,14 +36,13 @@ final class CompliancePopoverViewModelTests: CoreDataTestCase { XCTAssertEqual(mockCoordinator.navigateToSettingsCallCount, 1) } - func testDidTapSaveInvokesDismissWhenAccountIDExists() { - guard let defaults = try? XCTUnwrap(testDefaults) else { - return - } + func testDidTapSaveInvokesDismissWhenAccountIDExists() throws { + let defaults = try XCTUnwrap(testDefaults) let mockCoordinator = MockCompliancePopoverCoordinator() + UserSettings.defaultDotComUUID = account().uuid let sut = CompliancePopoverViewModel( defaults: defaults, - contextManager: makeCoreDataStack() + contextManager: contextManager ) sut.coordinator = mockCoordinator sut.didTapSave() @@ -55,23 +50,20 @@ final class CompliancePopoverViewModelTests: CoreDataTestCase { XCTAssert(defaults.didShowCompliancePopup) } - private func makeCoreDataStack() -> ContextManager { - let contextManager = ContextManager.forTesting() - let account = AccountBuilder(contextManager) + private func account() -> WPAccount { + return AccountBuilder(contextManager) .with(id: 1229) .with(username: "foobar") .with(email: "foo@automattic.com") .with(authToken: "9384rj398t34j98") .build() - UserSettings.defaultDotComUUID = account.uuid - return contextManager } } private class MockCompliancePopoverCoordinator: CompliancePopoverCoordinatorProtocol { - var navigateToSettingsCallCount = 0 - var presentIfNeededCallCount = 0 - var dismissCallCount = 0 + private(set) var navigateToSettingsCallCount = 0 + private(set) var presentIfNeededCallCount = 0 + private(set) var dismissCallCount = 0 func presentIfNeeded() { presentIfNeededCallCount += 1 From a36288ea5fe17eb12e3fb671594e7c7f2923a65f Mon Sep 17 00:00:00 2001 From: alpavanoglu Date: Tue, 11 Jul 2023 14:14:46 +0200 Subject: [PATCH 13/14] Update podfile to target WordPressKit 8.5 --- Podfile | 2 +- Podfile.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Podfile b/Podfile index c3047244de94..8d5a7f5bc272 100644 --- a/Podfile +++ b/Podfile @@ -50,7 +50,7 @@ def wordpress_ui end def wordpress_kit - pod 'WordPressKit', '~> 8.5-beta.3' + pod 'WordPressKit', '~> 8.5' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', tag: '' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', branch: '' # pod 'WordPressKit', git: 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', commit: '' diff --git a/Podfile.lock b/Podfile.lock index 2e989e6cd297..089906197b23 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -512,7 +512,7 @@ PODS: - WordPressKit (~> 8.0-beta) - WordPressShared (~> 2.1-beta) - WordPressUI (~> 1.7-beta) - - WordPressKit (8.5.0-beta.3): + - WordPressKit (8.5.0): - Alamofire (~> 4.8.0) - NSObject-SafeExpectations (~> 0.0.4) - UIDeviceIdentifier (~> 2.0) @@ -612,7 +612,7 @@ DEPENDENCIES: - SwiftLint (~> 0.50) - WordPress-Editor-iOS (~> 1.19.8) - WordPressAuthenticator (~> 6.1-beta) - - WordPressKit (~> 8.5-beta.3) + - WordPressKit (~> 8.5) - WordPressShared (~> 2.2-beta) - WordPressUI (~> 1.12.5) - WPMediaPicker (~> 1.8-beta) @@ -880,7 +880,7 @@ SPEC CHECKSUMS: WordPress-Aztec-iOS: 7d11d598f14c82c727c08b56bd35fbeb7dafb504 WordPress-Editor-iOS: 9eb9f12f21a5209cb837908d81ffe1e31cb27345 WordPressAuthenticator: b0b900696de5129a215adcd1e9ae6eb89da36ac8 - WordPressKit: 29a0cd0cfbddc8b4bfbe076e0d29b258ba8daa6a + WordPressKit: f6943a6e927e9f57bc8793938af1e3a4c3adb614 WordPressShared: 87f3ee89b0a3e83106106f13a8b71605fb8eb6d2 WordPressUI: c5be816f6c7b3392224ac21de9e521e89fa108ac WPMediaPicker: 0d40b8d66b6dfdaa2d6a41e3be51249ff5898775 @@ -895,6 +895,6 @@ SPEC CHECKSUMS: ZendeskSupportSDK: 3a8e508ab1d9dd22dc038df6c694466414e037ba ZIPFoundation: ae5b4b813d216d3bf0a148773267fff14bd51d37 -PODFILE CHECKSUM: 66f009c76957bfeaeeb56fd48cb877edb372951c +PODFILE CHECKSUM: 05c81b021c29ea99daf4ef573cb2dcf6ecaf1e4c COCOAPODS: 1.12.1 From 277c36091dd8c39e5ddf1fc612b41c42a6b0eff5 Mon Sep 17 00:00:00 2001 From: alpavanoglu Date: Tue, 11 Jul 2023 17:27:28 +0200 Subject: [PATCH 14/14] Revert WordPressUI update --- Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Podfile.lock b/Podfile.lock index 03c54408dff3..56855426b5d3 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -895,6 +895,6 @@ SPEC CHECKSUMS: ZendeskSupportSDK: 3a8e508ab1d9dd22dc038df6c694466414e037ba ZIPFoundation: ae5b4b813d216d3bf0a148773267fff14bd51d37 -PODFILE CHECKSUM: 463ed7d39926c127d8197fe925fd7d05125f647b +PODFILE CHECKSUM: d1a2566033c325f4188d736422f6997b7551d2cb COCOAPODS: 1.12.1