Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cherry pick Subscription Attribution Changes #2868

Merged
merged 2 commits into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Core/PixelEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,7 @@ extension Pixel {
case privacyProVPNAccessRevokedDialogShown
case privacyProVPNBetaStoppedWhenPrivacyProEnabled
case privacyProTransactionProgressNotHiddenAfter60s
case privacyProSuccessfulSubscriptionAttribution

// MARK: Pixel Experiment
case pixelExperimentEnrollment
Expand Down Expand Up @@ -1308,6 +1309,7 @@ extension Pixel.Event {
case .privacyProSubscriptionManagementPlanBilling: return "m_privacy-pro_settings_change-plan-or-billing_click"
case .privacyProSubscriptionManagementRemoval: return "m_privacy-pro_settings_remove-from-device_click"
case .privacyProTransactionProgressNotHiddenAfter60s: return "m_privacy-pro_progress_not_hidden_after_60s"
case .privacyProSuccessfulSubscriptionAttribution: return "m_subscribe"

// MARK: Pixel Experiment
case .pixelExperimentEnrollment: return "pixel_experiment_enrollment"
Expand Down
22 changes: 21 additions & 1 deletion DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,10 @@
98F3A1D8217B37010011A0D4 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98F3A1D7217B37010011A0D4 /* Theme.swift */; };
98F6EA472863124100720957 /* ContentBlockerRulesLists.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98F6EA462863124100720957 /* ContentBlockerRulesLists.swift */; };
98F78B8E22419093007CACF4 /* ThemableNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98F78B8D22419093007CACF4 /* ThemableNavigationController.swift */; };
9F2510142BF5809E0096DB16 /* SubscriptionFlowViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F2510132BF5809E0096DB16 /* SubscriptionFlowViewModelTests.swift */; };
9F8FE9492BAE50E50071E372 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 9F8FE9482BAE50E50071E372 /* Lottie */; };
9FA5E44B2BF1AF3400BDEF02 /* SubscriptionContainerViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FA5E44A2BF1AF3400BDEF02 /* SubscriptionContainerViewFactory.swift */; };
9FA5E44E2BF1B16400BDEF02 /* SubscriptionContainerViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FA5E44D2BF1B16400BDEF02 /* SubscriptionContainerViewModelTests.swift */; };
AA3D854523D9942200788410 /* AppIconSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3D854423D9942200788410 /* AppIconSettingsViewController.swift */; };
AA3D854723D9E88E00788410 /* AppIconSettingsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3D854623D9E88E00788410 /* AppIconSettingsCell.swift */; };
AA3D854923DA1DFB00788410 /* AppIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3D854823DA1DFB00788410 /* AppIcon.swift */; };
Expand Down Expand Up @@ -2212,6 +2215,9 @@
98F3A1D7217B37010011A0D4 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
98F6EA462863124100720957 /* ContentBlockerRulesLists.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBlockerRulesLists.swift; sourceTree = "<group>"; };
98F78B8D22419093007CACF4 /* ThemableNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemableNavigationController.swift; sourceTree = "<group>"; };
9F2510132BF5809E0096DB16 /* SubscriptionFlowViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionFlowViewModelTests.swift; sourceTree = "<group>"; };
9FA5E44A2BF1AF3400BDEF02 /* SubscriptionContainerViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionContainerViewFactory.swift; sourceTree = "<group>"; };
9FA5E44D2BF1B16400BDEF02 /* SubscriptionContainerViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionContainerViewModelTests.swift; sourceTree = "<group>"; };
AA3D854423D9942200788410 /* AppIconSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIconSettingsViewController.swift; sourceTree = "<group>"; };
AA3D854623D9E88E00788410 /* AppIconSettingsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIconSettingsCell.swift; sourceTree = "<group>"; };
AA3D854823DA1DFB00788410 /* AppIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIcon.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -4087,6 +4093,15 @@
name = Themes;
sourceTree = "<group>";
};
9FA5E44C2BF1B14100BDEF02 /* Subscription */ = {
isa = PBXGroup;
children = (
9FA5E44D2BF1B16400BDEF02 /* SubscriptionContainerViewModelTests.swift */,
9F2510132BF5809E0096DB16 /* SubscriptionFlowViewModelTests.swift */,
);
name = Subscription;
sourceTree = "<group>";
};
AA4D6A8023DE4973007E8790 /* AppIcon */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -4463,6 +4478,7 @@
children = (
D664C7AD2B289AA000CBFA76 /* PurchaseInProgressView.swift */,
D66F683C2BB333C100AE93E2 /* SubscriptionContainerView.swift */,
9FA5E44A2BF1AF3400BDEF02 /* SubscriptionContainerViewFactory.swift */,
D664C7AE2B289AA000CBFA76 /* SubscriptionFlowView.swift */,
D68DF81B2B58302E0023DBEA /* SubscriptionRestoreView.swift */,
D64648AC2B59936B0033090B /* SubscriptionEmailView.swift */,
Expand Down Expand Up @@ -4824,6 +4840,7 @@
F1D477C71F2139210031ED49 /* OmniBar */,
C1B7B52E28944DDC0098FD6A /* RemoteMessaging */,
98EA2C3F218BB5140023E1DC /* Settings */,
9FA5E44C2BF1B14100BDEF02 /* Subscription */,
F13B4BF71F18C9E800814661 /* Tabs */,
98EA2C3A218B9A880023E1DC /* Themes */,
F12790DD1EBBDDF3001D3AEC /* Tutorials */,
Expand Down Expand Up @@ -6492,6 +6509,7 @@
1E8AD1D527C2E22900ABA377 /* DownloadsListSectionViewModel.swift in Sources */,
EE0798C52B179936000A4F64 /* NetworkProtectionVPNCountryLabelsModel.swift in Sources */,
31584616281AFB46004ADB8B /* AutofillLoginDetailsViewController.swift in Sources */,
9FA5E44B2BF1AF3400BDEF02 /* SubscriptionContainerViewFactory.swift in Sources */,
C1F341C72A6924100032057B /* EmailAddressPromptViewModel.swift in Sources */,
F47E53D9250A97330037C686 /* OnboardingDefaultBroswerViewController.swift in Sources */,
F13B4BD51F183B3600814661 /* TabsModelPersistenceExtension.swift in Sources */,
Expand Down Expand Up @@ -6831,6 +6849,7 @@
83EDCC411F86B89C005CDFCD /* StatisticsLoaderTests.swift in Sources */,
C14882E327F20D9A00D59F0C /* BookmarksExporterTests.swift in Sources */,
85C29708247BDD060063A335 /* DaxDialogsBrowsingSpecTests.swift in Sources */,
9FA5E44E2BF1B16400BDEF02 /* SubscriptionContainerViewModelTests.swift in Sources */,
85BA58581F34F72F00C6E8CA /* AppUserDefaultsTests.swift in Sources */,
F1134EBC1F40D45700B73467 /* MockStatisticsStore.swift in Sources */,
31C138AC27A403CB00FFD4B2 /* DownloadManagerTests.swift in Sources */,
Expand Down Expand Up @@ -6884,6 +6903,7 @@
1E8146AD28C8ABF000D1AF63 /* TrackerAnimationLogicTests.swift in Sources */,
C1CDA31E2AFBF811006D1476 /* AutofillNeverPromptWebsitesManagerTests.swift in Sources */,
B6AD9E3A28D456820019CDE9 /* PrivacyConfigurationManagerMock.swift in Sources */,
9F2510142BF5809E0096DB16 /* SubscriptionFlowViewModelTests.swift in Sources */,
F189AED71F18F6DE001EBAE1 /* TabTests.swift in Sources */,
F13B4BFB1F18E3D900814661 /* TabsModelPersistenceExtensionTests.swift in Sources */,
C12B6E7C2BED69C100050D93 /* AutofillPixelReporterTests.swift in Sources */,
Expand Down Expand Up @@ -9834,7 +9854,7 @@
repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit";
requirement = {
kind = exactVersion;
version = "144.0.7-3";
version = "144.0.7-4";
};
};
9F8FE9472BAE50E50071E372 /* XCRemoteSwiftPackageReference "lottie-spm" */ = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/DuckDuckGo/BrowserServicesKit",
"state" : {
"revision" : "ada5f68970f098b3230dbd80a25cd048a606ac12",
"version" : "144.0.7-3"
"revision" : "43db1d59455246547fc4ea3998f07751dfa77166",
"version" : "144.0.7-4"
}
},
{
Expand Down Expand Up @@ -138,7 +138,7 @@
{
"identity" : "swift-argument-parser",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser.git",
"location" : "https://github.com/apple/swift-argument-parser",
"state" : {
"revision" : "46989693916f56d1186bd59ac15124caef896560",
"version" : "1.3.1"
Expand Down Expand Up @@ -183,7 +183,7 @@
{
"identity" : "trackerradarkit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/duckduckgo/TrackerRadarKit",
"location" : "https://github.com/duckduckgo/TrackerRadarKit.git",
"state" : {
"revision" : "6c84fd19139414fc0edbf9673ade06e532a564f0",
"version" : "2.0.0"
Expand Down
2 changes: 1 addition & 1 deletion DuckDuckGo/MainViewController+Segues.swift
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ extension MainViewController {
os_log(#function, log: .generalLog, type: .debug)
hideAllHighlightsIfNeeded()
launchSettings {
$0.triggerDeepLinkNavigation(to: .subscriptionFlow)
$0.triggerDeepLinkNavigation(to: .subscriptionFlow())
}
}

Expand Down
8 changes: 7 additions & 1 deletion DuckDuckGo/MainViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1371,7 +1371,13 @@ class MainViewController: UIViewController {
.sink { [weak self] notification in
switch notification.name {
case .urlInterceptPrivacyPro:
self?.launchSettings(deepLinkTarget: .subscriptionFlow)
let deepLinkTarget: SettingsViewModel.SettingsDeepLinkSection
if let origin = notification.userInfo?[AttributionParameter.origin] as? String {
deepLinkTarget = .subscriptionFlow(origin: origin)
} else {
deepLinkTarget = .subscriptionFlow()
}
self?.launchSettings(deepLinkTarget: deepLinkTarget)
default:
return
}
Expand Down
6 changes: 3 additions & 3 deletions DuckDuckGo/SettingsRootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,10 @@ struct SettingsRootView: View {
SubscriptionPIRView()
case .itr:
SubscriptionITPView()
case .subscriptionFlow:
SubscriptionContainerView(currentView: .subscribe).environmentObject(subscriptionNavigationCoordinator)
case let .subscriptionFlow(origin):
SubscriptionContainerViewFactory.makeSubscribeFlow(origin: origin, navigationCoordinator: subscriptionNavigationCoordinator)
case .subscriptionRestoreFlow:
SubscriptionContainerView(currentView: .restore).environmentObject(subscriptionNavigationCoordinator)
SubscriptionContainerViewFactory.makeRestoreFlow(navigationCoordinator: subscriptionNavigationCoordinator)
default:
EmptyView()
}
Expand Down
17 changes: 9 additions & 8 deletions DuckDuckGo/SettingsSubscriptionView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,12 @@ struct SettingsSubscriptionView: View {
Group {
SettingsCustomCell(content: { subscriptionDescriptionView })

let subscribeView = SubscriptionContainerView(currentView: .subscribe)
let subscribeView = SubscriptionContainerViewFactory.makeSubscribeFlow(
origin: nil,
navigationCoordinator: subscriptionNavigationCoordinator
).navigationViewStyle(.stack)
let restoreView = SubscriptionContainerViewFactory.makeRestoreFlow(navigationCoordinator: subscriptionNavigationCoordinator)
.navigationViewStyle(.stack)
.environmentObject(subscriptionNavigationCoordinator)
let restoreView = SubscriptionContainerView(currentView: .restore)
.navigationViewStyle(.stack)
.environmentObject(subscriptionNavigationCoordinator)
.onFirstAppear {
Pixel.fire(pixel: .privacyProRestorePurchaseClick)
}
Expand Down Expand Up @@ -122,9 +122,10 @@ struct SettingsSubscriptionView: View {
}
})

let subscribeView = SubscriptionContainerView(currentView: .subscribe)
.navigationViewStyle(.stack)
.environmentObject(subscriptionNavigationCoordinator)
let subscribeView = SubscriptionContainerViewFactory.makeSubscribeFlow(
origin: nil,
navigationCoordinator: subscriptionNavigationCoordinator
).navigationViewStyle(.stack)
NavigationLink(
destination: subscribeView,
isActive: $isShowingSubscribeFlow,
Expand Down
6 changes: 3 additions & 3 deletions DuckDuckGo/SettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,10 @@ struct SettingsView: View {
SubscriptionPIRView()
case .itr:
SubscriptionITPView()
case .subscriptionFlow:
SubscriptionContainerView(currentView: .subscribe).environmentObject(subscriptionNavigationCoordinator)
case let .subscriptionFlow(origin):
SubscriptionContainerViewFactory.makeSubscribeFlow(origin: origin, navigationCoordinator: subscriptionNavigationCoordinator)
case .subscriptionRestoreFlow:
SubscriptionContainerView(currentView: .restore).environmentObject(subscriptionNavigationCoordinator)
SubscriptionContainerViewFactory.makeRestoreFlow(navigationCoordinator: subscriptionNavigationCoordinator)
default:
EmptyView()
}
Expand Down
4 changes: 2 additions & 2 deletions DuckDuckGo/SettingsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -687,11 +687,11 @@ extension SettingsViewModel: AutofillLoginSettingsListViewControllerDelegate {
// MARK: DeepLinks
extension SettingsViewModel {

enum SettingsDeepLinkSection: Identifiable {
enum SettingsDeepLinkSection: Identifiable, Equatable {
case netP
case dbp
case itr
case subscriptionFlow
case subscriptionFlow(origin: String? = nil)
case subscriptionRestoreFlow
// Add other cases as needed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec

var originalMessage: WKScriptMessage?

private let subscriptionAttributionOrigin: String?
init(subscriptionAttributionOrigin: String?) {
self.subscriptionAttributionOrigin = subscriptionAttributionOrigin
}

func with(broker: UserScriptMessageBroker) {
self.broker = broker
}
Expand Down Expand Up @@ -252,6 +257,7 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec
case .success(let purchaseUpdate):
DailyPixel.fireDailyAndCount(pixel: .privacyProPurchaseSuccess)
UniquePixel.fire(pixel: .privacyProSubscriptionActivated)
Pixel.fireAttribution(pixel: .privacyProSuccessfulSubscriptionAttribution, origin: subscriptionAttributionOrigin)
setTransactionStatus(.idle)
await pushPurchaseUpdate(originalMessage: message, purchaseUpdate: purchaseUpdate)
case .failure:
Expand Down Expand Up @@ -421,6 +427,24 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec
onActivateSubscription = nil
onBackToSettings = nil
}


}

private extension Pixel {

enum AttributionParameters {
static let origin = "origin"
static let locale = "locale"
}

static func fireAttribution(pixel: Pixel.Event, origin: String?, locale: Locale = .current) {
var parameters: [String: String] = [:]
parameters[AttributionParameters.locale] = locale.identifier
if let origin {
parameters[AttributionParameters.origin] = origin
}
Self.fire(pixel: pixel, withAdditionalParameters: parameters)
}

}
// swiftlint:enable file_length
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
//

import Foundation
import Subscription
import Combine

@available(iOS 15.0, *)
Expand All @@ -31,11 +32,14 @@ final class SubscriptionContainerViewModel: ObservableObject {
let email: SubscriptionEmailViewModel


init(userScript: SubscriptionPagesUserScript = SubscriptionPagesUserScript(),
subFeature: SubscriptionPagesUseSubscriptionFeature = SubscriptionPagesUseSubscriptionFeature()) {
init(
origin: String?,
userScript: SubscriptionPagesUserScript,
subFeature: SubscriptionPagesUseSubscriptionFeature
) {
self.userScript = userScript
self.subFeature = subFeature
self.flow = SubscriptionFlowViewModel(userScript: userScript, subFeature: subFeature)
self.flow = SubscriptionFlowViewModel(origin: origin, userScript: userScript, subFeature: subFeature)
self.restore = SubscriptionRestoreViewModel(userScript: userScript, subFeature: subFeature)
self.email = SubscriptionEmailViewModel(userScript: userScript, subFeature: subFeature)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,8 @@ final class SubscriptionFlowViewModel: ObservableObject {
let subFeature: SubscriptionPagesUseSubscriptionFeature
let purchaseManager: PurchaseManager
var webViewModel: AsyncHeadlessWebViewViewModel

var purchaseURL = URL.subscriptionPurchase

let purchaseURL: URL

private var cancellables = Set<AnyCancellable>()
private var canGoBackCancellable: AnyCancellable?
private var urlCancellable: AnyCancellable?
Expand Down Expand Up @@ -69,10 +68,16 @@ final class SubscriptionFlowViewModel: ObservableObject {
allowedDomains: allowedDomains,
contentBlocking: false)

init(userScript: SubscriptionPagesUserScript,
init(origin: String?,
userScript: SubscriptionPagesUserScript,
subFeature: SubscriptionPagesUseSubscriptionFeature,
purchaseManager: PurchaseManager = PurchaseManager.shared,
selectedFeature: SettingsViewModel.SettingsDeepLinkSection? = nil) {
if let origin {
purchaseURL = URL.subscriptionPurchase.appendingParameter(name: AttributionParameter.origin, value: origin)
} else {
purchaseURL = URL.subscriptionPurchase
}
self.userScript = userScript
self.subFeature = subFeature
self.purchaseManager = purchaseManager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ struct SubscriptionContainerView: View {
private let emailViewModel: SubscriptionEmailViewModel

init(currentView: CurrentView,
viewModel: SubscriptionContainerViewModel = SubscriptionContainerViewModel()) {
viewModel: SubscriptionContainerViewModel) {
_currentViewState = State(initialValue: currentView)
self.viewModel = viewModel
let userScript = viewModel.userScript
Expand Down
Loading
Loading