Skip to content

Commit 6930254

Browse files
authored
Merge pull request #8034 from woocommerce/issue/8032-banner-design-feedback
Products Onboarding: Update banner design based on review feedback
2 parents 2266c70 + a4e1ed4 commit 6930254

File tree

7 files changed

+78
-24
lines changed

7 files changed

+78
-24
lines changed

Storage/Storage/Model/Feature Announcements/FeatureAnnouncementCampaign.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ public enum FeatureAnnouncementCampaign: String, Codable, Equatable {
55
case linkedProductsPromo = "linked_products_promo"
66
case paymentsInMenuTabBarButton = "payments_menu_tabbar_button"
77
case paymentsInHubMenuButton = "payments_hub_menu_button"
8+
case productsOnboarding = "products_onboarding_first_product"
89

910
/// Added for use in `test_setFeatureAnnouncementDismissed_with_another_campaign_previously_dismissed_keeps_values_for_both`
1011
/// This can be removed when we have a second campaign, which can be used in the above test instead.

WooCommerce/Classes/ViewModels/Feature Announcement Cards/FeatureAnnouncementCardViewModel.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ protocol AnnouncementCardViewModelProtocol {
1717
func onAppear()
1818
func ctaTapped()
1919

20-
var showDismissButton: Bool { get }
2120
var showDismissConfirmation: Bool { get }
2221
var dismissAlertTitle: String { get }
2322
var dismissAlertMessage: String { get }
@@ -46,8 +45,6 @@ class FeatureAnnouncementCardViewModel: AnnouncementCardViewModelProtocol {
4645
config.image
4746
}
4847

49-
var showDismissButton: Bool = true
50-
5148
var showDismissConfirmation: Bool {
5249
config.showDismissConfirmation
5350
}

WooCommerce/Classes/ViewModels/Feature Announcement Cards/JustInTimeMessageAnnouncementCardViewModel.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,7 @@ final class JustInTimeMessageAnnouncementCardViewModel: AnnouncementCardViewMode
6161

6262
let image: UIImage = .paymentsFeatureBannerImage
6363

64-
var showDismissButton: Bool = true
65-
66-
let showDismissConfirmation: Bool = false
64+
var showDismissConfirmation: Bool = false
6765

6866
let dismissAlertTitle: String = ""
6967

WooCommerce/Classes/ViewModels/Feature Announcement Cards/ProductsOnboardingAnnouncementCardViewModel.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import Foundation
22
import UIKit
3+
import enum Yosemite.AppSettingsAction
34

45
struct ProductsOnboardingAnnouncementCardViewModel: AnnouncementCardViewModelProtocol {
5-
var showDividers: Bool = false
6+
var showDividers: Bool = true
67

78
var badgeType: BadgeView.BadgeType = .tip
89

@@ -27,12 +28,15 @@ struct ProductsOnboardingAnnouncementCardViewModel: AnnouncementCardViewModelPro
2728
onCTATapped?()
2829
}
2930

30-
// MARK: Dismiss button (disabled)
31-
32-
var showDismissButton: Bool = false
31+
// MARK: Dismiss button
3332

33+
/// Ensures the banner isn't shown again after the user manually dismisses it.
34+
///
3435
func dontShowAgainTapped() {
35-
// No-op
36+
let action = AppSettingsAction.setFeatureAnnouncementDismissed(campaign: .productsOnboarding,
37+
remindLater: false,
38+
onCompletion: nil)
39+
ServiceLocator.stores.dispatch(action)
3640
}
3741

3842
// MARK: Dismiss confirmation alert (disabled)

WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewModel.swift

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -124,18 +124,14 @@ final class DashboardViewModel {
124124
if isEligible {
125125
ServiceLocator.analytics.track(event: .ProductsOnboarding.storeIsEligible())
126126

127-
if ABTest.productsOnboardingBanner.variation == .treatment(nil) {
128-
let viewModel = ProductsOnboardingAnnouncementCardViewModel(onCTATapped: { [weak self] in
129-
self?.announcementViewModel = nil // Dismiss announcement
130-
MainTabBarController.presentAddProductFlow()
131-
})
132-
self?.announcementViewModel = viewModel
133-
// For now, products onboarding takes precedence over Just In Time Messages,
134-
// so we can stop if there is an onboarding announcement to display.
135-
// This should be revisited when either onboarding or JITMs are expanded. See:
136-
// pe5pgL-11B-p2
137-
return
138-
}
127+
self?.setProductsOnboardingBannerIfNeeded()
128+
}
129+
130+
// For now, products onboarding takes precedence over Just In Time Messages,
131+
// so we can stop if there is an onboarding announcement to display.
132+
// This should be revisited when either onboarding or JITMs are expanded. See: pe5pgL-11B-p2
133+
if self?.announcementViewModel is ProductsOnboardingAnnouncementCardViewModel {
134+
return
139135
}
140136
onCompletion()
141137
case .failure(let error):
@@ -146,6 +142,27 @@ final class DashboardViewModel {
146142
stores.dispatch(action)
147143
}
148144

145+
/// Sets the view model for the products onboarding banner if the user hasn't dismissed it before,
146+
/// and if the user is part of the treatment group for the products onboarding A/B test.
147+
///
148+
private func setProductsOnboardingBannerIfNeeded() {
149+
guard ABTest.productsOnboardingBanner.variation == .treatment(nil) else {
150+
return
151+
}
152+
153+
let getVisibility = AppSettingsAction.getFeatureAnnouncementVisibility(campaign: .productsOnboarding) { [weak self] result in
154+
guard let self else { return }
155+
if case let .success(isVisible) = result, isVisible {
156+
let viewModel = ProductsOnboardingAnnouncementCardViewModel(onCTATapped: { [weak self] in
157+
self?.announcementViewModel = nil // Dismiss announcement
158+
MainTabBarController.presentAddProductFlow()
159+
})
160+
self.announcementViewModel = viewModel
161+
}
162+
}
163+
stores.dispatch(getVisibility)
164+
}
165+
149166
/// Checks for Just In Time Messages and prepares the announcement if needed.
150167
///
151168
private func syncJustInTimeMessages(for siteID: Int64) {

WooCommerce/Classes/ViewRelated/ReusableViews/SwiftUI Components/FeatureAnnouncementCardView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ struct FeatureAnnouncementCardView: View {
3030
BadgeView(type: viewModel.badgeType)
3131
.padding(.leading, Layout.padding)
3232
Spacer()
33-
if viewModel.showDismissButton, let dismiss = dismiss {
33+
if let dismiss = dismiss {
3434
Button(action: {
3535
if viewModel.showDismissConfirmation {
3636
showingDismissActionSheet = true

WooCommerce/WooCommerceTests/ViewRelated/Dashboard/DashboardViewModelTests.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import XCTest
22
import enum Networking.DotcomError
33
import enum Yosemite.StatsActionV4
44
import enum Yosemite.ProductAction
5+
import enum Yosemite.AppSettingsAction
56
import enum Yosemite.JustInTimeMessageAction
67
import struct Yosemite.JustInTimeMessage
78
@testable import WooCommerce
@@ -99,6 +100,14 @@ final class DashboardViewModelTests: XCTestCase {
99100
XCTFail("Received unsupported action: \(action)")
100101
}
101102
}
103+
stores.whenReceivingAction(ofType: AppSettingsAction.self) { action in
104+
switch action {
105+
case let .getFeatureAnnouncementVisibility(_, completion):
106+
completion(.success(true))
107+
default:
108+
XCTFail("Received unsupported action: \(action)")
109+
}
110+
}
102111
stores.whenReceivingAction(ofType: JustInTimeMessageAction.self) { action in
103112
switch action {
104113
case let .loadMessage(_, _, _, completion):
@@ -116,6 +125,34 @@ final class DashboardViewModelTests: XCTestCase {
116125
XCTAssertEqual(viewModel.announcementViewModel?.image, .emptyProductsImage)
117126
}
118127

128+
func test_onboarding_announcement_not_displayed_when_previously_dismissed() {
129+
// Given
130+
MockABTesting.setVariation(.treatment(nil), for: .productsOnboardingBanner)
131+
stores.whenReceivingAction(ofType: ProductAction.self) { action in
132+
switch action {
133+
case let .checkProductsOnboardingEligibility(_, completion):
134+
completion(.success(true))
135+
default:
136+
XCTFail("Received unsupported action: \(action)")
137+
}
138+
}
139+
stores.whenReceivingAction(ofType: AppSettingsAction.self) { action in
140+
switch action {
141+
case let .getFeatureAnnouncementVisibility(_, completion):
142+
completion(.success(false))
143+
default:
144+
XCTFail("Received unsupported action: \(action)")
145+
}
146+
}
147+
let viewModel = DashboardViewModel(stores: stores)
148+
149+
// When
150+
viewModel.syncAnnouncements(for: sampleSiteID)
151+
152+
// Then
153+
XCTAssertNil(viewModel.announcementViewModel)
154+
}
155+
119156
func test_view_model_syncs_just_in_time_messages_when_ineligible_for_products_onboarding() {
120157
// Given
121158
let message = Yosemite.JustInTimeMessage.fake().copy(title: "JITM Message")

0 commit comments

Comments
 (0)