Skip to content
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
23 changes: 13 additions & 10 deletions WooCommerce/Classes/Analytics/WooAnalytics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import UIKit
import WordPressShared
import WidgetKit
import enum Alamofire.AFError
import enum Networking.NetworkError
import Yosemite
import protocol WooFoundation.Analytics
import protocol WooFoundation.AnalyticsProvider
Expand Down Expand Up @@ -218,29 +219,31 @@ private extension Analytics {
return nil
}

let err = error as NSError
let nsError = error as NSError
let errorCode: String = {
if let networkError = error as? AFError {
if let responseCode = networkError.responseCode {
return "\(responseCode)"
} else if let underlyingError = networkError.underlyingError as? NSError {
return "\(underlyingError.code)"
if let networkError = error as? NetworkError, let code = networkError.responseCode {
return code.description
} else if let afError = error as? AFError {
if let responseCode = afError.responseCode {
return responseCode.description
} else if let underlyingError = afError.underlyingError as? NSError {
return underlyingError.code.description
}
} else if let loginError = error as? SiteCredentialLoginError {
return "\(loginError.underlyingError.code)"
return loginError.underlyingError.code.description
}
return "\(err.code)"
return nsError.code.description
}()

let errorDomain: String = {
if let networkError = error as? AFError,
let underlyingError = networkError.underlyingError as? NSError {
return underlyingError.domain
}
return err.domain
return nsError.domain
}()

let errorDescription = err.description
let errorDescription = nsError.description

return [
Constants.errorKeyCode: errorCode,
Expand Down
27 changes: 25 additions & 2 deletions WooCommerce/Classes/Analytics/WooAnalyticsEvent+JetpackSetup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ extension WooAnalyticsEvent {
case tap
case step
case isSignup = "is_signup"
case connectionType = "connection_type"
case usingApplicationPassword = "is_using_application_password"
}

enum LoginFlow {
Expand All @@ -35,6 +37,11 @@ extension WooAnalyticsEvent {
}
}

enum ConnectionType: String {
case native
case web
}

static func connectionCheckCompleted(isAlreadyConnected: Bool, requiresConnectionOnly: Bool) -> WooAnalyticsEvent {
.init(statName: .jetpackSetupConnectionCheckCompleted, properties: [
Key.isAlreadyConnected.rawValue: isAlreadyConnected,
Expand All @@ -56,8 +63,24 @@ extension WooAnalyticsEvent {
return .init(statName: .jetpackSetupLoginFlow, properties: properties, error: failure)
}

static func setupFlow(step: JetpackInstallStep, tap: SetupFlow.TapTarget? = nil, failure: Error? = nil) -> WooAnalyticsEvent {
var properties: [String: WooAnalyticsEventPropertyType] = [Key.step.rawValue: step.analyticsValue]
static func setupFlow(step: JetpackInstallStep,
tap: SetupFlow.TapTarget? = nil,
connectionType: ConnectionType,
failure: Error? = nil) -> WooAnalyticsEvent {
let isApplicationPassword: Bool = {
let credentials = ServiceLocator.stores.sessionManager.defaultCredentials
switch credentials {
case .some(.applicationPassword):
return true
default:
return false
}
}()
var properties: [String: WooAnalyticsEventPropertyType] = [
Key.step.rawValue: step.analyticsValue,
Key.connectionType.rawValue: connectionType.rawValue,
Key.usingApplicationPassword.rawValue: isApplicationPassword
]
if let tap {
properties[Key.tap.rawValue] = tap.rawValue
}
Expand Down
26 changes: 0 additions & 26 deletions WooCommerce/Classes/Analytics/WooAnalyticsStat.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,32 +107,6 @@ enum WooAnalyticsStat: String {
case loginSiteCredentialsAppPasswordLoginExitConfirmation = "login_site_credentials_app_password_login_exit_confirmation"
case loginSiteCredentialsAppPasswordLoginDismissed = "login_site_credentials_app_password_login_dismissed"


// MARK: Install/Setup Jetpack (`LoginJetpackSetupView`)
//
case loginJetpackSetupScreenViewed = "login_jetpack_setup_screen_viewed"
case loginJetpackSetupScreenDismissed = "login_jetpack_setup_screen_dismissed"

case loginJetpackSetupScreenInstallSuccessful = "login_jetpack_setup_install_successful"
case loginJetpackSetupScreenInstallFailed = "login_jetpack_setup_install_failed"

case loginJetpackSetupActivationSuccessful = "login_jetpack_setup_activation_successful"
case loginJetpackSetupActivationFailed = "login_jetpack_setup_activation_failed"

case loginJetpackSetupFetchJetpackConnectionURLSuccessful = "login_jetpack_setup_fetch_jetpack_connection_url_successful"
case loginJetpackSetupFetchJetpackConnectionURLFailed = "login_jetpack_setup_fetch_jetpack_connection_url_failed"

case loginJetpackSetupCannotFindWPCOMUser = "login_jetpack_setup_cannot_find_WPCOM_user"
case loginJetpackSetupAllStepsMarkedDone = "login_jetpack_setup_all_steps_marked_done"
case loginJetpackSetupErrorCheckingJetpackConnection = "login_jetpack_setup_error_checking_jetpack_connection"

case loginJetpackSetupGoToStoreTapped = "login_jetpack_setup_go_to_store_button_tapped"

case loginJetpackSetupAuthorizedUsingDifferentWPCOMAccount = "login_jetpack_setup_authorized_using_different_wpcom_account"

case loginJetpackSetupScreenTryAgainButtonTapped = "login_jetpack_setup_try_again_button_tapped"
case loginJetpackSetupScreenGetSupportTapped = "login_jetpack_setup_get_support_button_tapped"

// MARK: No matched site alert
//
case loginJetpackNoMatchedSiteErrorViewed = "login_jetpack_no_matched_site_error_viewed"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ private extension LoginJetpackSetupCoordinator {
guard let self, let email = connectedEmail else { return }
if email != self.stores.sessionManager.defaultAccount?.email {
// if the user authorized Jetpack with a different account, support them to log in with that account.
self.analytics.track(.loginJetpackSetupAuthorizedUsingDifferentWPCOMAccount)
self.showVerifyWPComAccount(email: email)
} else {
// dismiss the setup view
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ final class JetpackSetupHostingController: UIHostingController<JetpackSetupView>

rootView.supportHandler = { [weak self] in
guard let self else { return }

self.viewModel.trackSetupDuringLogin(.loginJetpackSetupScreenGetSupportTapped, properties: self.viewModel.currentSetupStep?.analyticsDescription)
self.viewModel.trackSetupAfterLogin(tap: .support)
self.viewModel.trackSetup(tap: .support)
self.presentSupport()
}

Expand All @@ -48,8 +46,6 @@ final class JetpackSetupHostingController: UIHostingController<JetpackSetupView>

override func viewDidLoad() {
super.viewDidLoad()

viewModel.trackSetupDuringLogin(.loginJetpackSetupScreenViewed)
configureNavigationBarAppearance()
}

Expand All @@ -62,8 +58,7 @@ final class JetpackSetupHostingController: UIHostingController<JetpackSetupView>

@objc
private func dismissView() {
viewModel.trackSetupDuringLogin(.loginJetpackSetupScreenDismissed, properties: viewModel.currentSetupStep?.analyticsDescription)
viewModel.trackSetupAfterLogin(tap: .dismiss)
viewModel.trackSetup(tap: .dismiss)
dismiss(animated: true)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ final class JetpackSetupViewModel: ObservableObject {
private let storeNavigationHandler: (_ connectedEmail: String?) -> Void
private let wpcomCredentials: Credentials?
private var isPluginActivated = false
private var connectionType = WooAnalyticsEvent.JetpackSetup.ConnectionType.native

@Published private(set) var setupSteps: [JetpackInstallStep]

Expand Down Expand Up @@ -151,15 +152,12 @@ final class JetpackSetupViewModel: ObservableObject {
}

func navigateToStore() {
trackSetupDuringLogin(.loginJetpackSetupGoToStoreTapped)
trackSetupAfterLogin(tap: .goToStore)
trackSetup(tap: .goToStore)
storeNavigationHandler(jetpackConnectedEmail)
}

func retryAllSteps() {
trackSetupDuringLogin(.loginJetpackSetupScreenTryAgainButtonTapped,
properties: currentSetupStep?.analyticsDescription)
trackSetupAfterLogin(tap: .retry)
trackSetup(tap: .retry)

setupFailed = false
setupError = nil
Expand All @@ -172,31 +170,18 @@ final class JetpackSetupViewModel: ObservableObject {

/// LoginJetpackSetupInterruptedView
func didTapContinueConnectionButton() {
trackSetupDuringLogin(.loginJetpackSetupScreenTryAgainButtonTapped)
trackSetupAfterLogin(tap: .continueSetup)
trackSetup(tap: .continueSetup)
checkJetpackConnection(afterConnection: false)
}

/// Tracks events if the current flow is Jetpack setup during login
func trackSetupDuringLogin(_ stat: WooAnalyticsStat,
properties: [AnyHashable: Any]? = nil,
failure: Error? = nil) {
guard stores.isAuthenticated == false else {
return
}
analytics.track(stat, properties: properties, error: failure)
}

/// Tracks events if the current flow is Jetpack setup after login with site credentials
func trackSetupAfterLogin(tap: WooAnalyticsEvent.JetpackSetup.SetupFlow.TapTarget? = nil,
failure: Error? = nil) {
guard stores.isAuthenticated else {
return
}
func trackSetup(tap: WooAnalyticsEvent.JetpackSetup.SetupFlow.TapTarget? = nil,
failure: Error? = nil) {
/// Helper for analytics since `currentSetupStep` is optional.
let currentStepForAnalytics: JetpackInstallStep = currentSetupStep ?? (connectionOnly ? .connection : .installation)
analytics.track(event: .JetpackSetup.setupFlow(step: currentStepForAnalytics,
tap: tap,
connectionType: connectionType,
failure: failure))
}
}
Expand Down Expand Up @@ -236,17 +221,15 @@ private extension JetpackSetupViewModel {

func installJetpack() {
currentSetupStep = .installation
trackSetupAfterLogin()
trackSetup()

let action = JetpackConnectionAction.installJetpackPlugin { [weak self] result in
guard let self else { return }
switch result {
case .success:
self.trackSetupDuringLogin(.loginJetpackSetupScreenInstallSuccessful)
self.activateJetpack()
case .failure(let error):
self.trackSetupDuringLogin(.loginJetpackSetupScreenInstallFailed, failure: error)
self.trackSetupAfterLogin(failure: error)
self.trackSetup(failure: error)
DDLogError("⛔️ Error installing Jetpack: \(error)")
self.setupError = error
self.setupFailed = true
Expand All @@ -257,17 +240,15 @@ private extension JetpackSetupViewModel {

func activateJetpack() {
currentSetupStep = .activation
trackSetupAfterLogin()
trackSetup()
let action = JetpackConnectionAction.activateJetpackPlugin { [weak self] result in
guard let self else { return }
switch result {
case .success:
isPluginActivated = true
self.trackSetupDuringLogin(.loginJetpackSetupActivationSuccessful)
self.checkJetpackConnection(afterConnection: false)
case .failure(let error):
self.trackSetupDuringLogin(.loginJetpackSetupActivationFailed, failure: error)
self.trackSetupAfterLogin(failure: error)
self.trackSetup(failure: error)
DDLogError("⛔️ Error activating Jetpack: \(error)")
self.setupError = error
self.setupFailed = true
Expand All @@ -281,12 +262,12 @@ private extension JetpackSetupViewModel {
///
func startConnectionWithWebView() {
currentSetupStep = .connection
trackSetupAfterLogin()
connectionType = .web
trackSetup()
let action = JetpackConnectionAction.fetchJetpackConnectionURL { [weak self] result in
guard let self else { return }
switch result {
case .success(let url):
self.trackSetupDuringLogin(.loginJetpackSetupFetchJetpackConnectionURLSuccessful)
/// Checks if the fetch URL is for account connection;
/// if not, use the web view solution to avoid the need for cookie-nonce.
/// Reference: pe5sF9-1le-p2#comment-1942.
Expand All @@ -297,8 +278,7 @@ private extension JetpackSetupViewModel {
}
self.shouldPresentWebView = true
case .failure(let error):
self.trackSetupDuringLogin(.loginJetpackSetupFetchJetpackConnectionURLFailed, failure: error)
self.trackSetupAfterLogin(failure: error)
self.trackSetup(failure: error)
DDLogError("⛔️ Error fetching Jetpack connection URL: \(error)")
self.setupError = error
self.setupFailed = true
Expand Down Expand Up @@ -369,7 +349,6 @@ private extension JetpackSetupViewModel {
let missingWpcomUserError = NSError(domain: Constants.errorDomain,
code: Constants.errorCodeNoWPComUser,
userInfo: [Constants.errorUserInfoReason: Constants.errorUserInfoNoWPComUser])
trackSetupDuringLogin(.loginJetpackSetupCannotFindWPCOMUser, failure: missingWpcomUserError)
if retryCount == Constants.maxRetryCount {
return didFailJetpackConnection(with: missingWpcomUserError)
}
Expand Down Expand Up @@ -436,7 +415,7 @@ private extension JetpackSetupViewModel {

func provisionSiteConnection(blogID: Int64) {
currentSetupStep = .connection
trackSetupAfterLogin()
trackSetup()
stores.dispatch(JetpackConnectionAction.provisionConnection(completion: { [weak self] result in
guard let self else { return }
switch result {
Expand Down Expand Up @@ -475,17 +454,13 @@ private extension JetpackSetupViewModel {
jetpackConnectedEmail = connectedEmail
currentConnectionStep = .authorized
currentSetupStep = .done

trackSetupDuringLogin(.loginJetpackSetupAllStepsMarkedDone)
trackSetupAfterLogin()
trackSetup()
}

func didFailJetpackConnection(with error: Error) {
setupFailed = true
setupError = error
if let setupError {
analytics.track(.loginJetpackSetupErrorCheckingJetpackConnection, withError: setupError)
}
trackSetup(failure: error)
setupFailed = true
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,6 @@ extension JetpackInstallStep {
}
}

/// Description dictionary for Analytics
///
var analyticsDescription: [String: String] {
["jetpack_install_step": analyticsValue]
}

var analyticsValue: String {
switch self {
case .installation:
Expand Down
Loading