@@ -31,8 +31,10 @@ final class TopPerformersDashboardViewModel: ObservableObject {
3131
3232 private var resultsController : ResultsController < StorageTopEarnerStats > ?
3333
34- // periphery:ignore - keep a strong reference to listen to changes of a product
35- private var entityListener : EntityListener < Product > ?
34+ // Map of product IDs and their listeners
35+ private var entityListeners : [ Int64 : EntityListener < Product > ] = [ : ]
36+
37+ private var hasProductChanges = PassthroughSubject < Void , Never > ( )
3638
3739 private var currentDate : Date {
3840 Date ( )
@@ -86,7 +88,6 @@ final class TopPerformersDashboardViewModel: ObservableObject {
8688 self . usageTracksEventEmitter = usageTracksEventEmitter
8789
8890 observeSyncingCompletion ( )
89- observeSelectedItem ( )
9091
9192 Task { @MainActor in
9293 self . timeRange = await loadLastTimeRange ( ) ?? . today
@@ -219,40 +220,40 @@ private extension TopPerformersDashboardViewModel {
219220 . store ( in: & subscriptions)
220221 }
221222
222- func observeSelectedItem( ) {
223- $selectedItem
224- . scan ( ( nil , nil ) ) { ( previous: ( current: TopEarnerStatsItem ? ,
225- previous: TopEarnerStatsItem ? ) ,
226- newValue: TopEarnerStatsItem ? ) in
227- return ( current: newValue, previous: previous. current)
223+ func observeProducts( ids: [ Int64 ] ) {
224+ entityListeners = [ : ]
225+ for id in ids {
226+ if let listener = createProductEntityListener ( id: id) {
227+ entityListeners [ id] = listener
228228 }
229- . sink { [ weak self] ( newItem, oldItem) in
230- guard let newItem, oldItem == nil else {
231- self ? . entityListener = nil
232- return
229+ }
230+
231+ hasProductChanges
232+ . debounce ( for: . milliseconds( 300 ) , scheduler: DispatchQueue . main)
233+ . sink { [ weak self] in
234+ Task {
235+ await self ? . reloadDataIfNeeded ( forceRefresh: true )
233236 }
234- self ? . observeOpenedProductIfPossible ( id: newItem. productID)
235237 }
236238 . store ( in: & subscriptions)
237239 }
238240
239- func observeOpenedProductIfPossible ( id: Int64 ) {
241+ func createProductEntityListener ( id: Int64 ) -> EntityListener < Product > ? {
240242 guard var product = storageManager. viewStorage. loadProduct ( siteID: siteID, productID: id) ? . toReadOnly ( ) else {
241- return
243+ return nil
242244 }
243245 let entityListener = EntityListener ( storageManager: ServiceLocator . storageManager, readOnlyEntity: product)
244246 entityListener. onUpsert = { [ weak self] updatedProduct in
245247 // reload stats if there are changes to product
246- guard updatedProduct. name != product. name ||
247- updatedProduct. imageURL != product. imageURL else {
248+ guard let self,
249+ updatedProduct. name != product. name ||
250+ updatedProduct. imageURL != product. imageURL else {
248251 return
249252 }
250253 product = updatedProduct
251- Task {
252- await self ? . reloadDataIfNeeded ( forceRefresh: true )
253- }
254+ hasProductChanges. send ( ( ) )
254255 }
255- self . entityListener = entityListener
256+ return entityListener
256257 }
257258
258259 @MainActor
@@ -305,6 +306,7 @@ private extension TopPerformersDashboardViewModel {
305306 return periodViewModel. update ( state: . loaded( rows: [ ] ) )
306307 }
307308 periodViewModel. update ( state: . loaded( rows: items) )
309+ observeProducts ( ids: items. map { $0. productID } )
308310 }
309311
310312 func createResultsController( timeRange: StatsTimeRangeV4 ) -> ResultsController < StorageTopEarnerStats > {
0 commit comments