Skip to content

Commit 07a9b7f

Browse files
Merge branch 'trunk' into issue/8075-tracks
2 parents 48156bd + 848f8a7 commit 07a9b7f

File tree

12 files changed

+91
-48
lines changed

12 files changed

+91
-48
lines changed

Experiments/Experiments/ABTest.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ public enum ABTest: String, CaseIterable {
1616
///
1717
case abTestLoginWithWPComOnly = "woocommerceios_login_wpcom_only"
1818

19+
/// A/B test to measure the sign-in success rate when native Jetpack installation experience is enabled
20+
/// Experiment ref: pbxNRc-29W-p2
21+
///
22+
case nativeJetpackSetupFlow = "woocommerceios_login_jetpack_setup_flow"
23+
1924
/// A/B test for the Products Onboarding banner on the My Store dashboard.
2025
/// Experiment ref: pbxNRc-26F-p2
2126
case productsOnboardingBanner = "woocommerceios_products_onboarding_first_product_banner"
@@ -34,7 +39,7 @@ public enum ABTest: String, CaseIterable {
3439
/// When adding a new experiment, add it to the appropriate case depending on its context (logged-in or logged-out experience).
3540
public var context: ExperimentContext {
3641
switch self {
37-
case .productsOnboardingBanner, .productsOnboardingTemplateProducts:
42+
case .productsOnboardingBanner, .productsOnboardingTemplateProducts, .nativeJetpackSetupFlow:
3843
return .loggedIn
3944
case .aaTestLoggedOut, .abTestLoginWithWPComOnly:
4045
return .loggedOut
@@ -47,6 +52,7 @@ public enum ABTest: String, CaseIterable {
4752
public extension ABTest {
4853
/// Start the AB Testing platform if any experiment exists for the provided context
4954
///
55+
@MainActor
5056
static func start(for context: ExperimentContext) async {
5157
let experiments = ABTest.allCases.filter { $0.context == context }
5258

Experiments/Experiments/DefaultFeatureFlagService.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@ public struct DefaultFeatureFlagService: FeatureFlagService {
5151
.performanceMonitoringUserInteraction:
5252
// Disabled by default to avoid costs spikes, unless in internal testing builds.
5353
return buildConfig == .alpha
54-
case .nativeJetpackSetupFlow:
55-
return buildConfig == .localDeveloper || buildConfig == .alpha
5654
case .analyticsHub:
5755
return buildConfig == .localDeveloper || buildConfig == .alpha
5856
case .tapToPayOnIPhone:

Experiments/Experiments/FeatureFlag.swift

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,6 @@ public enum FeatureFlag: Int {
130130
/// - Note: The app will ignore this if `performanceMonitoring` is `false`.
131131
case performanceMonitoringViewController
132132

133-
/// Temporary feature flag for the native Jetpack setup flow.
134-
/// TODO-8075: replace this with A/B test.
135-
///
136-
case nativeJetpackSetupFlow
137-
138133
/// Temporary feature flag for the native Jetpack setup flow.
139134
///
140135
case analyticsHub

WooCommerce/Classes/Analytics/WooAnalytics.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import Experiments
12
import Foundation
23
import UIKit
34
import WordPressShared
@@ -60,6 +61,15 @@ public extension WooAnalytics {
6061
}
6162

6263
analyticsProvider.refreshUserData()
64+
65+
// Refreshes A/B experiments since `ExPlat.shared` is reset after each `TracksProvider.refreshUserData` call
66+
// and any A/B test assignments that come back after the shared instance is reset won't be saved for later
67+
// access.
68+
let context: ExperimentContext = ServiceLocator.stores.isAuthenticated ?
69+
.loggedIn: .loggedOut
70+
Task { @MainActor in
71+
await ABTest.start(for: context)
72+
}
6373
}
6474

6575
/// Track a spcific event without any associated properties

WooCommerce/Classes/AppDelegate.swift

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
7373
// ever new source code is injected into our application.
7474
Inject.animation = .interactiveSpring()
7575

76-
Task { @MainActor in
77-
await startABTesting()
78-
79-
// Upgrade check...
80-
// This has to be called after A/B testing setup in `startABTesting` if any of the Tracks events
81-
// in `checkForUpgrades` is used as an exposure event for an experiment.
82-
// For example, `application_installed` could be the exposure event for logged-out experiments.
83-
checkForUpgrades()
84-
}
76+
// Upgrade check...
77+
// This has to be called after A/B testing setup in `setupAnalytics` (which calls
78+
// `WooAnalytics.refreshUserData`) if any of the Tracks events in `checkForUpgrades` is
79+
// used as an exposure event for an experiment.
80+
// For example, `application_installed` could be the exposure event for logged-out experiments.
81+
checkForUpgrades()
8582

8683
return true
8784
}
@@ -372,13 +369,6 @@ private extension AppDelegate {
372369
}
373370
}
374371

375-
/// Starts the AB testing platform and fetches test assignments for the current context
376-
///
377-
func startABTesting() async {
378-
let context: ExperimentContext = ServiceLocator.stores.isAuthenticated ? .loggedIn : .loggedOut
379-
await ABTest.start(for: context)
380-
}
381-
382372
/// Tracks if the application was opened via a widget tap.
383373
///
384374
func trackWidgetTappedIfNeeded(userActivity: NSUserActivity) {

WooCommerce/Classes/Authentication/AuthenticationManager.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -761,8 +761,7 @@ private extension AuthenticationManager {
761761
}
762762

763763
// Shows the native Jetpack flow during the site discovery flow.
764-
// TODO-8075: replace feature flag with A/B testing
765-
if featureFlagService.isFeatureFlagEnabled(.nativeJetpackSetupFlow) {
764+
if ABTest.nativeJetpackSetupFlow.variation != .control {
766765
return jetpackSetupUI(for: site.url,
767766
connectionMissingOnly: site.hasJetpack && site.isJetpackActive,
768767
in: navigationController)

WooCommerce/Classes/Authentication/Epilogue/StorePickerViewController.swift

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,6 @@ final class StorePickerViewController: UIViewController {
201201
switch configuration {
202202
case .login:
203203
startListeningToNotifications()
204-
startABTesting()
205204
case .switchingStores:
206205
secondaryActionButton.isHidden = true
207206
default:
@@ -574,17 +573,6 @@ private extension StorePickerViewController {
574573
fancyAlert.transitioningDelegate = AppDelegate.shared.tabBarController
575574
present(fancyAlert, animated: true)
576575
}
577-
578-
/// Refreshes the AB testing assignments (refresh is needed after a user logs in)
579-
///
580-
func startABTesting() {
581-
guard ServiceLocator.stores.isAuthenticated else {
582-
return
583-
}
584-
Task { @MainActor in
585-
await ABTest.start(for: .loggedIn)
586-
}
587-
}
588576
}
589577

590578
// MARK: Transition Controller Delegate

WooCommerce/Classes/Tools/Zendesk/ZendeskManager.swift

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import WordPressShared
88
import CoreTelephony
99
import SafariServices
1010
import Yosemite
11+
import Experiments
1112

1213
extension NSNotification.Name {
1314
static let ZDPNReceived = NSNotification.Name(rawValue: "ZDPNReceived")
@@ -41,6 +42,7 @@ protocol ZendeskManagerProtocol: SupportManagerAdapter {
4142
func showTicketListIfPossible(from controller: UIViewController)
4243
func showSupportEmailPrompt(from controller: UIViewController, completion: @escaping onUserInformationCompletion)
4344
func getTags(supportSourceTag: String?) -> [String]
45+
func fetchSystemStatusReport()
4446
func initialize()
4547
func reset()
4648
}
@@ -92,6 +94,10 @@ struct NoZendeskManager: ZendeskManagerProtocol {
9294
[]
9395
}
9496

97+
func fetchSystemStatusReport() {
98+
// no-op
99+
}
100+
95101
func initialize() {
96102
// no-op
97103
}
@@ -146,6 +152,8 @@ final class ZendeskManager: NSObject, ZendeskManagerProtocol {
146152
private let stores = ServiceLocator.stores
147153
private let storageManager = ServiceLocator.storageManager
148154

155+
private let isSSRFeatureFlagEnabled = DefaultFeatureFlagService().isFeatureFlagEnabled(.systemStatusReportInSupportRequest)
156+
149157
/// Controller for fetching site plugins from Storage
150158
///
151159
private lazy var pluginResultsController: ResultsController<StorageSitePlugin> = createPluginResultsController()
@@ -202,6 +210,25 @@ final class ZendeskManager: NSObject, ZendeskManagerProtocol {
202210
return ippTags.map { $0.rawValue }
203211
}
204212

213+
/// Instantiates the SystemStatusReportViewModel as soon as the Zendesk instance needs it
214+
/// This generally happens in the SettingsViewModel if we need to fetch the site's System Status Report
215+
///
216+
private lazy var systemStatusReportViewModel: SystemStatusReportViewModel = SystemStatusReportViewModel(
217+
siteID: ServiceLocator.stores.sessionManager.defaultSite?.siteID ?? 0
218+
)
219+
220+
/// Formatted system status report to be displayed on-screen
221+
///
222+
private var systemStatusReport: String {
223+
systemStatusReportViewModel.statusReport
224+
}
225+
226+
/// Handles fetching the site's System Status Report
227+
///
228+
func fetchSystemStatusReport() {
229+
systemStatusReportViewModel.fetchReport()
230+
}
231+
205232
func showNewRequestIfPossible(from controller: UIViewController) {
206233
showNewRequestIfPossible(from: controller, with: nil)
207234
}
@@ -638,11 +665,23 @@ private extension ZendeskManager {
638665
/// Without it, the tickets won't appear in the correct view(s) in the web portal and they won't contain all the metadata needed to solve a ticket.
639666
///
640667
func createRequest(supportSourceTag: String?) -> RequestUiConfiguration {
668+
669+
var logsFieldID: Int64 = TicketFieldIDs.legacyLogs
670+
var systemStatusReportFieldID: Int64 = 0
671+
if isSSRFeatureFlagEnabled {
672+
/// If the feature flag is enabled, `legacyLogs` Field ID is used to send the SSR logs,
673+
/// and `logs` Field ID is used to send the logs.
674+
///
675+
logsFieldID = TicketFieldIDs.logs
676+
systemStatusReportFieldID = TicketFieldIDs.legacyLogs
677+
}
678+
641679
let ticketFields = [
642680
CustomField(fieldId: TicketFieldIDs.appVersion, value: Bundle.main.version),
643681
CustomField(fieldId: TicketFieldIDs.deviceFreeSpace, value: getDeviceFreeSpace()),
644682
CustomField(fieldId: TicketFieldIDs.networkInformation, value: getNetworkInformation()),
645-
CustomField(fieldId: TicketFieldIDs.logs, value: getLogFile()),
683+
CustomField(fieldId: logsFieldID, value: getLogFile()),
684+
CustomField(fieldId: systemStatusReportFieldID, value: systemStatusReport),
646685
CustomField(fieldId: TicketFieldIDs.currentSite, value: getCurrentSiteDescription()),
647686
CustomField(fieldId: TicketFieldIDs.sourcePlatform, value: Constants.sourcePlatform),
648687
CustomField(fieldId: TicketFieldIDs.appLanguage, value: Locale.preferredLanguage),
@@ -657,12 +696,23 @@ private extension ZendeskManager {
657696

658697
func createWCPayRequest(supportSourceTag: String?) -> RequestUiConfiguration {
659698

699+
var logsFieldID: Int64 = TicketFieldIDs.legacyLogs
700+
var systemStatusReportFieldID: Int64 = 0
701+
if isSSRFeatureFlagEnabled {
702+
/// If the feature flag is enabled, `legacyLogs` Field ID is used to send the SSR logs,
703+
/// and `logs` Field ID is used to send the logs.
704+
///
705+
logsFieldID = TicketFieldIDs.logs
706+
systemStatusReportFieldID = TicketFieldIDs.legacyLogs
707+
}
708+
660709
// Set form field values
661710
let ticketFields = [
662711
CustomField(fieldId: TicketFieldIDs.appVersion, value: Bundle.main.version),
663712
CustomField(fieldId: TicketFieldIDs.deviceFreeSpace, value: getDeviceFreeSpace()),
664713
CustomField(fieldId: TicketFieldIDs.networkInformation, value: getNetworkInformation()),
665-
CustomField(fieldId: TicketFieldIDs.logs, value: getLogFile()),
714+
CustomField(fieldId: logsFieldID, value: getLogFile()),
715+
CustomField(fieldId: systemStatusReportFieldID, value: systemStatusReport),
666716
CustomField(fieldId: TicketFieldIDs.currentSite, value: getCurrentSiteDescription()),
667717
CustomField(fieldId: TicketFieldIDs.sourcePlatform, value: Constants.sourcePlatform),
668718
CustomField(fieldId: TicketFieldIDs.appLanguage, value: Locale.preferredLanguage),
@@ -1087,7 +1137,8 @@ private extension ZendeskManager {
10871137
static let allBlogs: Int64 = 360000087183
10881138
static let deviceFreeSpace: Int64 = 360000089123
10891139
static let networkInformation: Int64 = 360000086966
1090-
static let logs: Int64 = 22871957
1140+
static let legacyLogs: Int64 = 22871957
1141+
static let logs: Int64 = 10901699622036
10911142
static let currentSite: Int64 = 360000103103
10921143
static let sourcePlatform: Int64 = 360009311651
10931144
static let appLanguage: Int64 = 360008583691

WooCommerce/Classes/ViewRelated/Dashboard/Settings/Settings/SettingsViewModel.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ final class SettingsViewModel: SettingsViewModelOutput, SettingsViewModelActions
110110
configuration: upsellCardReadersCampaign.configuration)
111111
}
112112

113+
/// Reference to the Zendesk shared instance
114+
///
115+
private let zendeskShared: ZendeskManagerProtocol = ZendeskProvider.shared
116+
113117
init(stores: StoresManager = ServiceLocator.stores,
114118
storageManager: StorageManagerType = ServiceLocator.storageManager,
115119
featureFlagService: FeatureFlagService = ServiceLocator.featureFlagService,
@@ -141,6 +145,11 @@ final class SettingsViewModel: SettingsViewModelOutput, SettingsViewModelActions
141145
let action = SystemStatusAction.synchronizeSystemPlugins(siteID: siteID, onCompletion: { _ in })
142146
stores.dispatch(action)
143147
}
148+
149+
/// Fetch System Status Report from Zendesk
150+
/// so it will be ready to be attached to a a support request when needed
151+
///
152+
zendeskShared.fetchSystemStatusReport()
144153
}
145154

146155
/// Sets up the view model and loads the settings.

WooCommerce/Classes/Yosemite/DefaultStoresManager.swift

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -178,11 +178,6 @@ class DefaultStoresManager: StoresManager {
178178

179179
updateAndReloadWidgetInformation(with: nil)
180180

181-
// Refresh the A/B test assignments for the logged-out context
182-
Task { @MainActor in
183-
await ABTest.start(for: .loggedOut)
184-
}
185-
186181
NotificationCenter.default.post(name: .logOutEventReceived, object: nil)
187182

188183
return self

0 commit comments

Comments
 (0)