Skip to content

Commit 395b87d

Browse files
committed
Split AnalyticsProductCardViewModel into 2 viewmodels
1 parent 1678775 commit 395b87d

File tree

5 files changed

+97
-55
lines changed

5 files changed

+97
-55
lines changed

WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/AnalyticsHubView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ struct AnalyticsHubView: View {
9393
VStack(spacing: Layout.dividerSpacing) {
9494
Divider()
9595

96-
AnalyticsProductCard(viewModel: viewModel.productCard)
96+
AnalyticsProductCard(statsViewModel: viewModel.productsStatsCard, itemsViewModel: viewModel.itemsSoldCard)
9797
.padding(.horizontal, insets: safeAreaInsets)
9898
.background(Color(uiColor: .listForeground))
9999

WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/AnalyticsHubViewModel.swift

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,13 @@ final class AnalyticsHubViewModel: ObservableObject {
3535
///
3636
@Published var ordersCard = AnalyticsHubViewModel.ordersCard(currentPeriodStats: nil, previousPeriodStats: nil)
3737

38-
/// Products Card ViewModel
38+
/// Products Stats Card ViewModel
3939
///
40-
@Published var productCard = AnalyticsHubViewModel.productCard(currentPeriodStats: nil, previousPeriodStats: nil, itemsSoldStats: nil)
40+
@Published var productsStatsCard = AnalyticsHubViewModel.productsStatsCard(currentPeriodStats: nil, previousPeriodStats: nil)
41+
42+
/// Items Sold Card ViewModel
43+
///
44+
@Published var itemsSoldCard = AnalyticsHubViewModel.productsItemsSoldCard(itemsSoldStats: nil)
4145

4246
/// Time Range Selection Type
4347
///
@@ -178,7 +182,8 @@ private extension AnalyticsHubViewModel {
178182
func switchToLoadingState() {
179183
self.revenueCard = revenueCard.redacted
180184
self.ordersCard = ordersCard.redacted
181-
self.productCard = productCard.redacted
185+
self.productsStatsCard = productsStatsCard.redacted
186+
self.itemsSoldCard = itemsSoldCard.redacted
182187
}
183188

184189
@MainActor
@@ -189,16 +194,21 @@ private extension AnalyticsHubViewModel {
189194
}
190195

191196
func bindViewModelsWithData() {
192-
Publishers.CombineLatest3($currentOrderStats, $previousOrderStats, $itemsSoldStats)
193-
.sink { [weak self] currentOrderStats, previousOrderStats, itemsSoldStats in
197+
Publishers.CombineLatest($currentOrderStats, $previousOrderStats)
198+
.sink { [weak self] currentOrderStats, previousOrderStats in
194199
guard let self else { return }
195200

196201
self.revenueCard = AnalyticsHubViewModel.revenueCard(currentPeriodStats: currentOrderStats, previousPeriodStats: previousOrderStats)
197202
self.ordersCard = AnalyticsHubViewModel.ordersCard(currentPeriodStats: currentOrderStats, previousPeriodStats: previousOrderStats)
198-
self.productCard = AnalyticsHubViewModel.productCard(currentPeriodStats: currentOrderStats,
199-
previousPeriodStats: previousOrderStats,
200-
itemsSoldStats: itemsSoldStats)
203+
self.productsStatsCard = AnalyticsHubViewModel.productsStatsCard(currentPeriodStats: currentOrderStats, previousPeriodStats: previousOrderStats)
204+
205+
}.store(in: &subscriptions)
206+
207+
$itemsSoldStats
208+
.sink { [weak self] itemsSoldStats in
209+
guard let self else { return }
201210

211+
self.itemsSoldCard = AnalyticsHubViewModel.productsItemsSoldCard(itemsSoldStats: itemsSoldStats)
202212
}.store(in: &subscriptions)
203213

204214
$timeRangeSelectionType
@@ -253,22 +263,26 @@ private extension AnalyticsHubViewModel {
253263
syncErrorMessage: Localization.OrderCard.noOrders)
254264
}
255265

256-
/// Helper function to create a `AnalyticsProductCardViewModel` from the fetched stats.
266+
/// Helper function to create a `AnalyticsProductsStatsCardViewModel` from the fetched stats.
257267
///
258-
static func productCard(currentPeriodStats: OrderStatsV4?,
259-
previousPeriodStats: OrderStatsV4?,
260-
itemsSoldStats: TopEarnerStats?) -> AnalyticsProductCardViewModel {
268+
static func productsStatsCard(currentPeriodStats: OrderStatsV4?,
269+
previousPeriodStats: OrderStatsV4?) -> AnalyticsProductsStatsCardViewModel {
261270
let showStatsError = currentPeriodStats == nil || previousPeriodStats == nil
262-
let showItemsSoldError = itemsSoldStats == nil
263271
let itemsSold = StatsDataTextFormatter.createItemsSoldText(orderStats: currentPeriodStats)
264272
let itemsSoldDelta = StatsDataTextFormatter.createOrderItemsSoldDelta(from: previousPeriodStats, to: currentPeriodStats)
265273

266-
return AnalyticsProductCardViewModel(itemsSold: itemsSold,
267-
delta: itemsSoldDelta,
268-
itemsSoldData: itemSoldRows(from: itemsSoldStats),
269-
isRedacted: false,
270-
showStatsError: showStatsError,
271-
showItemsSoldError: showItemsSoldError)
274+
return AnalyticsProductsStatsCardViewModel(itemsSold: itemsSold,
275+
delta: itemsSoldDelta,
276+
isRedacted: false,
277+
showStatsError: showStatsError)
278+
}
279+
280+
/// Helper function to create a `AnalyticsItemsSoldViewModel` from the fetched stats.
281+
///
282+
static func productsItemsSoldCard(itemsSoldStats: TopEarnerStats?) -> AnalyticsItemsSoldViewModel {
283+
let showItemsSoldError = itemsSoldStats == nil
284+
285+
return AnalyticsItemsSoldViewModel(itemsSoldData: itemSoldRows(from: itemsSoldStats), isRedacted: false, showItemsSoldError: showItemsSoldError)
272286
}
273287

274288
/// Helper functions to create `TopPerformersRow.Data` items rom the provided `TopEarnerStats`.

WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/AnalyticsProductCard.swift

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,22 @@ struct AnalyticsProductCard: View {
1717
/// Delta Tag text color.
1818
let deltaTextColor: UIColor
1919

20-
/// Items Solds data to render.
21-
///
22-
let itemsSoldData: [TopPerformersRow.Data]
23-
2420
/// Indicates if the values should be hidden (for loading state)
2521
///
26-
let isRedacted: Bool
22+
let isStatsRedacted: Bool
2723

2824
/// Indicates if there was an error loading stats part of the card.
2925
///
3026
let showStatsError: Bool
3127

28+
/// Items Solds data to render.
29+
///
30+
let itemsSoldData: [TopPerformersRow.Data]
31+
32+
/// Indicates if the values should be hidden (for loading state)
33+
///
34+
let isItemsSoldRedacted: Bool
35+
3236
/// Indicates if there was an error loading items sold part of the card.
3337
///
3438
let showItemsSoldError: Bool
@@ -49,12 +53,12 @@ struct AnalyticsProductCard: View {
4953
Text(itemsSold)
5054
.titleStyle()
5155
.frame(maxWidth: .infinity, alignment: .leading)
52-
.redacted(reason: isRedacted ? .placeholder : [])
53-
.shimmering(active: isRedacted)
56+
.redacted(reason: isStatsRedacted ? .placeholder : [])
57+
.shimmering(active: isStatsRedacted)
5458

5559
DeltaTag(value: delta, backgroundColor: deltaBackgroundColor, textColor: deltaTextColor)
56-
.redacted(reason: isRedacted ? .placeholder : [])
57-
.shimmering(active: isRedacted)
60+
.redacted(reason: isStatsRedacted ? .placeholder : [])
61+
.shimmering(active: isStatsRedacted)
5862
}
5963

6064
if showStatsError {
@@ -68,7 +72,7 @@ struct AnalyticsProductCard: View {
6872
TopPerformersView(itemTitle: Localization.title.localizedCapitalized,
6973
valueTitle: Localization.itemsSold,
7074
rows: itemsSoldData,
71-
isRedacted: isRedacted)
75+
isRedacted: isItemsSoldRedacted)
7276
.padding(.top, Layout.columnSpacing)
7377

7478
if showItemsSoldError {
@@ -110,24 +114,26 @@ struct AnalyticsProductCardPreviews: PreviewProvider {
110114
delta: "+23%",
111115
deltaBackgroundColor: .withColorStudio(.green, shade: .shade50),
112116
deltaTextColor: .textInverted,
117+
isStatsRedacted: false,
118+
showStatsError: false,
113119
itemsSoldData: [
114120
.init(imageURL: imageURL, name: "Tabletop Photos", details: "Net Sales: $1,232", value: "32"),
115121
.init(imageURL: imageURL, name: "Kentya Palm", details: "Net Sales: $800", value: "10"),
116122
.init(imageURL: imageURL, name: "Love Ficus", details: "Net Sales: $599", value: "5"),
117123
.init(imageURL: imageURL, name: "Bird Of Paradise", details: "Net Sales: $23.50", value: "2"),
118124
],
119-
isRedacted: false,
120-
showStatsError: false,
125+
isItemsSoldRedacted: false,
121126
showItemsSoldError: false)
122127
.previewLayout(.sizeThatFits)
123128

124129
AnalyticsProductCard(itemsSold: "-",
125130
delta: "0%",
126131
deltaBackgroundColor: .withColorStudio(.gray, shade: .shade0),
127132
deltaTextColor: .text,
128-
itemsSoldData: [],
129-
isRedacted: false,
133+
isStatsRedacted: false,
130134
showStatsError: true,
135+
itemsSoldData: [],
136+
isItemsSoldRedacted: false,
131137
showItemsSoldError: true)
132138
.previewLayout(.sizeThatFits)
133139
.previewDisplayName("No data")
Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import Foundation
22
import class UIKit.UIColor
33

4-
/// Analytics Hub Product Card ViewModel.
4+
/// Analytics Hub Products Stats Card ViewModel.
55
/// Used to transmit analytics products data.
66
///
7-
struct AnalyticsProductCardViewModel {
7+
struct AnalyticsProductsStatsCardViewModel {
88
/// Items Sold Value
99
///
1010
let itemsSold: String
@@ -13,51 +13,73 @@ struct AnalyticsProductCardViewModel {
1313
///
1414
let delta: DeltaPercentage
1515

16-
/// Items Solds data to render.
17-
///
18-
let itemsSoldData: [TopPerformersRow.Data]
19-
2016
/// Indicates if the values should be hidden (for loading state)
2117
///
2218
let isRedacted: Bool
2319

2420
/// Indicates if there was an error loading stats part of the card.
2521
///
2622
let showStatsError: Bool
23+
}
24+
25+
/// Analytics Hub Items Sold ViewModel.
26+
/// Used to store top performing products data.
27+
///
28+
struct AnalyticsItemsSoldViewModel {
29+
30+
/// Items Solds data to render.
31+
///
32+
let itemsSoldData: [TopPerformersRow.Data]
33+
34+
/// Indicates if the values should be hidden (for loading state)
35+
///
36+
let isRedacted: Bool
2737

2838
/// Indicates if there was an error loading items sold part of the card.
2939
///
3040
let showItemsSoldError: Bool
3141
}
3242

33-
extension AnalyticsProductCardViewModel {
43+
extension AnalyticsProductsStatsCardViewModel {
3444

3545
/// Make redacted state of the card, replacing values with hardcoded placeholders
3646
///
3747
var redacted: Self {
3848
// Values here are placeholders and will be redacted in the UI
3949
.init(itemsSold: "1000",
4050
delta: DeltaPercentage(string: "0%", direction: .zero),
41-
itemsSoldData: [.init(imageURL: nil, name: "Product Name", details: "Net Sales", value: "$5678")],
4251
isRedacted: true,
43-
showStatsError: false,
44-
showItemsSoldError: false)
52+
showStatsError: false)
4553
}
54+
}
55+
56+
extension AnalyticsItemsSoldViewModel {
4657

58+
/// Make redacted state of the card, replacing values with hardcoded placeholders
59+
///
60+
var redacted: Self {
61+
// Values here are placeholders and will be redacted in the UI
62+
.init(itemsSoldData: [.init(imageURL: nil, name: "Product Name", details: "Net Sales", value: "$5678")],
63+
isRedacted: true,
64+
showItemsSoldError: false)
65+
}
4766
}
4867

49-
/// Convenience extension to create an `AnalyticsReportCard` from a view model.
68+
/// Convenience extension to create an `AnalyticsProductCard` from a view model.
5069
///
5170
extension AnalyticsProductCard {
52-
init(viewModel: AnalyticsProductCardViewModel) {
53-
self.itemsSold = viewModel.itemsSold
54-
self.delta = viewModel.delta.string
55-
self.deltaBackgroundColor = viewModel.delta.direction.deltaBackgroundColor
56-
self.deltaTextColor = viewModel.delta.direction.deltaTextColor
57-
self.itemsSoldData = viewModel.itemsSoldData
58-
self.isRedacted = viewModel.isRedacted
59-
self.showStatsError = viewModel.showStatsError
60-
self.showItemsSoldError = viewModel.showItemsSoldError
71+
init(statsViewModel: AnalyticsProductsStatsCardViewModel, itemsViewModel: AnalyticsItemsSoldViewModel) {
72+
// Header with stats
73+
self.itemsSold = statsViewModel.itemsSold
74+
self.delta = statsViewModel.delta.string
75+
self.deltaBackgroundColor = statsViewModel.delta.direction.deltaBackgroundColor
76+
self.deltaTextColor = statsViewModel.delta.direction.deltaTextColor
77+
self.isStatsRedacted = statsViewModel.isRedacted
78+
self.showStatsError = statsViewModel.showStatsError
6179

80+
// Top performers list
81+
self.itemsSoldData = itemsViewModel.itemsSoldData
82+
self.isItemsSoldRedacted = itemsViewModel.isRedacted
83+
self.showItemsSoldError = itemsViewModel.showItemsSoldError
6284
}
6385
}

WooCommerce/WooCommerceTests/ViewRelated/Dashboard/Analytics Hub/AnalyticsHubViewModelTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ final class AnalyticsHubViewModelTests: XCTestCase {
7070
let vm = AnalyticsHubViewModel(siteID: 123, statsTimeRange: .thisMonth, stores: stores)
7171
var loadingRevenueCard: AnalyticsReportCardViewModel?
7272
var loadingOrdersCard: AnalyticsReportCardViewModel?
73-
var loadingProductsCard: AnalyticsProductCardViewModel?
73+
var loadingProductsCard: AnalyticsProductsStatsCardViewModel?
7474
stores.whenReceivingAction(ofType: StatsActionV4.self) { action in
7575
switch action {
7676
case let .retrieveCustomStats(_, _, _, _, _, _, completion):

0 commit comments

Comments
 (0)