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
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public enum FeatureAnnouncementCampaign: String, Codable, Equatable {
case linkedProductsPromo = "linked_products_promo"
case paymentsInMenuTabBarButton = "payments_menu_tabbar_button"
case paymentsInHubMenuButton = "payments_hub_menu_button"
case productsOnboarding = "products_onboarding_first_product"

/// Added for use in `test_setFeatureAnnouncementDismissed_with_another_campaign_previously_dismissed_keeps_values_for_both`
/// This can be removed when we have a second campaign, which can be used in the above test instead.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ protocol AnnouncementCardViewModelProtocol {
func onAppear()
func ctaTapped()

var showDismissButton: Bool { get }
var showDismissConfirmation: Bool { get }
var dismissAlertTitle: String { get }
var dismissAlertMessage: String { get }
Expand Down Expand Up @@ -46,8 +45,6 @@ class FeatureAnnouncementCardViewModel: AnnouncementCardViewModelProtocol {
config.image
}

var showDismissButton: Bool = true

var showDismissConfirmation: Bool {
config.showDismissConfirmation
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,7 @@ final class JustInTimeMessageAnnouncementCardViewModel: AnnouncementCardViewMode

let image: UIImage = .paymentsFeatureBannerImage

var showDismissButton: Bool = true

let showDismissConfirmation: Bool = false
var showDismissConfirmation: Bool = false

let dismissAlertTitle: String = ""

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import Foundation
import UIKit
import enum Yosemite.AppSettingsAction

struct ProductsOnboardingAnnouncementCardViewModel: AnnouncementCardViewModelProtocol {
var showDividers: Bool = false
var showDividers: Bool = true

var badgeType: BadgeView.BadgeType = .tip

Expand All @@ -27,12 +28,15 @@ struct ProductsOnboardingAnnouncementCardViewModel: AnnouncementCardViewModelPro
onCTATapped?()
}

// MARK: Dismiss button (disabled)

var showDismissButton: Bool = false
// MARK: Dismiss button

/// Ensures the banner isn't shown again after the user manually dismisses it.
///
func dontShowAgainTapped() {
// No-op
let action = AppSettingsAction.setFeatureAnnouncementDismissed(campaign: .productsOnboarding,
remindLater: false,
onCompletion: nil)
ServiceLocator.stores.dispatch(action)
}

// MARK: Dismiss confirmation alert (disabled)
Expand Down
41 changes: 29 additions & 12 deletions WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -124,18 +124,14 @@ final class DashboardViewModel {
if isEligible {
ServiceLocator.analytics.track(event: .ProductsOnboarding.storeIsEligible())

if ABTest.productsOnboardingBanner.variation == .treatment(nil) {
let viewModel = ProductsOnboardingAnnouncementCardViewModel(onCTATapped: { [weak self] in
self?.announcementViewModel = nil // Dismiss announcement
MainTabBarController.presentAddProductFlow()
})
self?.announcementViewModel = viewModel
// For now, products onboarding takes precedence over Just In Time Messages,
// so we can stop if there is an onboarding announcement to display.
// This should be revisited when either onboarding or JITMs are expanded. See:
// pe5pgL-11B-p2
return
}
self?.setProductsOnboardingBannerIfNeeded()
}

// For now, products onboarding takes precedence over Just In Time Messages,
// so we can stop if there is an onboarding announcement to display.
// This should be revisited when either onboarding or JITMs are expanded. See: pe5pgL-11B-p2
if self?.announcementViewModel is ProductsOnboardingAnnouncementCardViewModel {
return
}
onCompletion()
case .failure(let error):
Expand All @@ -146,6 +142,27 @@ final class DashboardViewModel {
stores.dispatch(action)
}

/// Sets the view model for the products onboarding banner if the user hasn't dismissed it before,
/// and if the user is part of the treatment group for the products onboarding A/B test.
///
private func setProductsOnboardingBannerIfNeeded() {
guard ABTest.productsOnboardingBanner.variation == .treatment(nil) else {
return
}

let getVisibility = AppSettingsAction.getFeatureAnnouncementVisibility(campaign: .productsOnboarding) { [weak self] result in
guard let self else { return }
if case let .success(isVisible) = result, isVisible {
let viewModel = ProductsOnboardingAnnouncementCardViewModel(onCTATapped: { [weak self] in
self?.announcementViewModel = nil // Dismiss announcement
MainTabBarController.presentAddProductFlow()
})
self.announcementViewModel = viewModel
}
}
stores.dispatch(getVisibility)
}

/// Checks for Just In Time Messages and prepares the announcement if needed.
///
private func syncJustInTimeMessages(for siteID: Int64) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ struct FeatureAnnouncementCardView: View {
BadgeView(type: viewModel.badgeType)
.padding(.leading, Layout.padding)
Spacer()
if viewModel.showDismissButton, let dismiss = dismiss {
if let dismiss = dismiss {
Button(action: {
if viewModel.showDismissConfirmation {
showingDismissActionSheet = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import XCTest
import enum Networking.DotcomError
import enum Yosemite.StatsActionV4
import enum Yosemite.ProductAction
import enum Yosemite.AppSettingsAction
import enum Yosemite.JustInTimeMessageAction
import struct Yosemite.JustInTimeMessage
@testable import WooCommerce
Expand Down Expand Up @@ -99,6 +100,14 @@ final class DashboardViewModelTests: XCTestCase {
XCTFail("Received unsupported action: \(action)")
}
}
stores.whenReceivingAction(ofType: AppSettingsAction.self) { action in
switch action {
case let .getFeatureAnnouncementVisibility(_, completion):
completion(.success(true))
default:
XCTFail("Received unsupported action: \(action)")
}
}
stores.whenReceivingAction(ofType: JustInTimeMessageAction.self) { action in
switch action {
case let .loadMessage(_, _, _, completion):
Expand All @@ -116,6 +125,34 @@ final class DashboardViewModelTests: XCTestCase {
XCTAssertEqual(viewModel.announcementViewModel?.image, .emptyProductsImage)
}

func test_onboarding_announcement_not_displayed_when_previously_dismissed() {
// Given
MockABTesting.setVariation(.treatment(nil), for: .productsOnboardingBanner)
stores.whenReceivingAction(ofType: ProductAction.self) { action in
switch action {
case let .checkProductsOnboardingEligibility(_, completion):
completion(.success(true))
default:
XCTFail("Received unsupported action: \(action)")
}
}
stores.whenReceivingAction(ofType: AppSettingsAction.self) { action in
switch action {
case let .getFeatureAnnouncementVisibility(_, completion):
completion(.success(false))
default:
XCTFail("Received unsupported action: \(action)")
}
}
let viewModel = DashboardViewModel(stores: stores)

// When
viewModel.syncAnnouncements(for: sampleSiteID)

// Then
XCTAssertNil(viewModel.announcementViewModel)
}

func test_view_model_syncs_just_in_time_messages_when_ineligible_for_products_onboarding() {
// Given
let message = Yosemite.JustInTimeMessage.fake().copy(title: "JITM Message")
Expand Down