Skip to content

Commit 4abfd84

Browse files
committed
Merge branch 'trunk' into issue/7855-utm-params-and-analytics-on-JITM-cta
# Conflicts: # WooCommerce/Classes/ViewModels/Feature Announcement Cards/JustInTimeMessageAnnouncementCardViewModel.swift
2 parents af42593 + ae3cfc0 commit 4abfd84

File tree

45 files changed

+1868
-255
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1868
-255
lines changed

Networking/Networking/Remote/AccountRemote.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ public enum CreateAccountError: Error, Equatable {
233233
self = .invalidEmail
234234
case Constants.invalidPassword:
235235
self = .invalidPassword(message: message)
236-
case Constants.invalidUsername:
236+
case Constants.invalidUsername, Constants.usernameExists:
237237
self = .invalidUsername
238238
default:
239239
self = .unexpected(error: error)
@@ -249,6 +249,7 @@ public enum CreateAccountError: Error, Equatable {
249249
static let emailExists = "email_exists"
250250
static let invalidEmail = "email_invalid"
251251
static let invalidPassword = "password_invalid"
252-
static let invalidUsername = "username_exists"
252+
static let usernameExists = "username_exists"
253+
static let invalidUsername = "username_invalid"
253254
}
254255
}

Networking/NetworkingTests/Mapper/WCAnalyticsCustomerMapperTests.swift

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class WCAnalyticsCustomerMapperTests: XCTestCase {
3535
customers = try! mapper.map(response: data)
3636

3737
// Then
38-
XCTAssertEqual(customers.count, 3)
38+
XCTAssertEqual(customers.count, 4)
3939
}
4040

4141
func test_WCAnalyticsCustomer_array_response_values_are_correctly_parsed() {
@@ -50,11 +50,13 @@ class WCAnalyticsCustomerMapperTests: XCTestCase {
5050
let customers = try! mapper.map(response: data)
5151

5252
// Then
53-
XCTAssertEqual(customers[0].userID, 1)
54-
XCTAssertEqual(customers[0].name, "John")
55-
XCTAssertEqual(customers[1].userID, 2)
56-
XCTAssertEqual(customers[1].name, "Paul")
57-
XCTAssertEqual(customers[2].userID, 3)
58-
XCTAssertEqual(customers[2].name, "John Doe")
53+
XCTAssertEqual(customers[0].userID, 0)
54+
XCTAssertEqual(customers[0].name, "Matt The Unregistered")
55+
XCTAssertEqual(customers[1].userID, 1)
56+
XCTAssertEqual(customers[1].name, "John")
57+
XCTAssertEqual(customers[2].userID, 2)
58+
XCTAssertEqual(customers[2].name, "Paul")
59+
XCTAssertEqual(customers[3].userID, 3)
60+
XCTAssertEqual(customers[3].name, "John Doe")
5961
}
6062
}

Networking/NetworkingTests/Remote/WCAnalyticsCustomerRemoteTests.swift

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,15 @@ class WCAnalyticsCustomerRemoteTests: XCTestCase {
4545
let customers = try XCTUnwrap(result.get())
4646
let hasSearchParameter = network.queryParameters?.contains(where: { $0 == "search=John" }) ?? false
4747
XCTAssertTrue(hasSearchParameter)
48-
assertEqual(3, customers.count)
49-
assertEqual(1, customers[0].userID)
50-
assertEqual(2, customers[1].userID)
51-
assertEqual(3, customers[2].userID)
52-
assertEqual("John", customers[0].name)
53-
assertEqual("Paul", customers[1].name)
54-
assertEqual("John Doe", customers[2].name)
48+
assertEqual(4, customers.count)
49+
assertEqual(0, customers[0].userID)
50+
assertEqual(1, customers[1].userID)
51+
assertEqual(2, customers[2].userID)
52+
assertEqual(3, customers[3].userID)
53+
assertEqual("Matt The Unregistered", customers[0].name)
54+
assertEqual("John", customers[1].name)
55+
assertEqual("Paul", customers[2].name)
56+
assertEqual("John Doe", customers[3].name)
5557
}
5658

