From cec1679c05bc713ac1ef3a37f123a12c44a0bd4c Mon Sep 17 00:00:00 2001 From: alpavanoglu Date: Wed, 5 Jul 2023 14:17:06 +0200 Subject: [PATCH] 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 */,