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
20 changes: 17 additions & 3 deletions WooCommerce/Classes/Analytics/TracksProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,16 @@ public extension TracksProvider {
//
private extension TracksProvider {
func refreshMetadata() {
DDLogInfo("♻️ Refreshing tracks metadata...")
var userProperties = [String: Any]()
userProperties["platform"] = "iOS"
userProperties["accessibility_voice_over_enabled"] = UIAccessibility.isVoiceOverRunning
userProperties["is_rtl_language"] = (UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft)
userProperties[UserProperties.platformKey] = "iOS"
userProperties[UserProperties.voiceOverKey] = UIAccessibility.isVoiceOverRunning
userProperties[UserProperties.rtlKey] = (UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft)
if StoresManager.shared.isAuthenticated {
let site = StoresManager.shared.sessionManager.defaultSite
userProperties[UserProperties.blogIDKey] = site?.siteID
userProperties[UserProperties.wpcomStoreKey] = site?.isWordPressStore
}
tracksService.userProperties.removeAllObjects()
tracksService.userProperties.addEntries(from: userProperties)
}
Expand All @@ -69,4 +75,12 @@ private extension TracksProvider {
enum Constants {
static let eventNamePrefix = "woocommerceios"
}

enum UserProperties {
static let platformKey = "platform"
static let voiceOverKey = "accessibility_voice_over_enabled"
static let rtlKey = "is_rtl_language"
static let blogIDKey = "blog_id"
static let wpcomStoreKey = "is_wpcom_store"
}
}
73 changes: 70 additions & 3 deletions WooCommerce/Classes/Analytics/WooAnalyticsStat.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import WordPressShared


/// This enum contains all of the events we track in the app.
/// This enum contains all of the events we track in the app. Please reference the "Woo Mobile Events Draft i2"
/// spreadsheet for more details.
///
public enum WooAnalyticsStat: String {

// Application Events
//
case applicationInstalled = "application_installed"
case applicationUpgraded = "application_upgraded"
case applicationOpened = "application_opened"
case applicationClosed = "application_closed"

Expand All @@ -18,8 +21,6 @@ public enum WooAnalyticsStat: String {
case loginFailed = "login_failed_to_login"
case loginAutoFillCredentialsFilled = "login_autofill_credentials_filled"
case loginAutoFillCredentialsUpdated = "login_autofill_credentials_updated"
case loginProloguePaged = "login_prologue_paged"
case loginPrologueViewed = "login_prologue_viewed"
case loginEmailFormViewed = "login_email_form_viewed"
case loginMagicLinkOpenEmailClientViewed = "login_magic_link_open_email_client_viewed"
case loginMagicLinkRequestFormViewed = "login_magic_link_request_form_viewed"
Expand All @@ -34,6 +35,12 @@ public enum WooAnalyticsStat: String {
case loginUsernamePasswordFormViewed = "login_username_password_form_viewed"
case loginTwoFactorFormViewed = "login_two_factor_form_viewed"
case loginEpilogueViewed = "login_epilogue_viewed"
case loginEpilogueStoresShown = "login_epilogue_stores_shown"
case loginEpilogueContinueTapped = "login_epilogue_store_picker_continue_tapped"
case loginProloguePaged = "login_prologue_paged"
case loginPrologueViewed = "login_prologue_viewed"
case loginPrologueContinueTapped = "login_prologue_jetpack_login_button_tapped"
case loginPrologueJetpackInstructions = "login_prologue_jetpack_configuration_instructions_link_tapped"
case loginForgotPasswordClicked = "login_forgot_password_clicked"
case loginSocialButtonClick = "login_social_button_click"
case loginSocialButtonFailure = "login_social_button_failure"
Expand All @@ -49,6 +56,66 @@ public enum WooAnalyticsStat: String {
case onePasswordSignup = "one_password_signup"
case twoFactorCodeRequested = "two_factor_code_requested"
case twoFactorSentSMS = "two_factor_sent_sms"

// Dashboard View Events
//
case dashboardSelected = "main_tab_dashboard_selected"
case dashboardReselected = "main_tab_dashboard_reselected"
case dashboardPulledToRefresh = "dashboard_pulled_to_refresh"
case dashboardNewOrdersButtonTapped = "dashboard_unfulfilled_orders_button_tapped"

// Dashboard Data/Action Events
//
case dashboardMainStatsDate = "dashboard_main_stats_date"
case dashboardMainStatsLoaded = "dashboard_main_stats_loaded"
case dashboardTopPerformersDate = "dashboard_top_performers_date"
case dashboardTopPerformersLoaded = "dashboard_top_performers_loaded"
case dashboardUnfulfilledOrdersLoaded = "dashboard_unfulfilled_orders_loaded"

// Settings View Events
//
case settingsTapped = "main_menu_settings_tapped"
case settingsContactSupportTapped = "main_menu_contact_support_tapped"
case settingsLogoutTapped = "settings_logout_button_tapped"
case settingsLogoutConfirmation = "settings_logout_confirmation_dialog_result"

// Order View Events
//
case ordersSelected = "main_tab_orders_selected"
case ordersReselected = "main_tab_orders_reselected"
case ordersListPulledToRefresh = "orders_list_pulled_to_refresh"
case ordersListFilterTapped = "orders_list_menu_filter_tapped"
case filterOrdersOptionSelected = "filter_orders_by_status_dialog_option_selected"
case orderDetailAddNoteButtonTapped = "order_detail_add_note_button_tapped"
case orderDetailPulledToRefresh = "order_detail_pulled_to_refresh"
case orderNoteAddButtonTapped = "add_order_note_add_button_tapped"
case orderNoteEmailCustomerToggled = "add_order_note_email_note_to_customer_toggled"
case orderDetailShowBillingTapped = "order_detail_customer_info_show_billing_tapped"
case orderDetailHideBillingTapped = "order_detail_customer_info_hide_billing_tapped"
case orderDetailFulfillButtonTapped = "order_detail_fulfill_order_button_tapped"
case orderDetailCustomerEmailTapped = "order_detail_customer_info_email_menu_email_tapped"
case orderDetailCustomerPhoneMenuTapped = "order_detail_customer_info_phone_menu_tapped"
case orderDetailCustomerPhoneOptionTapped = "order_detail_customer_info_phone_menu_phone_tapped"
case orderDetailCustomerSMSOptionTapped = "order_detail_customer_info_phone_menu_sms_tapped"
case orderDetailProductDetailTapped = "order_detail_product_detail_button_tapped"
case orderFulfillmentCompleteButtonTapped = "order_fulfillment_mark_order_complete_button_tapped"
case orderMarkedCompleteUndoButtonTapped = "snack_order_marked_complete_undo_button_tapped"

// Order Data/Action Events
//
case orderOpen = "order_open"
case orderNotesLoaded = "order_notes_loaded"
case orderNoteAdd = "order_note_add"
case orderNoteAddSuccess = "order_note_add_success"
case orderNoteAddFailed = "order_note_add_failed"
case orderContactAction = "order_contact_action"
case ordersListFilterOrSearch = "orders_list_filter"
case ordersListLoaded = "orders_list_loaded"

// Notification Events
//
case notificationsSelected = "main_tab_notifications_selected"
case notificationsReselected = "main_tab_notifications_reselected"
}

public extension WooAnalyticsStat {
Expand Down
21 changes: 21 additions & 0 deletions WooCommerce/Classes/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// Yosemite Initialization
synchronizeEntitiesIfPossible()

// Upgrade check...
checkForUpgrades()

return true
}

Expand Down Expand Up @@ -198,6 +201,24 @@ private extension AppDelegate {
}


private extension AppDelegate {

func checkForUpgrades() {
let currentVersion = UserAgent.bundleShortVersion
let versionOfLastRun = UserDefaults.standard[.versionOfLastRun] as? String
if versionOfLastRun == nil {
// First run after a fresh install
WooAnalytics.shared.track(.applicationInstalled)
} else if versionOfLastRun != currentVersion {
// App was upgraded
WooAnalytics.shared.track(.applicationInstalled, withProperties: ["previous_version": versionOfLastRun ?? String()])
}

UserDefaults.standard[.versionOfLastRun] = currentVersion
}
}


// MARK: - Authentication Methods
//
extension AppDelegate {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ private extension StorePickerViewController {

func refreshResults() {
try? resultsController.performFetch()
WooAnalytics.shared.track(.loginEpilogueStoresShown, withProperties: ["num_of_stores": resultsController.numberOfObjects])
state = StorePickerState(sites: resultsController.fetchedObjects)
}

Expand Down Expand Up @@ -229,6 +230,11 @@ extension StorePickerViewController {
case .empty:
restartAuthentication()
default:
// We need to call refreshUserData() here because the user selected
// their default store and tracks should to know about it.
WooAnalytics.shared.refreshUserData()
WooAnalytics.shared.track(.loginEpilogueContinueTapped,
withProperties: ["selected_store_id": StoresManager.shared.sessionManager.defaultStoreID ?? String()])
dismiss(animated: true, completion: nil)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ extension LoginPrologueViewController {
/// Opens SafariViewController at the specified URL.
///
func displaySafariViewController(at url: URL) {
WooAnalytics.shared.track(.loginPrologueJetpackInstructions)
let safariViewController = SafariViewController(url: url)
safariViewController.modalPresentationStyle = .pageSheet
present(safariViewController, animated: true, completion: nil)
Expand All @@ -134,6 +135,7 @@ extension LoginPrologueViewController {
/// Proceeds with the Login Flow.
///
@IBAction func loginWasPressed() {
WooAnalytics.shared.track(.loginPrologueContinueTapped)
let loginViewController = AppDelegate.shared.authenticationManager.loginForWordPressDotCom()

navigationController?.pushViewController(loginViewController, animated: true)
Expand Down
1 change: 1 addition & 0 deletions WooCommerce/Classes/Extensions/UserDefaults+Woo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ extension UserDefaults {
case defaultUsername
case defaultStoreID
case defaultAnonymousID
case versionOfLastRun
}
}

Expand Down
4 changes: 2 additions & 2 deletions WooCommerce/Classes/Tools/UserAgent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ class UserAgent {

/// Returns the WebKit User Agent
///
private static var webkitUserAgent: String {
static var webkitUserAgent: String {
return UIWebView().stringByEvaluatingJavaScript(from: Constants.loadUserAgentScript) ?? String()
}

/// Returns the Bundle Version ID
///
private static var bundleShortVersion: String {
static var bundleShortVersion: String {
let version = Bundle.main.object(forInfoDictionaryKey: Constants.shortVersionKey) as? String
return version ?? String()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,12 @@ private extension DashboardViewController {
private extension DashboardViewController {

@objc func settingsTapped() {
WooAnalytics.shared.track(.settingsTapped)
performSegue(withIdentifier: Segues.settingsSegue, sender: nil)
}

@objc func pullToRefresh() {
WooAnalytics.shared.track(.dashboardPulledToRefresh)
applyHideAnimation(for: newOrdersContainerView)
reloadData()
}
Expand All @@ -122,8 +124,10 @@ extension DashboardViewController: NewOrdersDelegate {
func didUpdateNewOrdersData(hasNewOrders: Bool) {
if hasNewOrders {
applyUnhideAnimation(for: newOrdersContainerView)
WooAnalytics.shared.track(.dashboardUnfulfilledOrdersLoaded, withProperties: ["has_unfulfilled_orders": "true"])
} else {
applyHideAnimation(for: newOrdersContainerView)
WooAnalytics.shared.track(.dashboardUnfulfilledOrdersLoaded, withProperties: ["has_unfulfilled_orders": "false"])
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ private extension NewOrdersViewController {

@IBAction func buttonTouchUpInside(_ sender: UIButton) {
sender.fadeOutSelectedBackground {
WooAnalytics.shared.track(.dashboardNewOrdersButtonTapped)
MainTabBarController.switchToOrdersTab(filter: .processing)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class PeriodDataViewController: UIViewController, IndicatorInfoProvider {
private var lastUpdatedDate: Date?
private var yAxisMinimum: String = Constants.chartYAxisMinimum.friendlyString()
private var yAxisMaximum: String = ""
private var isInitialLoad: Bool = true // Used in trackChangedTabIfNeeded()

public let granularity: StatGranularity
public var orderStats: OrderStats? {
Expand Down Expand Up @@ -109,6 +110,7 @@ class PeriodDataViewController: UIViewController, IndicatorInfoProvider {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
reloadAllFields()
trackChangedTabIfNeeded()
}

override func viewDidDisappear(_ animated: Bool) {
Expand Down Expand Up @@ -309,6 +311,16 @@ private extension PeriodDataViewController {
//
private extension PeriodDataViewController {

func trackChangedTabIfNeeded() {
// This is a little bit of a workaround to prevent the "tab tapped" tracks event from firing when launching the app.
if granularity == .day && isInitialLoad {
isInitialLoad = false
return
}
WooAnalytics.shared.track(.dashboardMainStatsDate, withProperties: ["range": granularity.rawValue])
isInitialLoad = false
}

func reloadAllFields() {
reloadOrderFields()
reloadSiteFields()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ private extension StoreStatsViewController {

let vc = self.periodDataVC(for: granularity)
vc?.orderStats = orderStats
WooAnalytics.shared.track(.dashboardMainStatsLoaded, withProperties: ["granularity": granularity.rawValue])
onCompletion?(nil)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class TopPerformerDataViewController: UIViewController, IndicatorInfoProvider {
return ResultsController<StorageTopEarnerStats>(storageManager: storageManager, matching: predicate, sortedBy: [descriptor])
}()

private var isInitialLoad: Bool = true // Used in trackChangedTabIfNeeded()

// MARK: - Computed Properties

private var topEarnerStats: TopEarnerStats? {
Expand Down Expand Up @@ -72,6 +74,11 @@ class TopPerformerDataViewController: UIViewController, IndicatorInfoProvider {
registerTableViewCells()
registerTableViewHeaderFooters()
}

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
trackChangedTabIfNeeded()
}
}


Expand All @@ -85,9 +92,11 @@ extension TopPerformerDataViewController {
return
}

let action = StatsAction.retrieveTopEarnerStats(siteID: siteID, granularity: granularity, latestDateToInclude: Date()) { (error) in
let action = StatsAction.retrieveTopEarnerStats(siteID: siteID, granularity: granularity, latestDateToInclude: Date()) { [weak self] (error) in
if let error = error {
DDLogError("⛔️ Dashboard (Top Performers) — Error synchronizing top earner stats: \(error)")
} else {
WooAnalytics.shared.track(.dashboardTopPerformersLoaded, withProperties: ["granularity": self?.granularity.rawValue ?? String()])
}
}
StoresManager.shared.dispatch(action)
Expand Down Expand Up @@ -208,6 +217,16 @@ extension TopPerformerDataViewController: UITableViewDelegate {
//
private extension TopPerformerDataViewController {

func trackChangedTabIfNeeded() {
// This is a little bit of a workaround to prevent the "tab tapped" tracks event from firing when launching the app.
if granularity == .day && isInitialLoad {
isInitialLoad = false
return
}
WooAnalytics.shared.track(.dashboardTopPerformersDate, withProperties: ["range": granularity.rawValue])
isInitialLoad = false
}

func statsItem(at indexPath: IndexPath) -> TopEarnerStatsItem? {
guard let topEarnerStatsItem = topEarnerStats?.items?.sorted(by: >)[safe: indexPath.row] else {
return nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,22 +123,27 @@ private extension SettingsViewController {
private extension SettingsViewController {

func logoutWasPressed() {
WooAnalytics.shared.track(.settingsLogoutTapped)
let messageUnformatted = NSLocalizedString("Are you sure you want to log out of the account %@?", comment: "Alert message to confirm a user meant to log out.")
let messageFormatted = String(format: messageUnformatted, accountName)
let alertController = UIAlertController(title: "", message: messageFormatted, preferredStyle: .alert)

let cancelText = NSLocalizedString("Back", comment: "Alert button title - dismisses alert, which cancels the log out attempt")
alertController.addActionWithTitle(cancelText, style: .cancel)
alertController.addActionWithTitle(cancelText, style: .cancel) { _ in
WooAnalytics.shared.track(.settingsLogoutConfirmation, withProperties: ["result": "negative"])
}

let logoutText = NSLocalizedString("Log Out", comment: "Alert button title - confirms and logs out the user")
alertController.addDefaultActionWithTitle(logoutText) { _ in
WooAnalytics.shared.track(.settingsLogoutConfirmation, withProperties: ["result": "positive"])
self.logOutUser()
}

present(alertController, animated: true)
}

func supportWasPressed() {
WooAnalytics.shared.track(.settingsContactSupportTapped)
guard shouldDisplayEmailComposer() else {
displayContactUsAlert()
return
Expand All @@ -148,7 +153,6 @@ private extension SettingsViewController {
}

func logOutUser() {
WooAnalytics.shared.track(.logout)
StoresManager.shared.deauthenticate()
navigationController?.popToRootViewController(animated: true)
}
Expand Down
Loading