Skip to content

Commit 95a8b27

Browse files
[CIAB] Hide blaze and stock cards from dashboard for CIAB sites (#16116)
2 parents 710519d + a604522 commit 95a8b27

File tree

4 files changed

+225
-12
lines changed

4 files changed

+225
-12
lines changed

WooCommerce/Classes/ViewRelated/Dashboard/Blaze/BlazeCampaignDashboardViewModel.swift

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ final class BlazeCampaignDashboardViewModel: ObservableObject {
7373
private let storageManager: StorageManagerType
7474
private let analytics: Analytics
7575

76-
private var isSiteEligibleForBlaze = false
76+
@Published private var isSiteEligibleForBlaze = false
7777
private let blazeEligibilityChecker: BlazeEligibilityCheckerProtocol
7878

7979
/// Blaze campaign ResultsController.
@@ -103,12 +103,13 @@ final class BlazeCampaignDashboardViewModel: ObservableObject {
103103
)
104104
}()
105105

106-
private(set) var latestPublishedProduct: BlazeCampaignProduct?
106+
@Published private(set) var latestPublishedProduct: BlazeCampaignProduct?
107107

108108
private var subscriptions: Set<AnyCancellable> = []
109109

110110
@Published private var syncingError: Error?
111111

112+
private var cancellables = Set<AnyCancellable>()
112113

113114
init(siteID: Int64,
114115
stores: StoresManager = ServiceLocator.stores,
@@ -122,15 +123,42 @@ final class BlazeCampaignDashboardViewModel: ObservableObject {
122123
self.blazeEligibilityChecker = blazeEligibilityChecker
123124
self.state = .loading
124125

126+
observeIsSiteEligibleForBlaze()
125127
observeSectionVisibility()
126128
configureResultsController()
127129
}
128130

131+
func observeIsSiteEligibleForBlaze() {
132+
stores.site
133+
.removeDuplicates()
134+
.sink { site in
135+
Task { [weak self] in
136+
guard
137+
let self,
138+
let site
139+
else {
140+
return
141+
}
142+
143+
await updateIsSiteEligibleForBlaze(site)
144+
updateAvailability()
145+
}
146+
}
147+
.store(in: &cancellables)
148+
}
149+
150+
func updateIsSiteEligibleForBlaze() async {
151+
guard let site = stores.sessionManager.defaultSite else {
152+
return
153+
}
154+
155+
await updateIsSiteEligibleForBlaze(site)
156+
}
157+
129158
@MainActor
130159
func checkAvailability() async {
131-
isSiteEligibleForBlaze = await checkSiteEligibility()
160+
await updateIsSiteEligibleForBlaze()
132161
try? await synchronizePublishedProducts()
133-
updateAvailability()
134162
}
135163

136164
@MainActor
@@ -140,7 +168,7 @@ final class BlazeCampaignDashboardViewModel: ObservableObject {
140168

141169
analytics.track(event: .DynamicDashboard.cardLoadingStarted(type: .blaze))
142170

143-
isSiteEligibleForBlaze = await checkSiteEligibility()
171+
await updateIsSiteEligibleForBlaze()
144172

145173
guard isSiteEligibleForBlaze else {
146174
update(state: .empty)
@@ -221,13 +249,14 @@ private extension BlazeCampaignDashboardViewModel {
221249
})
222250
}
223251

224-
func checkSiteEligibility() async -> Bool {
225-
guard let site = stores.sessionManager.defaultSite else {
226-
return false
227-
}
252+
func checkSiteEligibility(_ site: Site) async -> Bool {
228253
return await blazeEligibilityChecker.isSiteEligible(site)
229254
}
230255

256+
func updateIsSiteEligibleForBlaze(_ site: Site) async {
257+
isSiteEligibleForBlaze = await checkSiteEligibility(site)
258+
}
259+
231260
@MainActor
232261
func synchronizeBlazeCampaigns() async throws {
233262
try await withCheckedThrowingContinuation({ continuation in

WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewModel.swift

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ final class DashboardViewModel: ObservableObject {
6969

7070
@Published private(set) var isEligibleForInbox = false
7171

72+
@Published private(set) var isEligibleForStock = false
73+
7274
@Published var showingCustomization = false
7375

7476
@Published private(set) var showNewCardsNotice = false
@@ -83,6 +85,7 @@ final class DashboardViewModel: ObservableObject {
8385
private let userDefaults: UserDefaults
8486
private let storageManager: StorageManagerType
8587
private let inboxEligibilityChecker: InboxEligibilityChecker
88+
private let siteIsCIABEligibilityChecker: CIABEligibilityCheckerProtocol
8689
private let usageTracksEventEmitter: StoreStatsUsageTracksEventEmitter
8790
private let blazeLocalNotificationScheduler: BlazeLocalNotificationScheduler
8891
private let tapToPayAwarenessMomentDeterminer: TapToPayAwarenessMomentDetermining
@@ -118,6 +121,7 @@ final class DashboardViewModel: ObservableObject {
118121
blazeEligibilityChecker: BlazeEligibilityCheckerProtocol = BlazeEligibilityChecker(),
119122
inboxEligibilityChecker: InboxEligibilityChecker = InboxEligibilityUseCase(),
120123
googleAdsEligibilityChecker: GoogleAdsEligibilityChecker = DefaultGoogleAdsEligibilityChecker(),
124+
siteIsCIABEligibilityChecker: CIABEligibilityCheckerProtocol = CIABEligibilityChecker(),
121125
localNotificationScheduler: BlazeLocalNotificationScheduler? = nil,
122126
tapToPayAwarenessMomentDeterminer: TapToPayAwarenessMomentDetermining = TapToPayAwarenessMomentDeterminer()) {
123127
self.siteID = siteID
@@ -147,6 +151,7 @@ final class DashboardViewModel: ObservableObject {
147151
)
148152

149153
self.inboxEligibilityChecker = inboxEligibilityChecker
154+
self.siteIsCIABEligibilityChecker = siteIsCIABEligibilityChecker
150155
self.usageTracksEventEmitter = usageTracksEventEmitter
151156

152157
self.blazeLocalNotificationScheduler = localNotificationScheduler ?? DefaultBlazeLocalNotificationScheduler(siteID: siteID,
@@ -164,6 +169,7 @@ final class DashboardViewModel: ObservableObject {
164169
self?.onInAppFeedbackCardAction()
165170
}
166171

172+
observeStockEligibility()
167173
configureOrdersResultController()
168174
setupDashboardCards()
169175
observeWPCOMSiteSuspendedState()
@@ -187,6 +193,7 @@ final class DashboardViewModel: ObservableObject {
187193
canShowBlaze: blazeCampaignDashboardViewModel.canShowInDashboard,
188194
canShowGoogle: googleAdsDashboardCardViewModel.canShowOnDashboard,
189195
canShowInbox: isEligibleForInbox,
196+
canShowStock: isEligibleForStock,
190197
hasOrders: hasOrders)
191198

192199
await reloadCardsWithBackgroundUpdateSupportIfNeeded()
@@ -487,18 +494,20 @@ private extension DashboardViewModel {
487494
private extension DashboardViewModel {
488495
func observeValuesForDashboardCards() {
489496
storeOnboardingViewModel.$canShowInDashboard
490-
.combineLatest(blazeCampaignDashboardViewModel.$canShowInDashboard)
497+
.combineLatest(blazeCampaignDashboardViewModel.$canShowInDashboard,
498+
$isEligibleForStock)
491499
.combineLatest(googleAdsDashboardCardViewModel.$canShowOnDashboard,
492500
$hasOrders,
493501
$isEligibleForInbox)
494502
.receive(on: DispatchQueue.main)
495503
.sink { [weak self] combinedResult in
496504
guard let self else { return }
497-
let ((canShowOnboarding, canShowBlaze), canShowGoogle, hasOrders, isEligibleForInbox) = combinedResult
505+
let ((canShowOnboarding, canShowBlaze, canShowStock), canShowGoogle, hasOrders, isEligibleForInbox) = combinedResult
498506
updateDashboardCards(canShowOnboarding: canShowOnboarding,
499507
canShowBlaze: canShowBlaze,
500508
canShowGoogle: canShowGoogle,
501509
canShowInbox: isEligibleForInbox,
510+
canShowStock: canShowStock,
502511
hasOrders: hasOrders)
503512
}
504513
.store(in: &subscriptions)
@@ -531,6 +540,26 @@ private extension DashboardViewModel {
531540
isEligibleForInbox = inboxEligibilityChecker.isEligibleForInbox(siteID: siteID)
532541
}
533542

543+
func observeStockEligibility() {
544+
stores.site
545+
.removeDuplicates()
546+
.map { [weak self] in
547+
guard
548+
let self,
549+
let site = $0
550+
else {
551+
return false
552+
}
553+
554+
return siteIsCIABEligibilityChecker
555+
.isFeatureSupported(
556+
.productsStockDashboardCard,
557+
for: site
558+
)
559+
}
560+
.assign(to: &$isEligibleForStock)
561+
}
562+
534563
func configureOrdersResultController() {
535564
func refreshHasOrders() {
536565
/// Upon logging out, `CoreDataManager` clears the storage triggering data change.
@@ -586,6 +615,7 @@ private extension DashboardViewModel {
586615
canShowGoogle: Bool,
587616
canShowAnalytics: Bool,
588617
canShowLastOrders: Bool,
618+
canShowStock: Bool,
589619
canShowInbox: Bool) -> [DashboardCard] {
590620
var cards = [DashboardCard]()
591621

@@ -616,7 +646,14 @@ private extension DashboardViewModel {
616646
enabled: false))
617647
cards.append(DashboardCard(type: .reviews, availability: .show, enabled: false))
618648
cards.append(DashboardCard(type: .coupons, availability: .show, enabled: false))
619-
cards.append(DashboardCard(type: .stock, availability: .show, enabled: false))
649+
650+
cards.append(
651+
DashboardCard(
652+
type: .stock,
653+
availability: canShowStock ? .show : .hide,
654+
enabled: false
655+
)
656+
)
620657

621658
// When not available, Last orders cards need to be hidden from Dashboard, but appear on Customize as "Unavailable"
622659
cards.append(DashboardCard(type: .lastOrders,
@@ -634,6 +671,7 @@ private extension DashboardViewModel {
634671
canShowBlaze: Bool,
635672
canShowGoogle: Bool,
636673
canShowInbox: Bool,
674+
canShowStock: Bool,
637675
hasOrders: Bool) {
638676

639677
let canShowAnalytics = hasOrders
@@ -645,6 +683,7 @@ private extension DashboardViewModel {
645683
canShowGoogle: canShowGoogle,
646684
canShowAnalytics: canShowAnalytics,
647685
canShowLastOrders: canShowLastOrders,
686+
canShowStock: canShowStock,
648687
canShowInbox: canShowInbox)
649688

650689
// Next, get saved cards and preserve existing enabled state for all available cards.

WooCommerce/WooCommerceTests/ViewRelated/Dashboard/Blaze/BlazeCampaignDashboardViewModelTests.swift

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,93 @@ final class BlazeCampaignDashboardViewModelTests: XCTestCase {
105105
XCTAssertFalse(sut.canShowInDashboard)
106106
}
107107

108+
@MainActor
109+
func test_canShowInDashboard_returns_false_if_store_is_ciab_and_other_requirements_met() async {
110+
// Given
111+
112+
let site = Site.fake().copy(
113+
siteID: sampleSiteID,
114+
isJetpackThePluginInstalled: true,
115+
isJetpackConnected: true,
116+
canBlaze: true,
117+
isAdmin: true,
118+
)
119+
120+
stores.updateDefaultStore(site)
121+
122+
let siteCIABChecker = MockCIABEligibilityChecker(
123+
mockedIsCurrentSiteCIAB: true,
124+
mockedCIABSites: [stores.sessionManager.defaultSite ?? .fake()]
125+
)
126+
let blazeEligibilityChecker = BlazeEligibilityChecker(
127+
stores: stores,
128+
siteCIABEligibilityChecker: siteCIABChecker
129+
)
130+
let sut = BlazeCampaignDashboardViewModel(
131+
siteID: sampleSiteID,
132+
stores: stores,
133+
storageManager: storageManager,
134+
blazeEligibilityChecker: blazeEligibilityChecker
135+
)
136+
137+
mockSynchronizeProducts(
138+
insertProductToStorage: .fake().copy(
139+
siteID: sampleSiteID,
140+
statusKey: (ProductStatus.published.rawValue)
141+
)
142+
)
143+
144+
mockSynchronizeCampaignsList()
145+
146+
// When
147+
await sut.checkAvailability()
148+
149+
// Then
150+
XCTAssertFalse(sut.canShowInDashboard)
151+
}
152+
153+
@MainActor
154+
func test_canShowInDashboard_returns_true_if_store_is_non_ciab_and_other_requirements_met() async {
155+
// Given
156+
let site = Site.fake().copy(
157+
siteID: sampleSiteID,
158+
isJetpackThePluginInstalled: true,
159+
isJetpackConnected: true,
160+
canBlaze: true,
161+
isAdmin: true,
162+
)
163+
164+
stores.updateDefaultStore(site)
165+
166+
let siteCIABChecker = MockCIABEligibilityChecker(mockedIsCurrentSiteCIAB: false)
167+
let blazeEligibilityChecker = BlazeEligibilityChecker(
168+
stores: stores,
169+
siteCIABEligibilityChecker: siteCIABChecker
170+
)
171+
172+
let sut = BlazeCampaignDashboardViewModel(
173+
siteID: sampleSiteID,
174+
stores: stores,
175+
storageManager: storageManager,
176+
blazeEligibilityChecker: blazeEligibilityChecker
177+
)
178+
179+
mockSynchronizeProducts(
180+
insertProductToStorage: .fake().copy(
181+
siteID: sampleSiteID,
182+
statusKey: (ProductStatus.published.rawValue)
183+
)
184+
)
185+
186+
mockSynchronizeCampaignsList()
187+
188+
// When
189+
await sut.checkAvailability()
190+
191+
// Then
192+
XCTAssertTrue(sut.canShowInDashboard)
193+
}
194+
108195
// MARK: Published product
109196

110197
@MainActor

0 commit comments

Comments
 (0)