@@ -41,9 +41,13 @@ final class AnalyticsHubViewModel: ObservableObject {
4141 ///
4242 @Published var ordersCard = AnalyticsHubViewModel . ordersCard ( currentPeriodStats: nil , previousPeriodStats: nil )
4343
44- /// Products Card ViewModel
44+ /// Products Stats Card ViewModel
4545 ///
46- @Published var productCard = AnalyticsHubViewModel . productCard ( currentPeriodStats: nil , previousPeriodStats: nil , itemsSoldStats: nil )
46+ @Published var productsStatsCard = AnalyticsHubViewModel . productsStatsCard ( currentPeriodStats: nil , previousPeriodStats: nil )
47+
48+ /// Items Sold Card ViewModel
49+ ///
50+ @Published var itemsSoldCard = AnalyticsHubViewModel . productsItemsSoldCard ( itemsSoldStats: nil )
4751
4852 /// Sessions Card ViewModel
4953 ///
@@ -92,7 +96,7 @@ final class AnalyticsHubViewModel: ObservableObject {
9296 @MainActor
9397 func updateData( ) async {
9498 do {
95- try await retrieveOrderStats ( )
99+ try await retrieveData ( )
96100 } catch is AnalyticsHubTimeRangeSelection . TimeRangeGeneratorError {
97101 dismissNotice = Notice ( title: Localization . timeRangeGeneratorError, feedbackType: . error)
98102 ServiceLocator . analytics. track ( event: . AnalyticsHub. dateRangeSelectionFailed ( for: timeRangeSelectionType) )
@@ -114,26 +118,44 @@ final class AnalyticsHubViewModel: ObservableObject {
114118private extension AnalyticsHubViewModel {
115119
116120 @MainActor
117- func retrieveOrderStats ( ) async throws {
121+ func retrieveData ( ) async throws {
118122 switchToLoadingState ( )
119123
120124 let currentTimeRange = try timeRangeSelection. unwrapCurrentTimeRange ( )
121125 let previousTimeRange = try timeRangeSelection. unwrapPreviousTimeRange ( )
122126
127+ try await withThrowingTaskGroup ( of: Void . self) { group in
128+ group. addTask {
129+ try await self . retrieveOrderStats ( currentTimeRange: currentTimeRange, previousTimeRange: previousTimeRange)
130+ }
131+ group. addTask {
132+ try await self . retrieveVisitorStats ( currentTimeRange: currentTimeRange, previousTimeRange: previousTimeRange)
133+ }
134+ try await group. waitForAll ( )
135+ }
136+ }
137+
138+ @MainActor
139+ func retrieveOrderStats( currentTimeRange: AnalyticsHubTimeRange , previousTimeRange: AnalyticsHubTimeRange ) async throws {
123140 async let currentPeriodRequest = retrieveStats ( earliestDateToInclude: currentTimeRange. start,
124141 latestDateToInclude: currentTimeRange. end,
125142 forceRefresh: true )
126143 async let previousPeriodRequest = retrieveStats ( earliestDateToInclude: previousTimeRange. start,
127144 latestDateToInclude: previousTimeRange. end,
128145 forceRefresh: true )
129146
147+ let ( currentPeriodStats, previousPeriodStats) = try await ( currentPeriodRequest, previousPeriodRequest)
148+ self . currentOrderStats = currentPeriodStats
149+ self . previousOrderStats = previousPeriodStats
150+ }
151+
152+ @MainActor
153+ func retrieveVisitorStats( currentTimeRange: AnalyticsHubTimeRange , previousTimeRange: AnalyticsHubTimeRange ) async throws {
130154 async let itemsSoldRequest = retrieveTopItemsSoldStats ( earliestDateToInclude: currentTimeRange. start,
131155 latestDateToInclude: currentTimeRange. end,
132156 forceRefresh: true )
133157
134- let ( currentPeriodStats, previousPeriodStats, itemsSoldStats) = try await ( currentPeriodRequest, previousPeriodRequest, itemsSoldRequest)
135- self . currentOrderStats = currentPeriodStats
136- self . previousOrderStats = previousPeriodStats
158+ let itemsSoldStats = try await itemsSoldRequest
137159 self . itemsSoldStats = itemsSoldStats
138160 }
139161
@@ -181,7 +203,8 @@ private extension AnalyticsHubViewModel {
181203 func switchToLoadingState( ) {
182204 self . revenueCard = revenueCard. redacted
183205 self . ordersCard = ordersCard. redacted
184- self . productCard = productCard. redacted
206+ self . productsStatsCard = productsStatsCard. redacted
207+ self . itemsSoldCard = itemsSoldCard. redacted
185208 }
186209
187210 @MainActor
@@ -192,25 +215,33 @@ private extension AnalyticsHubViewModel {
192215 }
193216
194217 func bindViewModelsWithData( ) {
195- Publishers . CombineLatest3 ( $currentOrderStats, $previousOrderStats, $itemsSoldStats )
196- . sink { [ weak self] currentOrderStats, previousOrderStats, itemsSoldStats in
218+ Publishers . CombineLatest ( $currentOrderStats, $previousOrderStats)
219+ . sink { [ weak self] currentOrderStats, previousOrderStats in
197220 guard let self else { return }
198221
199222 self . revenueCard = AnalyticsHubViewModel . revenueCard ( currentPeriodStats: currentOrderStats, previousPeriodStats: previousOrderStats)
200223 self . ordersCard = AnalyticsHubViewModel . ordersCard ( currentPeriodStats: currentOrderStats, previousPeriodStats: previousOrderStats)
201- self . productCard = AnalyticsHubViewModel . productCard ( currentPeriodStats: currentOrderStats,
202- previousPeriodStats: previousOrderStats,
203- itemsSoldStats: itemsSoldStats)
224+ self . productsStatsCard = AnalyticsHubViewModel . productsStatsCard ( currentPeriodStats: currentOrderStats, previousPeriodStats: previousOrderStats)
204225
205226 } . store ( in: & subscriptions)
206227
228+ $itemsSoldStats
229+ . sink { [ weak self] itemsSoldStats in
230+ guard let self else { return }
231+
232+ self . itemsSoldCard = AnalyticsHubViewModel . productsItemsSoldCard ( itemsSoldStats: itemsSoldStats)
233+ } . store ( in: & subscriptions)
234+
207235 $timeRangeSelectionType
236+ . dropFirst ( ) // do not trigger refresh action on initial value
208237 . removeDuplicates ( )
209238 . sink { [ weak self] newSelectionType in
210239 guard let self else { return }
211240 self . timeRangeSelection = AnalyticsHubTimeRangeSelection ( selectionType: newSelectionType)
212241 self . timeRangeCard = AnalyticsHubViewModel . timeRangeCard ( timeRangeSelection: self . timeRangeSelection,
213242 usageTracksEventEmitter: self . usageTracksEventEmitter)
243+
244+ // Update data on range selection change
214245 Task . init {
215246 await self . updateData ( )
216247 }
@@ -254,22 +285,26 @@ private extension AnalyticsHubViewModel {
254285 syncErrorMessage: Localization . OrderCard. noOrders)
255286 }
256287
257- /// Helper function to create a `AnalyticsProductCardViewModel ` from the fetched stats.
288+ /// Helper function to create a `AnalyticsProductsStatsCardViewModel ` from the fetched stats.
258289 ///
259- static func productCard( currentPeriodStats: OrderStatsV4 ? ,
260- previousPeriodStats: OrderStatsV4 ? ,
261- itemsSoldStats: TopEarnerStats ? ) -> AnalyticsProductCardViewModel {
290+ static func productsStatsCard( currentPeriodStats: OrderStatsV4 ? ,
291+ previousPeriodStats: OrderStatsV4 ? ) -> AnalyticsProductsStatsCardViewModel {
262292 let showStatsError = currentPeriodStats == nil || previousPeriodStats == nil
263- let showItemsSoldError = itemsSoldStats == nil
264293 let itemsSold = StatsDataTextFormatter . createItemsSoldText ( orderStats: currentPeriodStats)
265294 let itemsSoldDelta = StatsDataTextFormatter . createOrderItemsSoldDelta ( from: previousPeriodStats, to: currentPeriodStats)
266295
267- return AnalyticsProductCardViewModel ( itemsSold: itemsSold,
268- delta: itemsSoldDelta,
269- itemsSoldData: itemSoldRows ( from: itemsSoldStats) ,
270- isRedacted: false ,
271- showStatsError: showStatsError,
272- showItemsSoldError: showItemsSoldError)
296+ return AnalyticsProductsStatsCardViewModel ( itemsSold: itemsSold,
297+ delta: itemsSoldDelta,
298+ isRedacted: false ,
299+ showStatsError: showStatsError)
300+ }
301+
302+ /// Helper function to create a `AnalyticsItemsSoldViewModel` from the fetched stats.
303+ ///
304+ static func productsItemsSoldCard( itemsSoldStats: TopEarnerStats ? ) -> AnalyticsItemsSoldViewModel {
305+ let showItemsSoldError = itemsSoldStats == nil
306+
307+ return AnalyticsItemsSoldViewModel ( itemsSoldData: itemSoldRows ( from: itemsSoldStats) , isRedacted: false , showItemsSoldError: showItemsSoldError)
273308 }
274309
275310 /// Helper functions to create `TopPerformersRow.Data` items rom the provided `TopEarnerStats`.
0 commit comments