5759
func test_WCAnalyticsCustomerRemote_when_calls_retrieveCustomersByName_fails_then_returns_result_isFailure() {

Networking/NetworkingTests/Responses/wc-analytics-customers.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,24 @@
11
{ "data":
22
[
3+
{
4+
"id":0,
5+
"user_id":0,
6+
"username":"Matt.the.unregistered",
7+
"name":"Matt The Unregistered",
8+
"email":"[email protected]",
9+
"country":"US",
10+
"city":"San Francisco",
11+
"state":"CA",
12+
"postcode":"94103",
13+
"date_registered":null,
14+
"date_last_active":"2022-07-12T08:36:54",
15+
"date_last_order":"2022-07-12 08:36:54",
16+
"orders_count":1,
17+
"total_spend":10,
18+
"avg_order_value":10,
19+
"date_registered_gmt":null,
20+
"date_last_active_gmt":"2022-07-12T08:36:54"
21+
},
322
{
423
"id":1,
524
"user_id":1,
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import enum Yosemite.CreateAccountError
2+
3+
extension WooAnalyticsEvent {
4+
enum StoreCreation {
5+
/// Event property keys.
6+
private enum Key {
7+
static let source = "source"
8+
static let url = "url"
9+
static let errorType = "error_type"
10+
}
11+
12+
/// Tracked when the user taps on the CTA in store picker (logged in to WPCOM) to create a store.
13+
static func sitePickerCreateSiteTapped(source: StorePickerSource) -> WooAnalyticsEvent {
14+
WooAnalyticsEvent(statName: .sitePickerCreateSiteTapped,
15+
properties: [Key.source: source.rawValue])
16+
}
17+
18+
/// Tracked when a site is created from the store creation flow.
19+
static func siteCreated(source: Source, siteURL: String) -> WooAnalyticsEvent {
20+
WooAnalyticsEvent(statName: .siteCreated,
21+
properties: [Key.source: source.rawValue, Key.url: siteURL])
22+
}
23+
24+
/// Tracked when site creation fails.
25+
static func siteCreationFailed(source: Source, error: Error) -> WooAnalyticsEvent {
26+
WooAnalyticsEvent(statName: .siteCreationFailed,
27+
properties: [Key.source: source.rawValue],
28+
error: error)
29+
}
30+
31+
/// Tracked when the user dismisses the store creation flow before the flow is complete.
32+
static func siteCreationDismissed(source: Source) -> WooAnalyticsEvent {
33+
WooAnalyticsEvent(statName: .siteCreationDismissed,
34+
properties: [Key.source: source.rawValue])
35+
}
36+
37+
/// Tracked when the user taps on the CTA in login prologue (logged out) to create a store.
38+
static func loginPrologueCreateSiteTapped() -> WooAnalyticsEvent {
39+
WooAnalyticsEvent(statName: .loginPrologueCreateSiteTapped,
40+
properties: [:])
41+
}
42+
43+
/// Tracked when the user taps on the CTA in the account creation form to log in instead.
44+
static func signupFormLoginTapped() -> WooAnalyticsEvent {
45+
WooAnalyticsEvent(statName: .signupFormLoginTapped,
46+
properties: [:])
47+
}
48+
49+
/// Tracked when the user taps to submit the WPCOM signup form.
50+
static func signupSubmitted() -> WooAnalyticsEvent {
51+
WooAnalyticsEvent(statName: .signupSubmitted,
52+
properties: [:])
53+
}
54+
55+
/// Tracked when WPCOM signup succeeds.
56+
static func signupSuccess() -> WooAnalyticsEvent {
57+
WooAnalyticsEvent(statName: .signupSuccess,
58+
properties: [:])
59+
}
60+
61+
/// Tracked when WPCOM signup fails.
62+
static func signupFailed(error: CreateAccountError) -> WooAnalyticsEvent {
63+
WooAnalyticsEvent(statName: .signupFailed,
64+
properties: [Key.errorType: error.analyticsValue])
65+
}
66+
}
67+
}
68+
69+
extension WooAnalyticsEvent.StoreCreation {
70+
enum StorePickerSource: String {
71+
/// From switching stores.
72+
case switchStores = "switching_stores"
73+
/// From the login flow.
74+
case login
75+
/// The store creation flow is originally initiated from login prologue and dismissed,
76+
/// which lands on the store picker.
77+
case loginPrologue = "prologue"
78+
/// Other sources like from any error screens during the login flow.
79+
case other
80+
}
81+
82+
enum Source: String {
83+
case loginPrologue = "prologue"
84+
case storePicker = "store_picker"
85+
case loginEmailError = "login_email_error"
86+
}
87+
}
88+
89+
private extension CreateAccountError {
90+
var analyticsValue: String {
91+
switch self {
92+
case .emailExists:
93+
return "EMAIL_EXIST"
94+
case .invalidEmail:
95+
return "EMAIL_INVALID"
96+
case .invalidPassword:
97+
return "PASSWORD_INVALID"
98+
default:
99+
return "\(self)"
100+
}
101+
}
102+
}

WooCommerce/Classes/Analytics/WooAnalyticsEvent.swift

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1579,11 +1579,6 @@ extension WooAnalyticsEvent {
15791579
case hasValidJetpack = "has_valid_jetpack"
15801580
}
15811581

1582-
/// Tracks when the user taps the Enter Your Store Address button
1583-
static func enterStoreAddressTapped() -> WooAnalyticsEvent {
1584-
WooAnalyticsEvent(statName: .sitePickerEnterStoreAddressTapped, properties: [:])
1585-
}
1586-
15871582
/// Tracks when the result for site discovery is returned
15881583
static func siteDiscovery(hasWordPress: Bool, isWPCom: Bool, hasValidJetpack: Bool) -> WooAnalyticsEvent {
15891584
WooAnalyticsEvent(statName: .sitePickerSiteDiscovery, properties: [Key.hasWordPress.rawValue: hasWordPress,
@@ -1655,3 +1650,21 @@ extension WooAnalyticsEvent {
16551650
}
16561651
}
16571652
}
1653+
1654+
// MARK: - Products Onboarding
1655+
//
1656+
extension WooAnalyticsEvent {
1657+
enum ProductsOnboarding {
1658+
/// Tracks when a store is eligible for products onboarding
1659+
///
1660+
static func storeIsEligible() -> WooAnalyticsEvent {
1661+
WooAnalyticsEvent(statName: .productsOnboardingEligible, properties: [:])
1662+
}
1663+
1664+
/// Tracks when the call to action is tapped on the products onboarding banner
1665+
///
1666+
static func bannerCTATapped() -> WooAnalyticsEvent {
1667+
WooAnalyticsEvent(statName: .productsOnboardingCTATapped, properties: [:])
1668+
}
1669+
}
1670+
}

WooCommerce/Classes/Analytics/WooAnalyticsStat.swift

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,19 +111,33 @@ public enum WooAnalyticsStat: String {
111111
case dashboardNewStatsRevertedBannerLearnMoreTapped = "dashboard_new_stats_reverted_banner_learn_more_tapped"
112112
case usedAnalytics = "used_analytics"
113113

114-
// MARK: Onboarding Events
114+
// MARK: Products Onboarding Events
115115
//
116116
case productsOnboardingEligible = "products_onboarding_store_is_eligible"
117+
case productsOnboardingCTATapped = "products_onboarding_cta_tapped"
117118

118119
// MARK: Site picker. Can be triggered by login epilogue or settings.
119120
//
120121
case sitePickerContinueTapped = "site_picker_continue_tapped"
121122
case sitePickerStoresShown = "site_picker_stores_shown"
122123
case sitePickerHelpButtonTapped = "site_picker_help_button_tapped"
123124
case sitePickerNonWooSiteTapped = "site_picker_non_woo_site_tapped"
124-
case sitePickerEnterStoreAddressTapped = "site_picker_enter_store_address_tapped"
125125
case sitePickerSiteDiscovery = "site_picker_site_discovery"
126126
case sitePickerNewToWooTapped = "site_picker_new_to_woo_tapped"
127+
case sitePickerAddStoreTapped = "site_picker_add_a_store_tapped"
128+
case sitePickerConnectExistingStoreTapped = "site_picker_connect_existing_store_tapped"
129+
130+
// MARK: Site creation
131+
//
132+
case sitePickerCreateSiteTapped = "site_picker_create_site_tapped"
133+
case siteCreated = "login_woocommerce_site_created"
134+
case siteCreationFailed = "site_creation_failed"
135+
case siteCreationDismissed = "site_creation_dismissed"
136+
case loginPrologueCreateSiteTapped = "login_prologue_create_site_tapped"
137+
case signupFormLoginTapped = "signup_login_button_tapped"
138+
case signupSubmitted = "signup_submitted"
139+
case signupSuccess = "signup_success"
140+
case signupFailed = "signup_failed"
127141

128142
// MARK: Help & Support Events
129143
//

WooCommerce/Classes/Authentication/AuthenticationManager.swift

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ class AuthenticationManager: Authentication {
1818
///
1919
private var storePickerCoordinator: StorePickerCoordinator?
2020

21+
/// Store creation coordinator in the logged-out state.
22+
private var loggedOutStoreCreationCoordinator: LoggedOutStoreCreationCoordinator?
23+
2124
/// Keychain access for SIWA auth token
2225
///
2326
private lazy var keychain = Keychain(service: WooConstants.keychainServiceName)
@@ -543,8 +546,12 @@ extension AuthenticationManager: WordPressAuthenticatorDelegate {
543546

544547
// Navigate to store creation
545548
func showSiteCreation(in navigationController: UINavigationController) {
546-
// TODO: add tracks
547-
// Navigate to store creation
549+
ServiceLocator.analytics.track(event: .StoreCreation.loginPrologueCreateSiteTapped())
550+
551+
let coordinator = LoggedOutStoreCreationCoordinator(source: .prologue,
552+
navigationController: navigationController)
553+
self.loggedOutStoreCreationCoordinator = coordinator
554+
coordinator.start()
548555
}
549556
}
550557

@@ -628,8 +635,12 @@ private extension AuthenticationManager {
628635
onDismiss: @escaping () -> Void = {}) {
629636
let config: StorePickerConfiguration = {
630637
switch source {
631-
case .custom(let source) where source == StoreCreationCoordinator.Source.prologue.rawValue:
632-
return .storeCreationFromLoginPrologue
638+
case .custom(let source):
639+
if let loggedOutSource = LoggedOutStoreCreationCoordinator.Source(rawValue: source) {
640+
return .storeCreationFromLogin(source: loggedOutSource)
641+
} else {
642+
return .login
643+
}
633644
default:
634645
return .login
635646
}

WooCommerce/Classes/Authentication/Epilogue/AccountHeaderView.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ class AccountHeaderView: UIView {
3737
///
3838
@IBOutlet private weak var helpButton: UIButton!
3939

40+
@IBOutlet private var containerView: UIView!
41+
4042
/// Closure to be executed whenever the help button is pressed
4143
///
4244
var onHelpRequested: (() -> Void)?
@@ -46,6 +48,7 @@ class AccountHeaderView: UIView {
4648
override func awakeFromNib() {
4749
super.awakeFromNib()
4850
setupHelpButton()
51+
configureContainerView()
4952
}
5053
}
5154

@@ -98,6 +101,12 @@ extension AccountHeaderView {
98101
//
99102
private extension AccountHeaderView {
100103

104+
func configureContainerView() {
105+
containerView.layer.borderWidth = 1
106+
containerView.layer.borderColor = UIColor.border.cgColor
107+
containerView.layer.cornerRadius = 8
108+
}
109+
101110
func setupHelpButton() {
102111
helpButton.setTitle(Strings.helpButtonTitle, for: .normal)
103112
helpButton.setTitleColor(.accent, for: .normal)

0 commit comments

Comments
 (0)